//===-- irclass.cpp -------------------------------------------------------===// // // LDC – the LLVM D compiler // // This file is distributed under the BSD-style LDC license. See the LICENSE // file for details. // //===----------------------------------------------------------------------===// #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/ADT/SmallString.h" #ifndef NDEBUG #include "llvm/Support/raw_ostream.h" #endif #include "aggregate.h" #include "declaration.h" #include "hdrgen.h" // for parametersTypeToChars() #include "mtype.h" #include "target.h" #include "gen/funcgenstate.h" #include "gen/irstate.h" #include "gen/logger.h" #include "gen/tollvm.h" #include "gen/llvmhelpers.h" #include "gen/arrays.h" #include "gen/metadata.h" #include "gen/runtime.h" #include "gen/functions.h" #include "gen/abi.h" #include "gen/mangling.h" #include "ir/iraggr.h" #include "ir/irfunction.h" #include "ir/irtypeclass.h" ////////////////////////////////////////////////////////////////////////////// extern LLConstant *DtoDefineClassInfo(ClassDeclaration *cd); ////////////////////////////////////////////////////////////////////////////// LLGlobalVariable *IrAggr::getVtblSymbol() { if (vtbl) { return vtbl; } // create the vtblZ symbol auto initname = getMangledVTableSymbolName(aggrdecl); LLType *vtblTy = stripModifiers(type)->ctype->isClass()->getVtblType(); vtbl = getOrCreateGlobal(aggrdecl->loc, gIR->module, vtblTy, true, llvm::GlobalValue::ExternalLinkage, nullptr, initname); return vtbl; } ////////////////////////////////////////////////////////////////////////////// LLGlobalVariable *IrAggr::getClassInfoSymbol() { if (classInfo) { return classInfo; } // create the ClassZ / InterfaceZ symbol std::string initname = getMangledClassInfoSymbolName(aggrdecl); // The type is also ClassInfo for interfaces – the actual TypeInfo for them // is a TypeInfo_Interface instance that references __ClassZ in its "base" // member. ClassDeclaration *cinfo = Type::typeinfoclass; DtoType(cinfo->type); IrTypeClass *tc = stripModifiers(cinfo->type)->ctype->isClass(); assert(tc && "invalid ClassInfo type"); // classinfos cannot be constants since they're used as locks for synchronized classInfo = getOrCreateGlobal( aggrdecl->loc, gIR->module, tc->getMemoryLLType(), false, llvm::GlobalValue::ExternalLinkage, nullptr, initname); // Generate some metadata on this ClassInfo if it's for a class. ClassDeclaration *classdecl = aggrdecl->isClassDeclaration(); if (classdecl && !aggrdecl->isInterfaceDeclaration()) { // Gather information LLType *type = DtoType(aggrdecl->type); LLType *bodyType = llvm::cast(type)->getElementType(); bool hasDestructor = (classdecl->dtor != nullptr); bool hasCustomDelete = (classdecl->aggDelete != nullptr); // Construct the fields #if LDC_LLVM_VER >= 306 llvm::Metadata *mdVals[CD_NumFields]; mdVals[CD_BodyType] = llvm::ConstantAsMetadata::get(llvm::UndefValue::get(bodyType)); mdVals[CD_Finalize] = llvm::ConstantAsMetadata::get( LLConstantInt::get(LLType::getInt1Ty(gIR->context()), hasDestructor)); mdVals[CD_CustomDelete] = llvm::ConstantAsMetadata::get( LLConstantInt::get(LLType::getInt1Ty(gIR->context()), hasCustomDelete)); #else MDNodeField *mdVals[CD_NumFields]; mdVals[CD_BodyType] = llvm::UndefValue::get(bodyType); mdVals[CD_Finalize] = LLConstantInt::get(LLType::getInt1Ty(gIR->context()), hasDestructor); mdVals[CD_CustomDelete] = LLConstantInt::get(LLType::getInt1Ty(gIR->context()), hasCustomDelete); #endif // Construct the metadata and insert it into the module. llvm::SmallString<64> name; llvm::NamedMDNode *node = gIR->module.getOrInsertNamedMetadata( llvm::Twine(CD_PREFIX, initname).toStringRef(name)); node->addOperand(llvm::MDNode::get( gIR->context(), llvm::makeArrayRef(mdVals, CD_NumFields))); } return classInfo; } ////////////////////////////////////////////////////////////////////////////// LLGlobalVariable *IrAggr::getInterfaceArraySymbol() { if (classInterfacesArray) { return classInterfacesArray; } ClassDeclaration *cd = aggrdecl->isClassDeclaration(); size_t n = stripModifiers(type)->ctype->isClass()->getNumInterfaceVtbls(); assert(n > 0 && "getting ClassInfo.interfaces storage symbol, but we " "don't implement any interfaces"); LLType *InterfaceTy = DtoType(Type::typeinfoclass->fields[3]->type->nextOf()); // create Interface[N] LLArrayType *array_type = llvm::ArrayType::get(InterfaceTy, n); // put it in a global std::string name("_D"); name.append(mangle(cd)); name.append("16__interfaceInfosZ"); // We keep this as external for now and only consider template linkage if // we emit the initializer later. classInterfacesArray = getOrCreateGlobal(cd->loc, gIR->module, array_type, true, llvm::GlobalValue::ExternalLinkage, nullptr, name); return classInterfacesArray; } ////////////////////////////////////////////////////////////////////////////// LLConstant *IrAggr::getVtblInit() { if (constVtbl) { return constVtbl; } IF_LOG Logger::println("Building vtbl initializer"); LOG_SCOPE; ClassDeclaration *cd = aggrdecl->isClassDeclaration(); assert(cd && "not class"); std::vector constants; constants.reserve(cd->vtbl.dim); const auto voidPtrType = getVoidPtrType(); // start with the classinfo llvm::Constant *c; if (!cd->isCPPclass()) { c = getClassInfoSymbol(); c = DtoBitCast(c, voidPtrType); constants.push_back(c); } // add virtual function pointers size_t n = cd->vtbl.dim; for (size_t i = cd->vtblOffset(); i < n; i++) { Dsymbol *dsym = static_cast(cd->vtbl.data[i]); assert(dsym && "null vtbl member"); FuncDeclaration *fd = dsym->isFuncDeclaration(); assert(fd && "vtbl entry not a function"); if (cd->isAbstract() || (fd->isAbstract() && !fd->fbody)) { c = getNullValue(getPtrToType(DtoFunctionType(fd))); } else { DtoResolveFunction(fd); assert(isIrFuncCreated(fd) && "invalid vtbl function"); c = getIrFunc(fd)->func; if (cd->isFuncHidden(fd)) { // fd is hidden from the view of this class. If fd overlaps with any // function in the vtbl[], issue error. for (size_t j = 1; j < n; j++) { if (j == i) { continue; } auto fd2 = static_cast(cd->vtbl.data[j])->isFuncDeclaration(); if (!fd2->ident->equals(fd->ident)) { continue; } if (fd->leastAsSpecialized(fd2) || fd2->leastAsSpecialized(fd)) { TypeFunction *tf = static_cast(fd->type); if (tf->ty == Tfunction) { cd->error("use of %s%s is hidden by %s; use 'alias %s = %s.%s;' " "to introduce base class overload set", fd->toPrettyChars(), parametersTypeToChars(tf->parameters, tf->varargs), cd->toChars(), fd->toChars(), fd->parent->toChars(), fd->toChars()); } else { cd->error("use of %s is hidden by %s", fd->toPrettyChars(), cd->toChars()); } fatal(); break; } } } } constants.push_back(DtoBitCast(c, voidPtrType)); } // build the constant array LLArrayType *vtblTy = LLArrayType::get(voidPtrType, constants.size()); constVtbl = LLConstantArray::get(vtblTy, constants); return constVtbl; } ////////////////////////////////////////////////////////////////////////////// LLConstant *IrAggr::getClassInfoInit() { if (constClassInfo) { return constClassInfo; } constClassInfo = DtoDefineClassInfo(aggrdecl->isClassDeclaration()); return constClassInfo; } ////////////////////////////////////////////////////////////////////////////// llvm::GlobalVariable *IrAggr::getInterfaceVtbl(BaseClass *b, bool new_instance, size_t interfaces_index) { auto it = interfaceVtblMap.find({b->sym, interfaces_index}); if (it != interfaceVtblMap.end()) { return it->second; } IF_LOG Logger::println( "Building vtbl for implementation of interface %s in class %s", b->sym->toPrettyChars(), aggrdecl->toPrettyChars()); LOG_SCOPE; ClassDeclaration *cd = aggrdecl->isClassDeclaration(); assert(cd && "not a class aggregate"); FuncDeclarations vtbl_array; b->fillVtbl(cd, &vtbl_array, new_instance); std::vector constants; constants.reserve(vtbl_array.dim); const auto voidPtrTy = getVoidPtrType(); if (!b->sym->isCPPinterface()) { // skip interface info for CPP interfaces // index into the interfaces array llvm::Constant *idxs[2] = {DtoConstSize_t(0), DtoConstSize_t(interfaces_index)}; llvm::GlobalVariable *interfaceInfosZ = getInterfaceArraySymbol(); llvm::Constant *c = llvm::ConstantExpr::getGetElementPtr( #if LDC_LLVM_VER >= 307 isaPointer(interfaceInfosZ)->getElementType(), #endif interfaceInfosZ, idxs, true); constants.push_back(DtoBitCast(c, voidPtrTy)); } // Thunk prefix char thunkPrefix[16]; int thunkLen = sprintf(thunkPrefix, "Thn%d_", b->offset); char thunkPrefixLen[16]; sprintf(thunkPrefixLen, "%d", thunkLen); // add virtual function pointers size_t n = vtbl_array.dim; for (size_t i = b->sym->vtblOffset(); i < n; i++) { Dsymbol *dsym = static_cast(vtbl_array.data[i]); if (dsym == nullptr) { // FIXME // why is this null? // happens for mini/s.d constants.push_back(getNullValue(voidPtrTy)); continue; } FuncDeclaration *fd = dsym->isFuncDeclaration(); assert(fd && "vtbl entry not a function"); assert((!fd->isAbstract() || fd->fbody) && "null symbol in interface implementation vtable"); DtoResolveFunction(fd); assert(isIrFuncCreated(fd) && "invalid vtbl function"); IrFunction *irFunc = getIrFunc(fd); assert(irFunc->irFty.arg_this); int thunkOffset = b->offset; if (fd->interfaceVirtual) thunkOffset -= fd->interfaceVirtual->offset; if (thunkOffset == 0) { constants.push_back(DtoBitCast(irFunc->func, voidPtrTy)); continue; } // Create the thunk function if it does not already exist in this // module. OutBuffer nameBuf; const auto mangledTargetName = mangleExact(fd); nameBuf.write(mangledTargetName, 2); nameBuf.writestring(thunkPrefix); nameBuf.writestring(mangledTargetName + 2); const char *thunkName = nameBuf.extractString(); llvm::Function *thunk = gIR->module.getFunction(thunkName); if (!thunk) { const LinkageWithCOMDAT lwc(LLGlobalValue::LinkOnceODRLinkage, supportsCOMDAT()); thunk = LLFunction::Create( isaFunction(irFunc->func->getType()->getContainedType(0)), lwc.first, thunkName, &gIR->module); setLinkage(lwc, thunk); thunk->copyAttributesFrom(irFunc->func); // Thunks themselves don't have an identity, only the target // function has. #if LDC_LLVM_VER >= 309 thunk->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); #else thunk->setUnnamedAddr(true); #endif #if LDC_LLVM_VER >= 307 // thunks don't need exception handling themselves thunk->setPersonalityFn(nullptr); #endif // It is necessary to add debug information to the thunk in case it is // subject to inlining. See https://llvm.org/bugs/show_bug.cgi?id=26833 IF_LOG Logger::println("Doing function body for thunk to: %s", fd->toChars()); // Create a dummy FuncDeclaration with enough information to satisfy the // DIBuilder FuncDeclaration *thunkFd = reinterpret_cast( memcpy(new char[sizeof(FuncDeclaration)], (void *)fd, sizeof(FuncDeclaration))); thunkFd->ir = new IrDsymbol(); auto thunkFunc = getIrFunc(thunkFd, true); // create the IrFunction thunkFunc->func = thunk; thunkFunc->type = irFunc->type; gIR->funcGenStates.emplace_back(new FuncGenState(*thunkFunc, *gIR)); // debug info thunkFunc->diSubprogram = gIR->DBuilder.EmitThunk(thunk, thunkFd); // create entry and end blocks llvm::BasicBlock *beginbb = llvm::BasicBlock::Create(gIR->context(), "", thunk); gIR->scopes.push_back(IRScope(beginbb)); gIR->DBuilder.EmitFuncStart(thunkFd); // Copy the function parameters, so later we can pass them to the // real function and set their names from the original function (the // latter being just for IR readablilty). std::vector args; llvm::Function::arg_iterator thunkArg = thunk->arg_begin(); llvm::Function::arg_iterator origArg = irFunc->func->arg_begin(); for (; thunkArg != thunk->arg_end(); ++thunkArg, ++origArg) { thunkArg->setName(origArg->getName()); args.push_back(&(*thunkArg)); } // cast 'this' to Object const int thisArgIndex = (!irFunc->irFty.arg_sret || gABI->passThisBeforeSret(irFunc->type)) ? 0 : 1; LLValue *&thisArg = args[thisArgIndex]; LLType *targetThisType = thisArg->getType(); thisArg = DtoBitCast(thisArg, getVoidPtrType()); thisArg = DtoGEP1(thisArg, DtoConstInt(-thunkOffset), true); thisArg = DtoBitCast(thisArg, targetThisType); // all calls that might be subject to inlining into a caller with debug // info should have debug info, too gIR->DBuilder.EmitStopPoint(fd->loc); // call the real vtbl function. llvm::CallInst *call = gIR->ir->CreateCall(irFunc->func, args); call->setCallingConv(irFunc->func->getCallingConv()); call->setTailCallKind(llvm::CallInst::TCK_Tail); // return from the thunk if (thunk->getReturnType() == LLType::getVoidTy(gIR->context())) { llvm::ReturnInst::Create(gIR->context(), beginbb); } else { llvm::ReturnInst::Create(gIR->context(), call, beginbb); } gIR->DBuilder.EmitFuncEnd(thunkFd); // clean up gIR->scopes.pop_back(); gIR->funcGenStates.pop_back(); } constants.push_back(DtoBitCast(thunk, voidPtrTy)); } // build the vtbl constant llvm::Constant *vtbl_constant = LLConstantArray::get( LLArrayType::get(voidPtrTy, constants.size()), constants); std::string mangledName("_D"); mangledName.append(mangle(cd)); mangledName.append("11__interface"); mangledName.append(mangle(b->sym)); mangledName.append(thunkPrefixLen); mangledName.append(thunkPrefix); mangledName.append("6__vtblZ"); const auto lwc = DtoLinkage(cd); LLGlobalVariable *GV = getOrCreateGlobal(cd->loc, gIR->module, vtbl_constant->getType(), true, lwc.first, vtbl_constant, mangledName); setLinkage(lwc, GV); // insert into the vtbl map interfaceVtblMap.insert({{b->sym, interfaces_index}, GV}); return GV; } bool IrAggr::isPacked() const { return static_cast(type->ctype)->packed; } ////////////////////////////////////////////////////////////////////////////// LLConstant *IrAggr::getClassInfoInterfaces() { IF_LOG Logger::println("Building ClassInfo.interfaces"); LOG_SCOPE; ClassDeclaration *cd = aggrdecl->isClassDeclaration(); assert(cd); size_t n = interfacesWithVtbls.size(); assert(stripModifiers(type)->ctype->isClass()->getNumInterfaceVtbls() == n && "inconsistent number of interface vtables in this class"); VarDeclaration *interfaces_idx = Type::typeinfoclass->fields[3]; if (n == 0) { return getNullValue(DtoType(interfaces_idx->type)); } // Build array of: // // struct Interface // { // ClassInfo classinfo; // void*[] vtbl; // ptrdiff_t offset; // } LLSmallVector constants; constants.reserve(cd->vtblInterfaces->dim); LLType *classinfo_type = DtoType(Type::typeinfoclass->type); LLType *voidptrptr_type = DtoType(Type::tvoid->pointerTo()->pointerTo()); VarDeclaration *idx = Type::typeinfoclass->fields[3]; LLStructType *interface_type = isaStruct(DtoType(idx->type->nextOf())); assert(interface_type); for (size_t i = 0; i < n; ++i) { BaseClass *it = interfacesWithVtbls[i]; IF_LOG Logger::println("Adding interface %s", it->sym->toPrettyChars()); IrAggr *irinter = getIrAggr(it->sym); assert(irinter && "interface has null IrStruct"); IrTypeClass *itc = stripModifiers(irinter->type)->ctype->isClass(); assert(itc && "null interface IrTypeClass"); // classinfo LLConstant *ci = irinter->getClassInfoSymbol(); ci = DtoBitCast(ci, classinfo_type); // vtbl LLConstant *vtb; // interface get a null if (cd->isInterfaceDeclaration()) { vtb = DtoConstSlice(DtoConstSize_t(0), getNullValue(voidptrptr_type)); } else { auto itv = interfaceVtblMap.find({it->sym, i}); assert(itv != interfaceVtblMap.end() && "interface vtbl not found"); vtb = itv->second; vtb = DtoBitCast(vtb, voidptrptr_type); auto vtblSize = itc->getVtblType()->getNumContainedTypes(); vtb = DtoConstSlice(DtoConstSize_t(vtblSize), vtb); } // offset LLConstant *off = DtoConstSize_t(it->offset); // create Interface struct LLConstant *inits[3] = {ci, vtb, off}; LLConstant *entry = LLConstantStruct::get(interface_type, llvm::makeArrayRef(inits, 3)); constants.push_back(entry); } // create Interface[N] LLArrayType *array_type = llvm::ArrayType::get(interface_type, n); // create and apply initializer LLConstant *arr = LLConstantArray::get(array_type, constants); auto ciarr = getInterfaceArraySymbol(); ciarr->setInitializer(arr); setLinkage(cd, ciarr); // return null, only baseclass provide interfaces if (cd->vtblInterfaces->dim == 0) { return getNullValue(DtoType(interfaces_idx->type)); } // only the interface explicitly implemented by this class // (not super classes) should show in ClassInfo LLConstant *idxs[2] = {DtoConstSize_t(0), DtoConstSize_t(n - cd->vtblInterfaces->dim)}; LLConstant *ptr = llvm::ConstantExpr::getGetElementPtr( #if LDC_LLVM_VER >= 307 isaPointer(ciarr)->getElementType(), #endif ciarr, idxs, true); // return as a slice return DtoConstSlice(DtoConstSize_t(cd->vtblInterfaces->dim), ptr); } ////////////////////////////////////////////////////////////////////////////// void IrAggr::initializeInterface() { InterfaceDeclaration *base = aggrdecl->isInterfaceDeclaration(); assert(base && "not interface"); // has interface vtbls? if (!base->vtblInterfaces) { return; } for (auto bc : *base->vtblInterfaces) { // add to the interface list interfacesWithVtbls.push_back(bc); } } //////////////////////////////////////////////////////////////////////////////