static LLConstant* build_class_dtor(ClassDeclaration* cd) { FuncDeclaration* dtor = cd->dtor; // if no destructor emit a null if (!dtor) return getNullPtr(getVoidPtrType()); dtor->codegen(Type::sir); return llvm::ConstantExpr::getBitCast(dtor->ir.irFunc->func, getPtrToType(LLType::getInt8Ty(gIR->context()))); }
static void build_dso_registry_calls(std::string moduleMangle, llvm::Constant *thisModuleInfo) { // Build the ModuleInfo reference and bracketing symbols. llvm::Type *const moduleInfoPtrTy = DtoPtrToType(Module::moduleinfo->type); // Order is important here: We must create the symbols in the // bracketing sections right before/after the ModuleInfo reference // so that they end up in the correct order in the object file. auto minfoBeg = new llvm::GlobalVariable(gIR->module, moduleInfoPtrTy, false, // FIXME: mRelocModel != llvm::Reloc::PIC_ llvm::GlobalValue::LinkOnceODRLinkage, getNullPtr(moduleInfoPtrTy), "_minfo_beg"); minfoBeg->setSection(".minfo_beg"); minfoBeg->setVisibility(llvm::GlobalValue::HiddenVisibility); std::string thismrefname = "_D"; thismrefname += moduleMangle; thismrefname += "11__moduleRefZ"; auto thismref = new llvm::GlobalVariable( gIR->module, moduleInfoPtrTy, false, // FIXME: mRelocModel != llvm::Reloc::PIC_ llvm::GlobalValue::LinkOnceODRLinkage, DtoBitCast(thisModuleInfo, moduleInfoPtrTy), thismrefname); thismref->setSection(".minfo"); gIR->usedArray.push_back(thismref); auto minfoEnd = new llvm::GlobalVariable(gIR->module, moduleInfoPtrTy, false, // FIXME: mRelocModel != llvm::Reloc::PIC_ llvm::GlobalValue::LinkOnceODRLinkage, getNullPtr(moduleInfoPtrTy), "_minfo_end"); minfoEnd->setSection(".minfo_end"); minfoEnd->setVisibility(llvm::GlobalValue::HiddenVisibility); // Build the ctor to invoke _d_dso_registry. // This is the DSO slot for use by the druntime implementation. auto dsoSlot = new llvm::GlobalVariable(gIR->module, getVoidPtrType(), false, llvm::GlobalValue::LinkOnceODRLinkage, getNullPtr(getVoidPtrType()), "ldc.dso_slot"); dsoSlot->setVisibility(llvm::GlobalValue::HiddenVisibility); // Okay, so the theory is easy: We want to have one global constructor and // destructor per object (i.e. executable/shared library) that calls // _d_dso_registry with the respective DSO record. However, there are a // couple of issues that make this harder than necessary: // // 1) The natural way to implement the "one-per-image" part would be to // emit a weak reference to a weak function into a .ctors.<somename> // section (llvm.global_ctors doesn't support the necessary // functionality, so we'd use our knowledge of the linker script to work // around that). But as of LLVM 3.4, emitting a symbol both as weak and // into a custom section is not supported by the MC layer. Thus, we have // to use a normal ctor/dtor and manually ensure that we only perform // the call once. This is done by introducing ldc.dso_initialized. // // 2) To make sure the .minfo section isn't removed by the linker when // using --gc-sections, we need to keep a reference to it around in // _every_ object file (as --gc-sections works per object file). The // natural place for this is the ctor, where we just load a reference // on the stack after the DSO record (to ensure LLVM doesn't optimize // it out). However, this way, we need to have at least one ctor // instance per object file be pulled into the final executable. We // do this here by making the module mangle string part of its name, // even thoguht this is slightly wasteful on -singleobj builds. // // It might be a better idea to simply use a custom linker script (using // INSERT AFTER… so as to still keep the default one) to avoid all these // problems. This would mean that it is no longer safe to link D objects // directly using e.g. "g++ dcode.o cppcode.o", though. auto dsoInitialized = new llvm::GlobalVariable( gIR->module, llvm::Type::getInt8Ty(gIR->context()), false, llvm::GlobalValue::LinkOnceODRLinkage, llvm::ConstantInt::get(llvm::Type::getInt8Ty(gIR->context()), 0), "ldc.dso_initialized"); dsoInitialized->setVisibility(llvm::GlobalValue::HiddenVisibility); // There is no reason for this cast to void*, other than that removing it // seems to trigger a bug in the llvm::Linker (at least on LLVM 3.4) // causing it to not merge the %object.ModuleInfo types properly. This // manifests itself in a type mismatch assertion being triggered on the // minfoUsedPointer store in the ctor as soon as the optimizer runs. llvm::Value *minfoRefPtr = DtoBitCast(thismref, getVoidPtrType()); std::string ctorName = "ldc.dso_ctor."; ctorName += moduleMangle; llvm::Function *dsoCtor = llvm::Function::Create( llvm::FunctionType::get(llvm::Type::getVoidTy(gIR->context()), false), llvm::GlobalValue::LinkOnceODRLinkage, ctorName, &gIR->module); dsoCtor->setVisibility(llvm::GlobalValue::HiddenVisibility); build_dso_ctor_dtor_body(dsoCtor, dsoInitialized, dsoSlot, minfoBeg, minfoEnd, minfoRefPtr, false); llvm::appendToGlobalCtors(gIR->module, dsoCtor, 65535); std::string dtorName = "ldc.dso_dtor."; dtorName += moduleMangle; llvm::Function *dsoDtor = llvm::Function::Create( llvm::FunctionType::get(llvm::Type::getVoidTy(gIR->context()), false), llvm::GlobalValue::LinkOnceODRLinkage, dtorName, &gIR->module); dsoDtor->setVisibility(llvm::GlobalValue::HiddenVisibility); build_dso_ctor_dtor_body(dsoDtor, dsoInitialized, dsoSlot, minfoBeg, minfoEnd, minfoRefPtr, true); llvm::appendToGlobalDtors(gIR->module, dsoDtor, 65535); }
llvm::BasicBlock * TryCatchFinallyScopes::runCleanupPad(CleanupCursor scope, llvm::BasicBlock *unwindTo) { // a catch switch never needs to be cloned and is an unwind target itself if (isCatchSwitchBlock(cleanupScopes[scope].beginBlock())) return cleanupScopes[scope].beginBlock(); // each cleanup block is bracketed by a pair of cleanuppad/cleanupret // instructions, any unwinding should also just continue at the next // cleanup block, e.g.: // // cleanuppad: // %0 = cleanuppad within %funclet[] // %frame = nullptr // if (!_d_enter_cleanup(%frame)) br label %cleanupret // else br label %copy // // copy: // invoke _dtor to %cleanupret unwind %unwindTo [ "funclet"(token %0) ] // // cleanupret: // _d_leave_cleanup(%frame) // cleanupret %0 unwind %unwindTo // llvm::BasicBlock *cleanupbb = irs.insertBB("cleanuppad"); auto funcletToken = llvm::ConstantTokenNone::get(irs.context()); auto cleanuppad = llvm::CleanupPadInst::Create(funcletToken, {}, "", cleanupbb); llvm::BasicBlock *cleanupret = irs.insertBBAfter(cleanupbb, "cleanupret"); // preparation to allocate some space on the stack where _d_enter_cleanup // can place an exception frame (but not done here) auto frame = getNullPtr(getVoidPtrType()); auto savedInsertBlock = irs.ir->GetInsertBlock(); auto savedInsertPoint = irs.ir->GetInsertPoint(); auto savedDbgLoc = irs.DBuilder.GetCurrentLoc(); auto endFn = getRuntimeFunction(Loc(), irs.module, "_d_leave_cleanup"); irs.ir->SetInsertPoint(cleanupret); irs.DBuilder.EmitStopPoint(irs.func()->decl->loc); irs.ir->CreateCall(endFn, frame, {llvm::OperandBundleDef("funclet", cleanuppad)}, ""); llvm::CleanupReturnInst::Create(cleanuppad, unwindTo, cleanupret); auto copybb = cleanupScopes[scope].runCopying(irs, cleanupbb, cleanupret, unwindTo, cleanuppad); auto beginFn = getRuntimeFunction(Loc(), irs.module, "_d_enter_cleanup"); irs.ir->SetInsertPoint(cleanupbb); irs.DBuilder.EmitStopPoint(irs.func()->decl->loc); auto exec = irs.ir->CreateCall( beginFn, frame, {llvm::OperandBundleDef("funclet", cleanuppad)}, ""); llvm::BranchInst::Create(copybb, cleanupret, exec, cleanupbb); irs.ir->SetInsertPoint(savedInsertBlock, savedInsertPoint); irs.DBuilder.EmitStopPoint(savedDbgLoc); return cleanupbb; }