Skip to content

Commit ebadc7f

Browse files
committed
Merge pull request #412 from AlexeyProkhin/cpp
extern(C++) interfaces
2 parents 67a9391 + 90bb32d commit ebadc7f

File tree

4 files changed

+72
-15
lines changed

4 files changed

+72
-15
lines changed

gen/classes.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -513,8 +513,9 @@ LLValue* DtoVirtualFunctionPointer(DValue* inst, FuncDeclaration* fdecl, char* n
513513
// sanity checks
514514
assert(fdecl->isVirtual());
515515
assert(!fdecl->isFinal());
516-
assert(fdecl->vtblIndex > 0); // 0 is always ClassInfo/Interface*
517516
assert(inst->getType()->toBasetype()->ty == Tclass);
517+
// 0 is always ClassInfo/Interface* unless it is a CPP interface
518+
assert(fdecl->vtblIndex > 0 || (fdecl->vtblIndex == 0 && fdecl->linkage == LINKcpp));
518519

519520
// get instance
520521
LLValue* vthis = inst->getRVal();

gen/toir.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -1496,11 +1496,13 @@ DValue* DotVarExp::toElem(IRState* p)
14961496
// If we are calling a non-final interface function, we need to get
14971497
// the pointer to the underlying object instead of passing the
14981498
// interface pointer directly.
1499+
// Unless it is a cpp interface, in that case, we have to match
1500+
// C++ behavior and pass the interface pointer.
14991501
LLValue* passedThis = 0;
15001502
if (e1type->ty == Tclass)
15011503
{
15021504
TypeClass* tc = static_cast<TypeClass*>(e1type);
1503-
if (tc->sym->isInterfaceDeclaration() && nonFinal)
1505+
if (tc->sym->isInterfaceDeclaration() && nonFinal && !tc->sym->isCPPinterface())
15041506
passedThis = DtoCastInterfaceToObject(l, NULL)->getRVal();
15051507
}
15061508
LLValue* vthis = l->getRVal();

ir/irclass.cpp

+66-12
Original file line numberDiff line numberDiff line change
@@ -283,23 +283,25 @@ llvm::GlobalVariable * IrAggr::getInterfaceVtbl(BaseClass * b, bool new_instance
283283
std::vector<llvm::Constant*> constants;
284284
constants.reserve(vtbl_array.dim);
285285

286-
// start with the interface info
287-
VarDeclarationIter interfaces_idx(ClassDeclaration::classinfo->fields, 3);
286+
if (!b->base->isCPPinterface()) { // skip interface info for CPP interfaces
287+
// start with the interface info
288+
VarDeclarationIter interfaces_idx(ClassDeclaration::classinfo->fields, 3);
288289

289-
// index into the interfaces array
290-
llvm::Constant* idxs[2] = {
291-
DtoConstSize_t(0),
292-
DtoConstSize_t(interfaces_index)
293-
};
290+
// index into the interfaces array
291+
llvm::Constant* idxs[2] = {
292+
DtoConstSize_t(0),
293+
DtoConstSize_t(interfaces_index)
294+
};
294295

295-
llvm::Constant* c = llvm::ConstantExpr::getGetElementPtr(
296-
getInterfaceArraySymbol(), idxs, true);
296+
llvm::Constant* c = llvm::ConstantExpr::getGetElementPtr(
297+
getInterfaceArraySymbol(), idxs, true);
297298

298-
constants.push_back(c);
299+
constants.push_back(c);
300+
}
299301

300302
// add virtual function pointers
301303
size_t n = vtbl_array.dim;
302-
for (size_t i = 1; i < n; i++)
304+
for (size_t i = b->base->vtblOffset(); i < n; i++)
303305
{
304306
Dsymbol* dsym = static_cast<Dsymbol*>(vtbl_array.data[i]);
305307
if (dsym == NULL)
@@ -320,7 +322,59 @@ llvm::GlobalVariable * IrAggr::getInterfaceVtbl(BaseClass * b, bool new_instance
320322
fd->codegen(Type::sir);
321323
assert(fd->ir.irFunc && "invalid vtbl function");
322324

323-
constants.push_back(fd->ir.irFunc->func);
325+
LLFunction *fn = fd->ir.irFunc->func;
326+
327+
// If the base is a cpp interface, 'this' parameter is a pointer to
328+
// the interface not the underlying object as expected. Instead of
329+
// the function, we place into the vtable a small wrapper, called thunk,
330+
// that casts 'this' to the object and then pass it to the real function.
331+
if (b->base->isCPPinterface()) {
332+
TypeFunction *f = (TypeFunction*)fd->type->toBasetype();
333+
assert(f->fty.arg_this);
334+
335+
// create the thunk function
336+
OutBuffer name;
337+
name.writestring("Th");
338+
name.printf("%i", b->offset);
339+
name.writestring(fd->mangle());
340+
LLFunction *thunk = LLFunction::Create(isaFunction(fn->getType()->getContainedType(0)),
341+
DtoLinkage(fd), name.toChars(), gIR->module);
342+
343+
// create entry and end blocks
344+
llvm::BasicBlock* beginbb = llvm::BasicBlock::Create(gIR->context(), "entry", thunk);
345+
llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "endentry", thunk);
346+
gIR->scopes.push_back(IRScope(beginbb, endbb));
347+
348+
// copy the function parameters, so later we can pass them to the real function
349+
std::vector<LLValue*> args;
350+
llvm::Function::arg_iterator iarg = thunk->arg_begin();
351+
for (; iarg != thunk->arg_end(); ++iarg)
352+
args.push_back(iarg);
353+
354+
// cast 'this' to Object
355+
LLValue* &thisArg = args[(f->fty.arg_sret == 0) ? 0 : 1];
356+
LLType* thisType = thisArg->getType();
357+
thisArg = DtoBitCast(thisArg, getVoidPtrType());
358+
thisArg = DtoGEP1(thisArg, DtoConstInt(-b->offset));
359+
thisArg = DtoBitCast(thisArg, thisType);
360+
361+
// call the real vtbl function.
362+
LLValue *retVal = gIR->ir->CreateCall(fn, args);
363+
364+
// return from the thunk
365+
if (thunk->getReturnType() == LLType::getVoidTy(gIR->context()))
366+
llvm::ReturnInst::Create(gIR->context(), beginbb);
367+
else
368+
llvm::ReturnInst::Create(gIR->context(), retVal, beginbb);
369+
370+
// clean up
371+
gIR->scopes.pop_back();
372+
thunk->getBasicBlockList().pop_back();
373+
374+
fn = thunk;
375+
}
376+
377+
constants.push_back(fn);
324378
}
325379

326380
// build the vtbl constant

ir/irtypeclass.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ void IrTypeClass::addBaseClassData(
188188
ArrayIter<BaseClass> it2(*base->vtblInterfaces);
189189

190190
VarDeclarationIter interfaces_idx(ClassDeclaration::classinfo->fields, 3);
191-
Type* first = interfaces_idx->type->nextOf()->pointerTo();
191+
Type* first = interfaces_idx->type->nextOf()->pointerTo();
192192

193193
// align offset
194194
offset = (offset + Target::ptrsize - 1) & ~(Target::ptrsize - 1);

0 commit comments

Comments
 (0)