//===-- 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/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 "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 initZ symbol std::string initname("_D"); initname.append(mangle(aggrdecl)); initname.append("6__vtblZ"); LLType *vtblTy = stripModifiers(type)->ctype->isClass()->getVtbl(); vtbl = getOrCreateGlobal(aggrdecl->loc, gIR->module, vtblTy, true, llvm::GlobalValue::ExternalLinkage, nullptr, initname); return vtbl; } ////////////////////////////////////////////////////////////////////////////// LLGlobalVariable *IrAggr::getClassInfoSymbol() { if (classInfo) { return classInfo; } // create the initZ symbol std::string initname("_D"); initname.append(mangle(aggrdecl)); if (aggrdecl->isInterfaceDeclaration()) { initname.append("11__InterfaceZ"); } else { initname.append("7__ClassZ"); } // 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); // start with the classinfo llvm::Constant *c; if (!cd->isCPPclass()) { c = getClassInfoSymbol(); c = DtoBitCast(c, DtoType(Type::typeinfoclass->type)); 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[], then * issue 'hidden' error. */ for (size_t j = 1; j < n; j++) { if (j == i) { continue; } FuncDeclaration *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(c); } // build the constant struct LLType *vtblTy = stripModifiers(type)->ctype->isClass()->getVtbl(); #ifndef NDEBUG size_t nc = constants.size(); for (size_t i = 0; i < nc; ++i) { if (constants[i]->getType() != vtblTy->getContainedType(i)) { llvm::errs() << "type mismatch for entry # " << i << " in vtbl initializer\n"; constants[i]->getType()->dump(); vtblTy->getContainedType(i)->dump(); } } #endif constVtbl = LLConstantStruct::get(isaStruct(vtblTy), constants); assert(constVtbl->getType() == stripModifiers(type)->ctype->isClass()->getVtbl() && "vtbl initializer type mismatch"); 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); 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); 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(c); } // 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(getVoidPtrType())); 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); // Create the thunk function if it does not already exist in this // module. OutBuffer nameBuf; nameBuf.writestring("Th"); nameBuf.printf("%i", b->offset); nameBuf.writestring(mangleExact(fd)); const char *thunkName = nameBuf.extractString(); llvm::Function *thunk = gIR->module.getFunction(thunkName); if (!thunk) { thunk = LLFunction::Create( isaFunction(irFunc->func->getType()->getContainedType(0)), llvm::GlobalValue::LinkOnceODRLinkage, thunkName, &gIR->module); SET_COMDAT(thunk, gIR->module); thunk->copyAttributesFrom(irFunc->func); // Thunks themselves don't have an identity, only the target // function has. thunk->setUnnamedAddr(true); #if LDC_LLVM_VER >= 307 // thunks don't need exception handling themselves thunk->setPersonalityFn(nullptr); #endif // create entry and end blocks llvm::BasicBlock *beginbb = llvm::BasicBlock::Create(gIR->context(), "", thunk); gIR->scopes.push_back(IRScope(beginbb)); // 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 LLValue *&thisArg = args[(!irFunc->irFty.arg_sret || gABI->passThisBeforeSret(irFunc->type)) ? 0 : 1]; LLType *targetThisType = thisArg->getType(); thisArg = DtoBitCast(thisArg, getVoidPtrType()); thisArg = DtoGEP1(thisArg, DtoConstInt(-b->offset), true); thisArg = DtoBitCast(thisArg, targetThisType); // call the real vtbl function. llvm::CallSite call = gIR->ir->CreateCall(irFunc->func, args); call.setCallingConv(irFunc->func->getCallingConv()); // return from the thunk if (thunk->getReturnType() == LLType::getVoidTy(gIR->context())) { llvm::ReturnInst::Create(gIR->context(), beginbb); } else { llvm::ReturnInst::Create(gIR->context(), call.getInstruction(), beginbb); } // clean up gIR->scopes.pop_back(); } constants.push_back(thunk); } // build the vtbl constant llvm::Constant *vtbl_constant = LLConstantStruct::getAnon(gIR->context(), constants, false); std::string mangledName("_D"); mangledName.append(mangle(cd)); mangledName.append("11__interface"); mangledName.append(mangle(b->sym)); mangledName.append("6__vtblZ"); const LinkageWithCOMDAT lwc = DtoLinkage(cd); llvm::GlobalVariable *GV = getOrCreateGlobal(cd->loc, gIR->module, vtbl_constant->getType(), true, lwc.first, vtbl_constant, mangledName); if (lwc.second) { SET_COMDAT(GV, gIR->module); } // insert into the vtbl map interfaceVtblMap.insert(std::make_pair(b->sym, 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); assert(itv != interfaceVtblMap.end() && "interface vtbl not found"); vtb = itv->second; vtb = DtoBitCast(vtb, voidptrptr_type); vtb = DtoConstSlice(DtoConstSize_t(itc->getVtblSize()), 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); classInterfacesArray->setInitializer(arr); const LinkageWithCOMDAT lwc = DtoLinkage(cd); classInterfacesArray->setLinkage(lwc.first); if (lwc.second) { SET_COMDAT(classInterfacesArray, gIR->module); } // 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(classInterfacesArray)->getElementType(), #endif classInterfacesArray, 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); } } //////////////////////////////////////////////////////////////////////////////