From 35d9d139e6f801b6d63492063190410aed151d11 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Mon, 21 Dec 2015 18:46:28 +0100 Subject: [PATCH 01/19] thunks don't need a personality --- ir/irclass.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ir/irclass.cpp b/ir/irclass.cpp index e980e64667f..6356c69f526 100644 --- a/ir/irclass.cpp +++ b/ir/irclass.cpp @@ -350,6 +350,11 @@ llvm::GlobalVariable *IrAggr::getInterfaceVtbl(BaseClass *b, bool new_instance, // 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); From 7e9d60bd7db8901d9ed4dca8641da5cedbe17e14 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Mon, 21 Dec 2015 21:34:00 +0100 Subject: [PATCH 02/19] MSVC/x86 EH support using __CxxFrameHandler3 personality --- gen/irstate.h | 6 + gen/ms-cxx-helper.cpp | 265 ++++++++++++++++++++++++++++++++++++++++++ gen/ms-cxx-helper.h | 31 +++++ gen/runtime.cpp | 5 +- gen/statements.cpp | 230 +++++++++++++++++++++++++++++------- ir/irfunction.cpp | 234 ++++++++++++++++++++++++++++++++++++- ir/irfunction.h | 71 ++++++++--- 7 files changed, 779 insertions(+), 63 deletions(-) create mode 100644 gen/ms-cxx-helper.cpp create mode 100644 gen/ms-cxx-helper.h diff --git a/gen/irstate.h b/gen/irstate.h index 09bc607ac7b..deb93866498 100644 --- a/gen/irstate.h +++ b/gen/irstate.h @@ -182,6 +182,12 @@ struct IRState { #else llvm::SmallVector LinkerMetadataArgs; #endif + +#if LDC_LLVM_VER >= 308 + // MS C++ compatible type descriptors + llvm::DenseMap TypeDescriptorTypeMap; + llvm::DenseMap TypeDescriptorMap; +#endif }; void Statement_toIR(Statement *s, IRState *irs); diff --git a/gen/ms-cxx-helper.cpp b/gen/ms-cxx-helper.cpp new file mode 100644 index 00000000000..ed30327bed3 --- /dev/null +++ b/gen/ms-cxx-helper.cpp @@ -0,0 +1,265 @@ +//===-- ms-cxx-helper.cpp -------------------------------------------------===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the BSD-style LDC license. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// + +#include "gen/ms-cxx-helper.h" +#include "gen/llvm.h" +#include "gen/llvmhelpers.h" +#include "gen/irstate.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" + +#if LDC_LLVM_VER >= 308 +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/IR/CFG.h" + +llvm::BasicBlock *getUnwindDest(llvm::Instruction *I) { + if (auto II = llvm::dyn_cast (I)) + return II->getUnwindDest(); + else if (auto CSI = llvm::dyn_cast (I)) + return CSI->getUnwindDest(); + else if (auto CRPI = llvm::dyn_cast (I)) + return CRPI->getUnwindDest(); + return nullptr; +} + +void mapFunclet(llvm::Instruction *I, llvm::ValueToValueMapTy &VMap, llvm::Value *funclet) { + if (auto II = llvm::dyn_cast (I)) { + auto bundle = II->getOperandBundle(llvm::LLVMContext::OB_funclet); + if (bundle) + VMap[bundle->Inputs[0].get()] = funclet; + } else if (auto CI = llvm::dyn_cast (I)) { + auto bundle = CI->getOperandBundle(llvm::LLVMContext::OB_funclet); + if (bundle) + VMap[bundle->Inputs[0].get()] = funclet; + } +} + +// return all basic blocks that are reachable from bb, but don't pass through +// ebb and don't follow unwinding target +void findSuccessors(std::vector &blocks, + llvm::BasicBlock *bb, llvm::BasicBlock *ebb) { + blocks.push_back(bb); + if (bb != ebb) { + assert(bb->getTerminator()); + for (size_t pos = 0; pos < blocks.size(); ++pos) { + bb = blocks[pos]; + if (auto term = bb->getTerminator()) { + llvm::BasicBlock *unwindDest = getUnwindDest(term); + unsigned cnt = term->getNumSuccessors(); + for (unsigned s = 0; s < cnt; s++) { + llvm::BasicBlock *succ = term->getSuccessor(s); + if (succ != ebb && succ != unwindDest && + std::find(blocks.begin(), blocks.end(), succ) == blocks.end()) { + blocks.push_back(succ); + } + } + } + } + blocks.push_back(ebb); + } +} + +// remap values in all instructions of all blocks +void remapBlocks(std::vector &blocks, + llvm::ValueToValueMapTy &VMap, llvm::BasicBlock *unwindTo, + llvm::Value *funclet) { + for (llvm::BasicBlock *bb : blocks) + for (llvm::BasicBlock::iterator I = bb->begin(); I != bb->end(); ++I) { + //if (funclet) + // mapFunclet(&*I, VMap, funclet); + llvm::RemapInstruction(&*I, VMap, llvm::RF_IgnoreMissingEntries | + llvm::RF_NoModuleLevelChanges); + } +} + +void remapBlocksValue(std::vector &blocks, + llvm::Value *from, llvm::Value *to) { + llvm::ValueToValueMapTy VMap; + VMap[from] = to; + remapBlocks(blocks, VMap, nullptr, nullptr); +} + +// make a copy of all srcblocks, mapping values to clones and redirect srcTarget +// to continueWith +void cloneBlocks(const std::vector &srcblocks, + std::vector &blocks, + llvm::BasicBlock *continueWith, llvm::BasicBlock *unwindTo, + llvm::Value *funclet) { + llvm::ValueToValueMapTy VMap; + // map the terminal branch to the new target + if (continueWith) + if (auto term = srcblocks.back()->getTerminator()) + if (auto succ = term->getSuccessor(0)) + VMap[succ] = continueWith; + + for (size_t b = 0; b < srcblocks.size(); b++) { + llvm::BasicBlock *bb = srcblocks[b]; + llvm::Function* F = bb->getParent(); + + auto nbb = llvm::BasicBlock::Create(bb->getContext()); + // Loop over all instructions, and copy them over. + for (auto II = bb->begin(), IE = bb->end(); II != IE; ++II) { + llvm::Instruction *Inst = &*II; + llvm::Instruction *newInst = nullptr; + if (funclet && !llvm::isa(Inst)) { + if (auto IInst = llvm::dyn_cast (Inst)) { + newInst = llvm::InvokeInst::Create( + IInst, llvm::OperandBundleDef("funclet", funclet)); + } else if (auto CInst = llvm::dyn_cast (Inst)) { + newInst = llvm::CallInst::Create( + CInst, llvm::OperandBundleDef("funclet", funclet)); + } + } + if (!newInst) + newInst = Inst->clone(); + + nbb->getInstList().push_back(newInst); + VMap[Inst] = newInst; // Add instruction map to value. + if (unwindTo) + if (auto dest = getUnwindDest(Inst)) + VMap[dest] = unwindTo; + } + nbb->insertInto(F, continueWith); + VMap[bb] = nbb; + blocks.push_back(nbb); + } + + remapBlocks(blocks, VMap, unwindTo, funclet); +} + +// copy from clang/.../MicrosoftCXXABI.cpp + +// 5 routines for constructing the llvm types for MS RTTI structs. +llvm::StructType *getTypeDescriptorType(IRState &irs, + llvm::Constant *classInfoPtr, + llvm::StringRef TypeInfoString) { + llvm::SmallString<256> TDTypeName("rtti.TypeDescriptor"); + TDTypeName += llvm::utostr(TypeInfoString.size()); + llvm::StructType *&TypeDescriptorType = + irs.TypeDescriptorTypeMap[TypeInfoString.size()]; + if (TypeDescriptorType) + return TypeDescriptorType; + auto int8Ty = LLType::getInt8Ty(gIR->context()); + llvm::Type *FieldTypes[] = { + classInfoPtr->getType(), // CGM.Int8PtrPtrTy, + getPtrToType(int8Ty), // CGM.Int8PtrTy, + llvm::ArrayType::get(int8Ty, TypeInfoString.size() + 1)}; + TypeDescriptorType = + llvm::StructType::create(gIR->context(), FieldTypes, TDTypeName); + return TypeDescriptorType; +} + +llvm::GlobalVariable *getTypeDescriptor(IRState &irs, ClassDeclaration *cd) { + + auto classInfoPtr = getIrAggr(cd, true)->getClassInfoSymbol(); + llvm::GlobalVariable *&Var = irs.TypeDescriptorMap[classInfoPtr]; + if (Var) + return Var; + + // first character skipped in debugger output, so we add 'D' as prefix + std::string TypeNameString = "D"; + TypeNameString.append(cd->toPrettyChars()); + std::string TypeDescName = TypeNameString + "@TypeDescriptor"; + + // Declare and initialize the TypeDescriptor. + llvm::Constant *Fields[] = { + classInfoPtr, // VFPtr + llvm::ConstantPointerNull::get( + LLType::getInt8PtrTy(gIR->context())), // Runtime data + llvm::ConstantDataArray::getString(gIR->context(), TypeNameString)}; + llvm::StructType *TypeDescriptorType = + getTypeDescriptorType(irs, classInfoPtr, TypeNameString); + Var = new llvm::GlobalVariable( + gIR->module, TypeDescriptorType, /*Constant=*/false, + LLGlobalVariable::InternalLinkage, // getLinkageForRTTI(Type), + llvm::ConstantStruct::get(TypeDescriptorType, Fields), TypeDescName); + return Var; +} + +#if 0 +// currently unused, information built at runtime ATM +llvm::StructType *BaseClassDescriptorType; +llvm::StructType *ClassHierarchyDescriptorType; +llvm::StructType *CompleteObjectLocatorType; + +bool isImageRelative() { + return global.params.targetTriple.isArch64Bit(); +} + +llvm::Type *getImageRelativeType(llvm::Type *PtrType) { + if (!isImageRelative()) + return PtrType; + return LLType::getInt32Ty(gIR->context()); +} + +llvm::StructType *getClassHierarchyDescriptorType(); + +llvm::StructType *getBaseClassDescriptorType() { + if (BaseClassDescriptorType) + return BaseClassDescriptorType; + auto intTy = LLType::getInt32Ty(gIR->context()); + auto int8Ty = LLType::getInt8Ty(gIR->context()); + llvm::Type *FieldTypes[] = { + getImageRelativeType(getPtrToType(int8Ty)), + intTy, + intTy, + intTy, + intTy, + intTy, + getImageRelativeType(getClassHierarchyDescriptorType()->getPointerTo()), + }; + BaseClassDescriptorType = llvm::StructType::create( + gIR->context(), FieldTypes, "rtti.BaseClassDescriptor"); + return BaseClassDescriptorType; +} + +llvm::StructType *getClassHierarchyDescriptorType() { + if (ClassHierarchyDescriptorType) + return ClassHierarchyDescriptorType; + // Forward-declare RTTIClassHierarchyDescriptor to break a cycle. + ClassHierarchyDescriptorType = llvm::StructType::create( + gIR->context(), "rtti.ClassHierarchyDescriptor"); + auto intTy = LLType::getInt32Ty(gIR->context()); + llvm::Type *FieldTypes[] = { + intTy, + intTy, + intTy, + getImageRelativeType( + getBaseClassDescriptorType()->getPointerTo()->getPointerTo()), + }; + ClassHierarchyDescriptorType->setBody(FieldTypes); + return ClassHierarchyDescriptorType; +} + +llvm::StructType *getCompleteObjectLocatorType() { + if (CompleteObjectLocatorType) + return CompleteObjectLocatorType; + CompleteObjectLocatorType = llvm::StructType::create( + gIR->context(), "rtti.CompleteObjectLocator"); + auto intTy = LLType::getInt32Ty(gIR->context()); + auto int8Ty = LLType::getInt8Ty(gIR->context()); + llvm::Type *FieldTypes[] = { + intTy, + intTy, + intTy, + getImageRelativeType(getPtrToType(int8Ty)), + getImageRelativeType(getClassHierarchyDescriptorType()->getPointerTo()), + getImageRelativeType(CompleteObjectLocatorType), + }; + llvm::ArrayRef FieldTypesRef(FieldTypes); + if (!isImageRelative()) + FieldTypesRef = FieldTypesRef.drop_back(); + CompleteObjectLocatorType->setBody(FieldTypesRef); + return CompleteObjectLocatorType; +} +#endif + +#endif // LDC_LLVM_VER >= 308 diff --git a/gen/ms-cxx-helper.h b/gen/ms-cxx-helper.h new file mode 100644 index 00000000000..6ee70516f6b --- /dev/null +++ b/gen/ms-cxx-helper.h @@ -0,0 +1,31 @@ +//===-- ms-cxx-helper.h ---------------------------------------------------===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the BSD-style LDC license. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LDC_GEN_MS_CXX_HELPER_H +#define LDC_GEN_MS_CXX_HELPER_H + +#include "gen/irstate.h" + +llvm::StructType *getTypeDescriptorType(IRState &irs, + llvm::Constant *classInfoPtr, + llvm::StringRef TypeInfoString); +llvm::GlobalVariable *getTypeDescriptor(IRState &irs, ClassDeclaration *cd); + +void findSuccessors(std::vector &blocks, + llvm::BasicBlock *bb, llvm::BasicBlock *ebb); + +void remapBlocksValue(std::vector &blocks, + llvm::Value *from, llvm::Value *to); + +void cloneBlocks(const std::vector &srcblocks, + std::vector &blocks, + llvm::BasicBlock *continueWith, llvm::BasicBlock *unwindTo, + llvm::Value *funclet); + +#endif // LDC_GEN_MS_CXX_HELPER_H diff --git a/gen/runtime.cpp b/gen/runtime.cpp index 22e536a4f95..bc0b0b7c37e 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -24,6 +24,7 @@ #include "gen/llvmhelpers.h" #include "gen/logger.h" #include "gen/tollvm.h" +#include "ir/irfunction.h" #include "ir/irtype.h" #include "ir/irtypefunction.h" #include "llvm/Bitcode/ReaderWriter.h" @@ -620,9 +621,11 @@ static void buildRuntimeModule() { // int _d_eh_personality(...) { if (global.params.targetTriple->isWindowsMSVCEnvironment()) { + const char *fname = + useMSVCEH() ? "__CxxFrameHandler3" : "_d_eh_personality"; // (ptr ExceptionRecord, ptr EstablisherFrame, ptr ContextRecord, // ptr DispatcherContext) - createFwdDecl(LINKc, intTy, {"_d_eh_personality"}, + createFwdDecl(LINKc, intTy, {fname}, {voidPtrTy, voidPtrTy, voidPtrTy, voidPtrTy}); } else if (global.params.targetTriple->getArch() == llvm::Triple::arm) { // (int state, ptr ucb, ptr context) diff --git a/gen/statements.cpp b/gen/statements.cpp index e8f4eee0494..bfd073e40c0 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -23,6 +23,7 @@ #include "gen/logger.h" #include "gen/runtime.h" #include "gen/tollvm.h" +#include "gen/ms-cxx-helper.h" #include "ir/irfunction.h" #include "ir/irmodule.h" #include "llvm/IR/CFG.h" @@ -709,6 +710,88 @@ class ToIRVisitor : public Visitor { ////////////////////////////////////////////////////////////////////////// +#if LDC_LLVM_VER >= 308 + void emitBeginCatchMSVC(Catch *ctch, llvm::BasicBlock *catchbb, + llvm::BasicBlock *endbb, + llvm::CatchSwitchInst *catchSwitchInst) { + VarDeclaration *var = ctch->var; + // The MSVC/x86 build uses C++ exception handling + // This needs a series of catch pads to match the exception + // and the catch handler must be terminated by a catch return instruction + LLValue *exnObj = nullptr; + LLValue *cpyObj = nullptr; + LLValue *typeDesc = nullptr; + if (var) { + // alloca storage for the variable, it always needs a place on the stack + // do not initialize, this will be done by the C++ exception handler + var->init = nullptr; + + // redirect scope to avoid the generation of debug info before the + // catchpad + IRScope save = irs->scope(); + irs->scope() = IRScope(gIR->topallocapoint()->getParent()); + irs->scope().builder.SetInsertPoint(gIR->topallocapoint()); + DtoDeclarationExp(var); + + // catch handler will be outlined, so always treat as a nested reference + exnObj = getIrValue(var); + + if (var->nestedrefs.dim) { + // if variable needed in a closure, use a stack temporary and copy it + // when caught + cpyObj = exnObj; + exnObj = DtoAlloca(var->type, "exnObj"); + } + + irs->scope() = save; + + } else { + // catch without var + exnObj = llvm::Constant::getNullValue(getVoidPtrType()); + } + + if (ctch->type) { + ClassDeclaration *cd = ctch->type->toBasetype()->isClassHandle(); + typeDesc = getTypeDescriptor(*irs, cd); + } else { + // catch all + typeDesc = llvm::Constant::getNullValue(getVoidPtrType()); + } + + // "catchpad within %switch [TypeDescriptor, 0, &caughtObject]" must be + // first + // instruction + int flags = var ? 0 : 64; // just mimicking clang here + LLValue *args[] = {typeDesc, DtoConstUint(flags), exnObj}; + auto catchpad = llvm::CatchPadInst::Create( + catchSwitchInst, llvm::ArrayRef(args), "", catchbb); + catchSwitchInst->addHandler(catchbb); + + irs->scope() = IRScope(catchbb); + + if (cpyObj) { + // assign the caught exception to the location in the closure + auto val = irs->ir->CreateLoad(exnObj); + irs->ir->CreateStore(val, cpyObj); + exnObj = cpyObj; + } + const auto enterCatchFn = + getRuntimeFunction(Loc(), irs->module, "_d_eh_enter_catch"); + auto throwableObj = + irs->ir->CreateCall(enterCatchFn, DtoBitCast(exnObj, getVoidPtrType()), + {llvm::OperandBundleDef("funclet", catchpad)}); + + // The code generator will extract the catch handler to funclets + // so it needs to know the end of the code executed in the handler. + // This is marked by a catch return instruction that is created here + // as a cleanup so it appears in all code paths exiting the catch block + llvm::BasicBlock *retbb = + llvm::BasicBlock::Create(irs->context(), "catchret", irs->topfunc()); + llvm::CatchReturnInst::Create(catchpad, endbb, retbb); + irs->func()->scopes->pushCleanup(retbb, retbb); + } +#endif + void visit(TryCatchStatement *stmt) LLVM_OVERRIDE { IF_LOG Logger::println("TryCatchStatement::toIR(): %s", stmt->loc.toChars()); @@ -733,59 +816,118 @@ class ToIRVisitor : public Visitor { CatchBlocks catchBlocks; catchBlocks.reserve(stmt->catches->dim); - for (Catches::reverse_iterator it = stmt->catches->rbegin(), - end = stmt->catches->rend(); - it != end; ++it) { - llvm::BasicBlock *catchBB = llvm::BasicBlock::Create( +#if LDC_LLVM_VER >= 308 + if (useMSVCEH()) { + ScopeStack *scopes = irs->func()->scopes; + auto catchSwitchBlock = llvm::BasicBlock::Create( + irs->context(), "catch.dispatch", irs->topfunc()); + llvm::BasicBlock *unwindto = + scopes->currentCleanupScope() > 0 || scopes->currentCatchScope() > 0 + ? scopes->getLandingPad() + : nullptr; + auto funclet = scopes->getFunclet(); + auto catchSwitchInst = llvm::CatchSwitchInst::Create( + funclet ? funclet : llvm::ConstantTokenNone::get(irs->context()), + unwindto, stmt->catches->dim, "", catchSwitchBlock); + + for (auto it = stmt->catches->begin(), end = stmt->catches->end(); + it != end; ++it) { + llvm::BasicBlock *catchBB = llvm::BasicBlock::Create( irs->context(), llvm::Twine("catch.") + (*it)->type->toChars(), irs->topfunc(), endbb); - irs->scope() = IRScope(catchBB); - irs->DBuilder.EmitBlockStart((*it)->loc); - - const auto enterCatchFn = - getRuntimeFunction(Loc(), irs->module, "_d_eh_enter_catch"); - auto ptr = DtoLoad(irs->func()->getOrCreateEhPtrSlot()); - auto throwableObj = irs->ir->CreateCall(enterCatchFn, ptr); - - // For catches that use the Throwable object, create storage for it. - // We will set it in the code that branches from the landing pads - // (there might be more than one) to catchBB. - auto var = (*it)->var; - if (var) { - // This will alloca if we haven't already and take care of nested refs - // if there are any. - DtoDeclarationExp(var); - - // Copy the exception reference over from the _d_eh_enter_catch return - // value. - DtoStore(DtoBitCast(throwableObj, DtoType((*it)->var->type)), - getIrLocal(var)->value); - } + irs->scope() = IRScope(catchBB); + irs->DBuilder.EmitBlockStart((*it)->loc); - // Emit handler, if there is one. The handler is zero, for instance, when - // building 'catch { debug foo(); }' in non-debug mode. - if ((*it)->handler) { - Statement_toIR((*it)->handler, irs); - } + CleanupCursor currentScope = scopes->currentCleanupScope(); - if (!irs->scopereturned()) { - irs->ir->CreateBr(endbb); - } + emitBeginCatchMSVC(*it, catchBB, endbb, catchSwitchInst); + scopes->pushFunclet(&catchBB->front()); - irs->DBuilder.EmitBlockEnd(); + // Emit handler, if there is one. The handler is zero, for instance, + // when building 'catch { debug foo(); }' in non-debug mode. + if ((*it)->handler) { + Statement_toIR((*it)->handler, irs); + } + if (!irs->scopereturned()) { + scopes->runCleanups(currentScope, endbb); + } + scopes->popCleanups(currentScope); + scopes->popFunclet(); + + irs->DBuilder.EmitBlockEnd(); + + } catchBlocks.push_back( - std::make_pair((*it)->type->toBasetype()->isClassHandle(), catchBB)); - } + std::make_pair(nullptr, catchSwitchBlock)); // just for cleanup + scopes->pushCatch(nullptr, catchSwitchBlock); + + // if no landing pad is created, the catch blocks are unused, but + // the verifier complains if there are catchpads without personality + // so we can just set it unconditionally + if (!irs->func()->func->hasPersonalityFn()) { + const char *personality = "__CxxFrameHandler3"; + LLFunction *personalityFn = + getRuntimeFunction(Loc(), irs->module, personality); + irs->func()->func->setPersonalityFn(personalityFn); + } + } else +#endif + { + for (Catches::reverse_iterator it = stmt->catches->rbegin(), + end = stmt->catches->rend(); + it != end; ++it) { + llvm::BasicBlock *catchBB = llvm::BasicBlock::Create( + irs->context(), llvm::Twine("catch.") + (*it)->type->toChars(), + irs->topfunc(), endbb); + + irs->scope() = IRScope(catchBB); + irs->DBuilder.EmitBlockStart((*it)->loc); + + const auto enterCatchFn = + getRuntimeFunction(Loc(), irs->module, "_d_eh_enter_catch"); + auto ptr = DtoLoad(irs->func()->getOrCreateEhPtrSlot()); + auto throwableObj = irs->ir->CreateCall(enterCatchFn, ptr); + + // For catches that use the Throwable object, create storage for it. + // We will set it in the code that branches from the landing pads + // (there might be more than one) to catchBB. + auto var = (*it)->var; + if (var) { + // This will alloca if we haven't already and take care of nested refs + // if there are any. + DtoDeclarationExp(var); + + // Copy the exception reference over from the _d_eh_enter_catch return + // value. + DtoStore(DtoBitCast(throwableObj, DtoType((*it)->var->type)), + getIrLocal(var)->value); + } - // Only after emitting all the catch bodies, register the catch scopes. - // This is so that (re)throwing inside a catch does not match later - // catches. - for (const auto &pair : catchBlocks) { - DtoResolveClass(pair.first); - irs->func()->scopes->pushCatch( - getIrAggr(pair.first)->getClassInfoSymbol(), pair.second); + // Emit handler, if there is one. The handler is zero, for instance, + // when building 'catch { debug foo(); }' in non-debug mode. + if ((*it)->handler) { + Statement_toIR((*it)->handler, irs); + } + + if (!irs->scopereturned()) { + irs->ir->CreateBr(endbb); + } + + irs->DBuilder.EmitBlockEnd(); + + catchBlocks.push_back(std::make_pair( + (*it)->type->toBasetype()->isClassHandle(), catchBB)); + } + // Only after emitting all the catch bodies, register the catch scopes. + // This is so that (re)throwing inside a catch does not match later + // catches. + for (const auto &pair : catchBlocks) { + DtoResolveClass(pair.first); + irs->func()->scopes->pushCatch( + getIrAggr(pair.first)->getClassInfoSymbol(), pair.second); + } } // Emit the try block. diff --git a/ir/irfunction.cpp b/ir/irfunction.cpp index bfdd7136a28..14d5ee16c44 100644 --- a/ir/irfunction.cpp +++ b/ir/irfunction.cpp @@ -12,6 +12,7 @@ #include "gen/irstate.h" #include "gen/runtime.h" #include "gen/tollvm.h" +#include "gen/ms-cxx-helper.h" #include "ir/irdsymbol.h" #include "ir/irfunction.h" #include @@ -31,10 +32,74 @@ CatchScope::CatchScope(llvm::Constant *classInfoPtr, : classInfoPtr(classInfoPtr), bodyBlock(bodyBlock), cleanupScope(cleanupScope) {} +bool useMSVCEH() { + return global.params.targetTriple.isWindowsMSVCEnvironment() && + !global.params.targetTriple.isArch64Bit(); +} + namespace { + +#if LDC_LLVM_VER >= 308 + +// MSVC/x86 uses C++ exception handling that puts cleanup blocks into funclets. +// This means that we cannot use a branch selector and conditional branches +// at cleanup exit to continue with different targets. +// Instead we make a full copy of the cleanup code for every target +// +// Return the beginning basic block of the cleanup code +llvm::BasicBlock *executeCleanupCopying(IRState *irs, CleanupScope &scope, + llvm::BasicBlock *sourceBlock, + llvm::BasicBlock *continueWith, + llvm::BasicBlock *unwindTo, + llvm::Value* funclet) { + if (scope.cleanupBlocks.empty()) { + // figure out the list of blocks used by this cleanup step + findSuccessors(scope.cleanupBlocks, scope.beginBlock, scope.endBlock); + if (!scope.endBlock->getTerminator()) + // Set up the unconditional branch at the end of the cleanup + llvm::BranchInst::Create(continueWith, scope.endBlock); + } else { + // check whether we have an exit target with the same continuation + for (CleanupExitTarget &tgt : scope.exitTargets) + if (tgt.branchTarget == continueWith) { + tgt.sourceBlocks.push_back(sourceBlock); + return tgt.cleanupBlocks.front(); + } + } + + // reuse the original IR if not unwinding and not already used + bool useOriginal = unwindTo == nullptr && funclet == nullptr; + for (CleanupExitTarget &tgt : scope.exitTargets) + useOriginal = useOriginal && tgt.cleanupBlocks.front() != scope.beginBlock; + + // append new target + scope.exitTargets.push_back(CleanupExitTarget(continueWith)); + scope.exitTargets.back().sourceBlocks.push_back(sourceBlock); + + if (useOriginal) { + // change the continuation target if the initial branch was created + // by another instance with unwinding + if (continueWith) + if (auto term = scope.endBlock->getTerminator()) + if (auto succ = term->getSuccessor(0)) + if (succ != continueWith) { + remapBlocksValue(scope.cleanupBlocks, succ, continueWith); + } + scope.exitTargets.back().cleanupBlocks = scope.cleanupBlocks; + } else { + // clone the code + cloneBlocks(scope.cleanupBlocks, scope.exitTargets.back().cleanupBlocks, + continueWith, unwindTo, funclet); + } + return scope.exitTargets.back().cleanupBlocks.front(); +} +#endif // LDC_LLVM_VER >= 308 + void executeCleanup(IRState *irs, CleanupScope &scope, llvm::BasicBlock *sourceBlock, llvm::BasicBlock *continueWith) { + assert(!useMSVCEH()); // should always use executeCleanupCopying + if (scope.exitTargets.empty() || (scope.exitTargets.size() == 1 && scope.exitTargets[0].branchTarget == continueWith)) { @@ -134,6 +199,12 @@ void ScopeStack::pushCleanup(llvm::BasicBlock *beginBlock, void ScopeStack::runCleanups(CleanupCursor sourceScope, CleanupCursor targetScope, llvm::BasicBlock *continueWith) { +#if LDC_LLVM_VER >= 308 + if (useMSVCEH()) { + runCleanupCopies(sourceScope, targetScope, continueWith, false); + return; + } +#endif assert(targetScope <= sourceScope); if (targetScope == sourceScope) { @@ -154,6 +225,76 @@ void ScopeStack::runCleanups(CleanupCursor sourceScope, } } +#if LDC_LLVM_VER >= 308 +void ScopeStack::runCleanupCopies(CleanupCursor sourceScope, + CleanupCursor targetScope, + llvm::BasicBlock *continueWith, + bool withCleanupRet) { + assert(targetScope <= sourceScope); + + if (withCleanupRet) { + llvm::BasicBlock *target = continueWith; + for (CleanupCursor i = targetScope; i < sourceScope; ++i) { + // each cleanup block is bracketed by a pair of cleanuppad/cleanupret + // instructions, unwinding should also just continue at the next + // cleanup block + // cleanuppad: + // %0 = cleanuppad[] + // invoke _dtor to %cleanupret unwind %continueWith + // + // cleanupret: + // cleanupret %0 unwind %continueWith + // + // continueWith: + llvm::BasicBlock *cleanupbb = + i == sourceScope - 1 + ? irs->scopebb() + : llvm::BasicBlock::Create(irs->context(), "cleanuppad", + irs->topfunc()); + auto funclet = getFunclet(); + auto cleanuppad = llvm::CleanupPadInst::Create( + funclet ? funclet : llvm::ConstantTokenNone::get(irs->context()), {}, + "", cleanupbb); + + llvm::BasicBlock *cleanupret = llvm::BasicBlock::Create( + irs->context(), "cleanupret", irs->topfunc()); + + // when hitting a catch return instruction during cleanup, + // unwind to the corresponding catchswitch block instead + auto catchret = cleanupScopes[i].beginBlock->empty() + ? nullptr + : llvm::dyn_cast( + &cleanupScopes[i].beginBlock->front()); + if (catchret) { + llvm::BasicBlock* endcatch = nullptr; + auto catchpad = catchret->getCatchPad(); + auto catchswitch = catchpad->getCatchSwitch(); + + llvm::CleanupReturnInst::Create(cleanuppad, catchswitch->getUnwindDest(), + cleanupret); + continueWith = cleanupret; + + } else { + llvm::CleanupReturnInst::Create(cleanuppad, continueWith, cleanupret); + continueWith = executeCleanupCopying(irs, cleanupScopes[i], cleanupbb, + cleanupret, continueWith, cleanuppad); + } + llvm::BranchInst::Create(continueWith, cleanupbb); + continueWith = cleanupbb; + } + } else { + // work through the blocks in reverse execution order, so we + // can merge cleanups that end up at the same continuation target + for (CleanupCursor i = targetScope; i < sourceScope; ++i) + continueWith = executeCleanupCopying(irs, cleanupScopes[i], irs->scopebb(), + continueWith, nullptr, nullptr); + + // Insert the unconditional branch to the first cleanup block. + irs->ir->CreateBr(continueWith); + } +} +#endif + void ScopeStack::runAllCleanups(llvm::BasicBlock *continueWith) { runCleanups(0, continueWith); } @@ -170,11 +311,24 @@ void ScopeStack::popCleanups(CleanupCursor targetScope) { for (const auto &gotoJump : currentUnresolvedGotos()) { // Make the source resp. last cleanup branch to this one. llvm::BasicBlock *tentative = gotoJump.tentativeTarget; - tentative->replaceAllUsesWith(cleanupScopes[i].beginBlock); +#if LDC_LLVM_VER >= 308 + if (useMSVCEH()) { + llvm::BasicBlock *continueWith = + llvm::BasicBlock::Create(irs->context(), "jumpcleanup", irs->topfunc()); + auto startCleanup = + executeCleanupCopying(irs, cleanupScopes[i], gotoJump.sourceBlock, + continueWith, nullptr, nullptr); + tentative->replaceAllUsesWith(startCleanup); + llvm::BranchInst::Create(tentative, continueWith); + } else +#endif + { + tentative->replaceAllUsesWith(cleanupScopes[i].beginBlock); - // And continue execution with the tentative target (we simply reuse - // it because there is no reason not to). - executeCleanup(irs, cleanupScopes[i], gotoJump.sourceBlock, tentative); + // And continue execution with the tentative target (we simply reuse + // it because there is no reason not to). + executeCleanup(irs, cleanupScopes[i], gotoJump.sourceBlock, tentative); + } } std::vector &nextUnresolved = @@ -282,6 +436,25 @@ std::vector &ScopeStack::currentLandingPads() { : cleanupScopes.back().landingPads; } +llvm::BasicBlock *ScopeStack::getLandingPad() { + if (currentLandingPads().empty()) { + // Have not encountered any catches (for which we would push a scope) or + // calls to throwing functions (where we would have already executed + // this if) in this cleanup scope yet. + currentLandingPads().push_back(nullptr); + } + llvm::BasicBlock *&landingPad = currentLandingPads().back(); + if (!landingPad) { +#if LDC_LLVM_VER >= 308 + if (useMSVCEH()) + landingPad = emitWin32LandingPad(); + else +#endif + landingPad = emitLandingPad(); + } + return landingPad; +} + namespace { llvm::LandingPadInst *createLandingPadInst(IRState *irs) { LLType *retType = @@ -303,6 +476,59 @@ llvm::LandingPadInst *createLandingPadInst(IRState *irs) { } } +#if LDC_LLVM_VER >= 308 +llvm::BasicBlock *ScopeStack::emitWin32LandingPad() { + + LLFunction *currentFunction = irs->func()->func; + if (!currentFunction->hasPersonalityFn()) { + const char *personality = "__CxxFrameHandler3"; + LLFunction *personalityFn = + getRuntimeFunction(Loc(), irs->module, personality); + currentFunction->setPersonalityFn(personalityFn); + } + + // save and rewrite scope + IRScope savedIRScope = irs->scope(); + + // iterating through cleanup and catches in reverse order (from outer to inner + // scope) + CleanupCursor prevCleanup = 0; + llvm::BasicBlock *prevCatch = nullptr; + + auto doCleanup = [&](CleanupCursor cleanupScope) { + if (prevCleanup < cleanupScope) { + auto bb = + llvm::BasicBlock::Create(irs->context(), "cleanup", irs->topfunc()); + irs->scope() = IRScope(bb); + runCleanupCopies(cleanupScope, prevCleanup, prevCatch, true); + prevCleanup = cleanupScope; + prevCatch = bb; + } + }; + // run cleanup code, insert catchend between different scope levels, + // patch catchpad instructions + for (std::vector::iterator it = catchScopes.begin(), + end = catchScopes.end(); + it != end; ++it) { + // Insert any cleanups in between the last catch we ran and this one. + assert(prevCleanup <= it->cleanupScope); + doCleanup(it->cleanupScope); + + llvm::CatchSwitchInst &catchswitch = + llvm::cast(*it->bodyBlock->getFirstNonPHIOrDbg()); + if (prevCatch != catchswitch.getUnwindDest()) + catchswitch.setUnwindDest(prevCatch); + prevCatch = it->bodyBlock; + } + + doCleanup(currentCleanupScope()); + irs->scope() = savedIRScope; + + assert(prevCatch && prevCatch->front().isEHPad()); + return prevCatch; +} +#endif + llvm::BasicBlock *ScopeStack::emitLandingPad() { // save and rewrite scope IRScope savedIRScope = irs->scope(); diff --git a/ir/irfunction.h b/ir/irfunction.h index 32adf45353e..6b089955dc2 100644 --- a/ir/irfunction.h +++ b/ir/irfunction.h @@ -98,6 +98,9 @@ struct CleanupExitTarget { /// stores to the branch selector variable when converting from one to two /// targets. std::vector sourceBlocks; + + /// The basic blocks that are executed when going this route + std::vector cleanupBlocks; }; /// Represents a scope (in abstract terms, not curly braces) that requires a @@ -148,6 +151,9 @@ class CleanupScope { /// and popped again once it is left. If the corresponding landing pad has /// not been generated yet (this is done lazily), the pointer is null. std::vector landingPads; + + /// The original basic blocks that are executed for beginBlock to endBlock + std::vector cleanupBlocks; }; /// Stores information to be able to branch to a catch clause if it matches. @@ -210,6 +216,11 @@ class ScopeStack { /// reached. void runAllCleanups(llvm::BasicBlock *continueWith); +#if LDC_LLVM_VER >= 308 + void runCleanupCopies(CleanupCursor sourceScope, CleanupCursor targetScope, + llvm::BasicBlock* continueWith, bool withCleanupRet); +#endif + /// Pops all the cleanups between the current scope and the target cursor. /// /// This does not insert any cleanup calls, use #runCleanups() beforehand. @@ -235,6 +246,23 @@ class ScopeStack { /// Unregisters the last registered catch block. void popCatch(); + size_t currentCatchScope() { return catchScopes.size(); } + + void pushFunclet(llvm::Value *funclet) { + funclets.push_back(funclet); + } + + void popFunclet() { + funclets.pop_back(); + } + + llvm::Value *getFunclet() { + if (funclets.empty()) + return nullptr; + else + return funclets.back(); + } + /// Registers a loop statement to be used as a target for break/continue /// statements in the current scope. void pushLoopTarget(Statement *loopStatement, @@ -295,6 +323,9 @@ class ScopeStack { /// the way there. void breakToClosest() { jumpToClosest(breakTargets); } + /// get exisiting or emit new landing pad + llvm::BasicBlock *getLandingPad(); + private: /// Internal version that allows specifying the scope at which to start /// emitting the cleanups. @@ -308,6 +339,10 @@ class ScopeStack { /// Emits a landing pad to honor all the active cleanups and catches. llvm::BasicBlock *emitLandingPad(); +#if LDC_LLVM_VER >= 308 + llvm::BasicBlock *emitWin32LandingPad(); +#endif + /// Unified implementation for labeled break/continue. void jumpToStatement(std::vector &targets, Statement *loopOrSwitchStatement); @@ -347,6 +382,9 @@ class ScopeStack { /// (null if not yet emitted, one element is pushed to/popped from the back /// on entering/leaving a catch block). std::vector topLevelLandingPads; + + /// stack of currently built catch clauses + std::vector funclets; }; template @@ -360,31 +398,35 @@ llvm::CallSite ScopeStack::callOrInvoke(llvm::Value *callee, const T &args, const bool doesNotThrow = calleeFn && (calleeFn->isIntrinsic() || calleeFn->doesNotThrow()); +#if LDC_LLVM_VER >= 308 + // calls inside a funclet must be annotated with its value + llvm::SmallVector BundleList; + if (auto funclet = getFunclet()) + BundleList.push_back(llvm::OperandBundleDef("funclet", funclet)); +#endif + if (doesNotThrow || (cleanupScopes.empty() && catchScopes.empty())) { - llvm::CallInst *call = irs->ir->CreateCall(callee, args, name); + llvm::CallInst *call = irs->ir->CreateCall(callee, args, +#if LDC_LLVM_VER >= 308 + BundleList, +#endif + name); if (calleeFn) { call->setAttributes(calleeFn->getAttributes()); } return call; } - if (currentLandingPads().empty()) { - // Have not encountered any catches (for which we would push a scope) or - // calls to throwing functions (where we would have already executed - // this if) in this cleanup scope yet. - currentLandingPads().push_back(nullptr); - } - - llvm::BasicBlock *&landingPad = currentLandingPads().back(); - if (!landingPad) { - landingPad = emitLandingPad(); - } + llvm::BasicBlock* landingPad = getLandingPad(); llvm::BasicBlock *postinvoke = llvm::BasicBlock::Create( irs->context(), "postinvoke", irs->topfunc(), landingPad); llvm::InvokeInst *invoke = - irs->ir->CreateInvoke(callee, postinvoke, landingPad, args, name); - + irs->ir->CreateInvoke(callee, postinvoke, landingPad, args, +#if LDC_LLVM_VER >= 308 + BundleList, +#endif + name); if (calleeFn) { invoke->setAttributes(calleeFn->getAttributes()); } @@ -474,5 +516,6 @@ struct IrFunction { IrFunction *getIrFunc(FuncDeclaration *decl, bool create = false); bool isIrFuncCreated(FuncDeclaration *decl); +bool useMSVCEH(); #endif From 00d21caaddede9b9d716ab633b8c353c309be933 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Tue, 29 Dec 2015 10:12:53 +0100 Subject: [PATCH 03/19] fix MSVC-EH again, disable inlining and tail call optimization for functions with EH --- gen/attributes.cpp | 5 + gen/attributes.h | 1 + gen/ms-cxx-helper.cpp | 10 +- gen/ms-cxx-helper.h | 2 + gen/statements.cpp | 18 ++-- ir/irfunction.cpp | 216 +++++++++++++++++++++--------------------- ir/irfunction.h | 29 ++++-- 7 files changed, 150 insertions(+), 131 deletions(-) diff --git a/gen/attributes.cpp b/gen/attributes.cpp index ef2688c96da..ebd81b1af38 100644 --- a/gen/attributes.cpp +++ b/gen/attributes.cpp @@ -29,6 +29,11 @@ AttrBuilder &AttrBuilder::add(LLAttribute attribute) { return *this; } +AttrBuilder &AttrBuilder::add(llvm::StringRef A, llvm::StringRef V) { + builder.addAttribute(A, V); + return *this; +} + AttrBuilder &AttrBuilder::remove(LLAttribute attribute) { // never remove 'None' explicitly if (attribute) { diff --git a/gen/attributes.h b/gen/attributes.h index 8c6164dde68..86dddd8c250 100644 --- a/gen/attributes.h +++ b/gen/attributes.h @@ -27,6 +27,7 @@ class AttrBuilder { AttrBuilder &add(LLAttribute attribute); AttrBuilder &remove(LLAttribute attribute); AttrBuilder &merge(const AttrBuilder &other); + AttrBuilder &add(llvm::StringRef A, llvm::StringRef V = llvm::StringRef()); AttrBuilder &addAlignment(unsigned alignment); AttrBuilder &addByVal(unsigned alignment); diff --git a/gen/ms-cxx-helper.cpp b/gen/ms-cxx-helper.cpp index ed30327bed3..384dfd59c98 100644 --- a/gen/ms-cxx-helper.cpp +++ b/gen/ms-cxx-helper.cpp @@ -104,12 +104,12 @@ void cloneBlocks(const std::vector &srcblocks, llvm::BasicBlock *bb = srcblocks[b]; llvm::Function* F = bb->getParent(); - auto nbb = llvm::BasicBlock::Create(bb->getContext()); + auto nbb = llvm::BasicBlock::Create(bb->getContext(), bb->getName()); // Loop over all instructions, and copy them over. for (auto II = bb->begin(), IE = bb->end(); II != IE; ++II) { llvm::Instruction *Inst = &*II; llvm::Instruction *newInst = nullptr; - if (funclet && !llvm::isa(Inst)) { + if (funclet && !llvm::isa(Inst)) { // IntrinsicInst? if (auto IInst = llvm::dyn_cast (Inst)) { newInst = llvm::InvokeInst::Create( IInst, llvm::OperandBundleDef("funclet", funclet)); @@ -135,6 +135,12 @@ void cloneBlocks(const std::vector &srcblocks, remapBlocks(blocks, VMap, unwindTo, funclet); } +bool isCatchSwitchBlock(llvm::BasicBlock* bb) { + if (bb->empty()) + return false; + return llvm::dyn_cast(&bb->front()); +} + // copy from clang/.../MicrosoftCXXABI.cpp // 5 routines for constructing the llvm types for MS RTTI structs. diff --git a/gen/ms-cxx-helper.h b/gen/ms-cxx-helper.h index 6ee70516f6b..28633df435c 100644 --- a/gen/ms-cxx-helper.h +++ b/gen/ms-cxx-helper.h @@ -28,4 +28,6 @@ void cloneBlocks(const std::vector &srcblocks, llvm::BasicBlock *continueWith, llvm::BasicBlock *unwindTo, llvm::Value *funclet); +bool isCatchSwitchBlock(llvm::BasicBlock* bb); + #endif // LDC_GEN_MS_CXX_HELPER_H diff --git a/gen/statements.cpp b/gen/statements.cpp index bfd073e40c0..981ee653722 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -711,9 +711,8 @@ class ToIRVisitor : public Visitor { ////////////////////////////////////////////////////////////////////////// #if LDC_LLVM_VER >= 308 - void emitBeginCatchMSVC(Catch *ctch, llvm::BasicBlock *catchbb, - llvm::BasicBlock *endbb, - llvm::CatchSwitchInst *catchSwitchInst) { + void emitBeginCatchMSVCEH(Catch *ctch, llvm::BasicBlock *endbb, + llvm::CatchSwitchInst *catchSwitchInst) { VarDeclaration *var = ctch->var; // The MSVC/x86 build uses C++ exception handling // This needs a series of catch pads to match the exception @@ -759,15 +758,12 @@ class ToIRVisitor : public Visitor { } // "catchpad within %switch [TypeDescriptor, 0, &caughtObject]" must be - // first - // instruction + // first instruction int flags = var ? 0 : 64; // just mimicking clang here LLValue *args[] = {typeDesc, DtoConstUint(flags), exnObj}; - auto catchpad = llvm::CatchPadInst::Create( - catchSwitchInst, llvm::ArrayRef(args), "", catchbb); - catchSwitchInst->addHandler(catchbb); - - irs->scope() = IRScope(catchbb); + auto catchpad = irs->ir->CreateCatchPad( + catchSwitchInst, llvm::ArrayRef(args), ""); + catchSwitchInst->addHandler(irs->scopebb()); if (cpyObj) { // assign the caught exception to the location in the closure @@ -841,7 +837,7 @@ class ToIRVisitor : public Visitor { CleanupCursor currentScope = scopes->currentCleanupScope(); - emitBeginCatchMSVC(*it, catchBB, endbb, catchSwitchInst); + emitBeginCatchMSVCEH(*it, endbb, catchSwitchInst); scopes->pushFunclet(&catchBB->front()); // Emit handler, if there is one. The handler is zero, for instance, diff --git a/ir/irfunction.cpp b/ir/irfunction.cpp index 14d5ee16c44..6bcc2d040ac 100644 --- a/ir/irfunction.cpp +++ b/ir/irfunction.cpp @@ -33,8 +33,12 @@ CatchScope::CatchScope(llvm::Constant *classInfoPtr, cleanupScope(cleanupScope) {} bool useMSVCEH() { +#if LDC_LLVM_VER >= 308 return global.params.targetTriple.isWindowsMSVCEnvironment() && !global.params.targetTriple.isArch64Bit(); +#else + return false; +#endif } namespace { @@ -52,6 +56,8 @@ llvm::BasicBlock *executeCleanupCopying(IRState *irs, CleanupScope &scope, llvm::BasicBlock *continueWith, llvm::BasicBlock *unwindTo, llvm::Value* funclet) { + if (isCatchSwitchBlock(scope.beginBlock)) + return continueWith; if (scope.cleanupBlocks.empty()) { // figure out the list of blocks used by this cleanup step findSuccessors(scope.cleanupBlocks, scope.beginBlock, scope.endBlock); @@ -93,6 +99,7 @@ llvm::BasicBlock *executeCleanupCopying(IRState *irs, CleanupScope &scope, } return scope.exitTargets.back().cleanupBlocks.front(); } + #endif // LDC_LLVM_VER >= 308 void executeCleanup(IRState *irs, CleanupScope &scope, @@ -177,6 +184,7 @@ void executeCleanup(IRState *irs, CleanupScope &scope, scope.exitTargets.push_back(CleanupExitTarget(continueWith)); scope.exitTargets.back().sourceBlocks.push_back(sourceBlock); } + } ScopeStack::~ScopeStack() { @@ -201,7 +209,7 @@ void ScopeStack::runCleanups(CleanupCursor sourceScope, llvm::BasicBlock *continueWith) { #if LDC_LLVM_VER >= 308 if (useMSVCEH()) { - runCleanupCopies(sourceScope, targetScope, continueWith, false); + runCleanupCopies(sourceScope, targetScope, continueWith); return; } #endif @@ -228,70 +236,63 @@ void ScopeStack::runCleanups(CleanupCursor sourceScope, #if LDC_LLVM_VER >= 308 void ScopeStack::runCleanupCopies(CleanupCursor sourceScope, CleanupCursor targetScope, - llvm::BasicBlock *continueWith, - bool withCleanupRet) { + llvm::BasicBlock *continueWith) { assert(targetScope <= sourceScope); - if (withCleanupRet) { - llvm::BasicBlock *target = continueWith; - for (CleanupCursor i = targetScope; i < sourceScope; ++i) { - // each cleanup block is bracketed by a pair of cleanuppad/cleanupret - // instructions, unwinding should also just continue at the next - // cleanup block - // cleanuppad: - // %0 = cleanuppad[] - // invoke _dtor to %cleanupret unwind %continueWith - // - // cleanupret: - // cleanupret %0 unwind %continueWith - // - // continueWith: - llvm::BasicBlock *cleanupbb = - i == sourceScope - 1 - ? irs->scopebb() - : llvm::BasicBlock::Create(irs->context(), "cleanuppad", - irs->topfunc()); - auto funclet = getFunclet(); - auto cleanuppad = llvm::CleanupPadInst::Create( - funclet ? funclet : llvm::ConstantTokenNone::get(irs->context()), {}, - "", cleanupbb); - - llvm::BasicBlock *cleanupret = llvm::BasicBlock::Create( - irs->context(), "cleanupret", irs->topfunc()); - - // when hitting a catch return instruction during cleanup, - // unwind to the corresponding catchswitch block instead - auto catchret = cleanupScopes[i].beginBlock->empty() - ? nullptr - : llvm::dyn_cast( - &cleanupScopes[i].beginBlock->front()); - if (catchret) { - llvm::BasicBlock* endcatch = nullptr; - auto catchpad = catchret->getCatchPad(); - auto catchswitch = catchpad->getCatchSwitch(); - - llvm::CleanupReturnInst::Create(cleanuppad, catchswitch->getUnwindDest(), - cleanupret); - continueWith = cleanupret; - - } else { - llvm::CleanupReturnInst::Create(cleanuppad, continueWith, cleanupret); - continueWith = executeCleanupCopying(irs, cleanupScopes[i], cleanupbb, - cleanupret, continueWith, cleanuppad); - } - llvm::BranchInst::Create(continueWith, cleanupbb); - continueWith = cleanupbb; - } - } else { - // work through the blocks in reverse execution order, so we - // can merge cleanups that end up at the same continuation target - for (CleanupCursor i = targetScope; i < sourceScope; ++i) - continueWith = executeCleanupCopying(irs, cleanupScopes[i], irs->scopebb(), - continueWith, nullptr, nullptr); + // work through the blocks in reverse execution order, so we + // can merge cleanups that end up at the same continuation target + for (CleanupCursor i = targetScope; i < sourceScope; ++i) + continueWith = executeCleanupCopying(irs, cleanupScopes[i], irs->scopebb(), + continueWith, nullptr, nullptr); - // Insert the unconditional branch to the first cleanup block. - irs->ir->CreateBr(continueWith); + // Insert the unconditional branch to the first cleanup block. + irs->ir->CreateBr(continueWith); +} + +llvm::BasicBlock *ScopeStack::runCleanupPad(CleanupCursor scope, + llvm::BasicBlock *unwindTo) { + if (isCatchSwitchBlock(cleanupScopes[scope].beginBlock)) + return cleanupScopes[scope].beginBlock; + + // when hitting a catch return instruction during cleanup, + // just unwind to the corresponding catchswitch block instead + if (!cleanupScopes[scope].beginBlock->empty()) { + if (auto catchret = llvm::dyn_cast( + &cleanupScopes[scope].beginBlock->front())) { + llvm::BasicBlock* endcatch = nullptr; + auto catchpad = catchret->getCatchPad(); + auto catchswitch = catchpad->getCatchSwitch(); + return catchswitch->getUnwindDest(); + } } + + // 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[] + // br label %copy + // + // copy: + // invoke _dtor to %cleanupret unwind %unwindTo [ "funclet"(token %0) ] + // + // cleanupret: + // cleanupret %0 unwind %unwindTo + // + llvm::BasicBlock *cleanupbb = + llvm::BasicBlock::Create(irs->context(), "cleanuppad", irs->topfunc()); + auto cleanuppad = + llvm::CleanupPadInst::Create(getFuncletToken(), {}, "", cleanupbb); + + llvm::BasicBlock *cleanupret = + llvm::BasicBlock::Create(irs->context(), "cleanupret", irs->topfunc()); + + llvm::CleanupReturnInst::Create(cleanuppad, unwindTo, cleanupret); + auto copybb = executeCleanupCopying(irs, cleanupScopes[scope], cleanupbb, + cleanupret, unwindTo, cleanuppad); + llvm::BranchInst::Create(copybb, cleanupbb); + return cleanupbb; } #endif @@ -344,13 +345,27 @@ void ScopeStack::popCleanups(CleanupCursor targetScope) { void ScopeStack::pushCatch(llvm::Constant *classInfoPtr, llvm::BasicBlock *bodyBlock) { - catchScopes.emplace_back(classInfoPtr, bodyBlock, currentCleanupScope()); - currentLandingPads().push_back(nullptr); + if (useMSVCEH()) { +#if LDC_LLVM_VER >= 308 + assert(isCatchSwitchBlock(bodyBlock)); + pushCleanup(bodyBlock, bodyBlock); +#endif + } else { + catchScopes.emplace_back(classInfoPtr, bodyBlock, currentCleanupScope()); + currentLandingPads().push_back(nullptr); + } } void ScopeStack::popCatch() { - catchScopes.pop_back(); - currentLandingPads().pop_back(); + if (useMSVCEH()) { +#if LDC_LLVM_VER >= 308 + assert(isCatchSwitchBlock(cleanupScopes.back().beginBlock)); + popCleanups(currentCleanupScope() - 1); +#endif + } else { + catchScopes.pop_back(); + currentLandingPads().pop_back(); + } } void ScopeStack::pushLoopTarget(Statement *loopStatement, @@ -436,19 +451,26 @@ std::vector &ScopeStack::currentLandingPads() { : cleanupScopes.back().landingPads; } -llvm::BasicBlock *ScopeStack::getLandingPad() { - if (currentLandingPads().empty()) { +llvm::BasicBlock *&ScopeStack::getLandingPadRef(CleanupCursor scope) { + auto &pads = cleanupScopes.empty() ? topLevelLandingPads + : cleanupScopes[scope].landingPads; + if (pads.empty()) { // Have not encountered any catches (for which we would push a scope) or // calls to throwing functions (where we would have already executed // this if) in this cleanup scope yet. - currentLandingPads().push_back(nullptr); + pads.push_back(nullptr); } - llvm::BasicBlock *&landingPad = currentLandingPads().back(); + return pads.back(); +} + +llvm::BasicBlock *ScopeStack::getLandingPad() { + llvm::BasicBlock *&landingPad = getLandingPadRef(currentCleanupScope() - 1); if (!landingPad) { #if LDC_LLVM_VER >= 308 - if (useMSVCEH()) - landingPad = emitWin32LandingPad(); - else + if (useMSVCEH()) { + assert(currentCleanupScope() > 0); + landingPad = emitLandingPadMSVCEH(currentCleanupScope() - 1); + } else #endif landingPad = emitLandingPad(); } @@ -477,7 +499,7 @@ llvm::LandingPadInst *createLandingPadInst(IRState *irs) { } #if LDC_LLVM_VER >= 308 -llvm::BasicBlock *ScopeStack::emitWin32LandingPad() { +llvm::BasicBlock *ScopeStack::emitLandingPadMSVCEH(CleanupCursor scope) { LLFunction *currentFunction = irs->func()->func; if (!currentFunction->hasPersonalityFn()) { @@ -487,45 +509,21 @@ llvm::BasicBlock *ScopeStack::emitWin32LandingPad() { currentFunction->setPersonalityFn(personalityFn); } - // save and rewrite scope - IRScope savedIRScope = irs->scope(); - - // iterating through cleanup and catches in reverse order (from outer to inner - // scope) - CleanupCursor prevCleanup = 0; - llvm::BasicBlock *prevCatch = nullptr; - - auto doCleanup = [&](CleanupCursor cleanupScope) { - if (prevCleanup < cleanupScope) { - auto bb = - llvm::BasicBlock::Create(irs->context(), "cleanup", irs->topfunc()); - irs->scope() = IRScope(bb); - runCleanupCopies(cleanupScope, prevCleanup, prevCatch, true); - prevCleanup = cleanupScope; - prevCatch = bb; - } - }; - // run cleanup code, insert catchend between different scope levels, - // patch catchpad instructions - for (std::vector::iterator it = catchScopes.begin(), - end = catchScopes.end(); - it != end; ++it) { - // Insert any cleanups in between the last catch we ran and this one. - assert(prevCleanup <= it->cleanupScope); - doCleanup(it->cleanupScope); - - llvm::CatchSwitchInst &catchswitch = - llvm::cast(*it->bodyBlock->getFirstNonPHIOrDbg()); - if (prevCatch != catchswitch.getUnwindDest()) - catchswitch.setUnwindDest(prevCatch); - prevCatch = it->bodyBlock; + // WinEHPrepare pass fails after optimizations, so disable these for now + if (!currentFunction->getFnAttribute("disable-tail-calls") + .isStringAttribute()) { + currentFunction->addFnAttr("disable-tail-calls", "true"); + currentFunction->addFnAttr(llvm::Attribute::NoInline); } - doCleanup(currentCleanupScope()); - irs->scope() = savedIRScope; + if (scope == 0) + return runCleanupPad(scope, nullptr); + + llvm::BasicBlock *&pad = getLandingPadRef(scope - 1); + if (!pad) + pad = emitLandingPadMSVCEH(scope - 1); - assert(prevCatch && prevCatch->front().isEHPad()); - return prevCatch; + return runCleanupPad(scope, pad); } #endif diff --git a/ir/irfunction.h b/ir/irfunction.h index 6b089955dc2..c59061b7ae7 100644 --- a/ir/irfunction.h +++ b/ir/irfunction.h @@ -99,7 +99,7 @@ struct CleanupExitTarget { /// targets. std::vector sourceBlocks; - /// The basic blocks that are executed when going this route + /// MSVC: The basic blocks that are executed when going this route std::vector cleanupBlocks; }; @@ -152,7 +152,8 @@ class CleanupScope { /// not been generated yet (this is done lazily), the pointer is null. std::vector landingPads; - /// The original basic blocks that are executed for beginBlock to endBlock + /// MSVC: The original basic blocks that are executed for beginBlock to + /// endBlock std::vector cleanupBlocks; }; @@ -218,7 +219,9 @@ class ScopeStack { #if LDC_LLVM_VER >= 308 void runCleanupCopies(CleanupCursor sourceScope, CleanupCursor targetScope, - llvm::BasicBlock* continueWith, bool withCleanupRet); + llvm::BasicBlock *continueWith); + llvm::BasicBlock *runCleanupPad(CleanupCursor scope, + llvm::BasicBlock *unwindTo); #endif /// Pops all the cleanups between the current scope and the target cursor. @@ -248,6 +251,8 @@ class ScopeStack { size_t currentCatchScope() { return catchScopes.size(); } + /// MSVC: catch and cleanup code is emitted as funclets and need + /// to be referenced from inner pads and calls void pushFunclet(llvm::Value *funclet) { funclets.push_back(funclet); } @@ -257,10 +262,11 @@ class ScopeStack { } llvm::Value *getFunclet() { - if (funclets.empty()) - return nullptr; - else - return funclets.back(); + return funclets.empty() ? nullptr : funclets.back(); + } + llvm::Value *getFuncletToken() { + return funclets.empty() ? llvm::ConstantTokenNone::get(irs->context()) + : funclets.back(); } /// Registers a loop statement to be used as a target for break/continue @@ -336,11 +342,13 @@ class ScopeStack { std::vector ¤tLandingPads(); + llvm::BasicBlock * &getLandingPadRef(CleanupCursor scope); + /// Emits a landing pad to honor all the active cleanups and catches. llvm::BasicBlock *emitLandingPad(); #if LDC_LLVM_VER >= 308 - llvm::BasicBlock *emitWin32LandingPad(); + llvm::BasicBlock *emitLandingPadMSVCEH(CleanupCursor scope); #endif /// Unified implementation for labeled break/continue. @@ -383,7 +391,7 @@ class ScopeStack { /// on entering/leaving a catch block). std::vector topLevelLandingPads; - /// stack of currently built catch clauses + /// MSVC: stack of currently built catch/cleanup funclets std::vector funclets; }; @@ -406,6 +414,7 @@ llvm::CallSite ScopeStack::callOrInvoke(llvm::Value *callee, const T &args, #endif if (doesNotThrow || (cleanupScopes.empty() && catchScopes.empty())) { +L_call: llvm::CallInst *call = irs->ir->CreateCall(callee, args, #if LDC_LLVM_VER >= 308 BundleList, @@ -418,6 +427,8 @@ llvm::CallSite ScopeStack::callOrInvoke(llvm::Value *callee, const T &args, } llvm::BasicBlock* landingPad = getLandingPad(); + if (!landingPad) + goto L_call; llvm::BasicBlock *postinvoke = llvm::BasicBlock::Create( irs->context(), "postinvoke", irs->topfunc(), landingPad); From 9b6c80d53becb1ea82a2931288ffa9ff422e67d8 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Wed, 30 Dec 2015 14:33:40 +0100 Subject: [PATCH 04/19] inside a cleanup block, convert "unreachable" to a branch to "cleanupret" --- gen/ms-cxx-helper.cpp | 24 ++++++++---------------- ir/irfunction.cpp | 5 +++-- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/gen/ms-cxx-helper.cpp b/gen/ms-cxx-helper.cpp index 384dfd59c98..9d26c8da8cf 100644 --- a/gen/ms-cxx-helper.cpp +++ b/gen/ms-cxx-helper.cpp @@ -30,18 +30,6 @@ llvm::BasicBlock *getUnwindDest(llvm::Instruction *I) { return nullptr; } -void mapFunclet(llvm::Instruction *I, llvm::ValueToValueMapTy &VMap, llvm::Value *funclet) { - if (auto II = llvm::dyn_cast (I)) { - auto bundle = II->getOperandBundle(llvm::LLVMContext::OB_funclet); - if (bundle) - VMap[bundle->Inputs[0].get()] = funclet; - } else if (auto CI = llvm::dyn_cast (I)) { - auto bundle = CI->getOperandBundle(llvm::LLVMContext::OB_funclet); - if (bundle) - VMap[bundle->Inputs[0].get()] = funclet; - } -} - // return all basic blocks that are reachable from bb, but don't pass through // ebb and don't follow unwinding target void findSuccessors(std::vector &blocks, @@ -73,8 +61,6 @@ void remapBlocks(std::vector &blocks, llvm::Value *funclet) { for (llvm::BasicBlock *bb : blocks) for (llvm::BasicBlock::iterator I = bb->begin(); I != bb->end(); ++I) { - //if (funclet) - // mapFunclet(&*I, VMap, funclet); llvm::RemapInstruction(&*I, VMap, llvm::RF_IgnoreMissingEntries | llvm::RF_NoModuleLevelChanges); } @@ -87,8 +73,11 @@ void remapBlocksValue(std::vector &blocks, remapBlocks(blocks, VMap, nullptr, nullptr); } -// make a copy of all srcblocks, mapping values to clones and redirect srcTarget -// to continueWith +// make a copy of all blocks and instructions in srcblocks +// - map values to clones +// - redirect srcTarget to continueWith +// - set "funclet" attribute inside catch/cleanup pads +// - inside funclets, replace "unreachable" with "branch cleanupret" void cloneBlocks(const std::vector &srcblocks, std::vector &blocks, llvm::BasicBlock *continueWith, llvm::BasicBlock *unwindTo, @@ -118,6 +107,9 @@ void cloneBlocks(const std::vector &srcblocks, CInst, llvm::OperandBundleDef("funclet", funclet)); } } + if (funclet && llvm::isa(Inst)) { + newInst = llvm::BranchInst::Create(continueWith); // cleanupret + } if (!newInst) newInst = Inst->clone(); diff --git a/ir/irfunction.cpp b/ir/irfunction.cpp index 6bcc2d040ac..c130a19e4c4 100644 --- a/ir/irfunction.cpp +++ b/ir/irfunction.cpp @@ -251,6 +251,7 @@ void ScopeStack::runCleanupCopies(CleanupCursor sourceScope, llvm::BasicBlock *ScopeStack::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; @@ -287,10 +288,10 @@ llvm::BasicBlock *ScopeStack::runCleanupPad(CleanupCursor scope, llvm::BasicBlock *cleanupret = llvm::BasicBlock::Create(irs->context(), "cleanupret", irs->topfunc()); - llvm::CleanupReturnInst::Create(cleanuppad, unwindTo, cleanupret); + auto copybb = executeCleanupCopying(irs, cleanupScopes[scope], cleanupbb, - cleanupret, unwindTo, cleanuppad); + cleanupret, unwindTo, cleanuppad); llvm::BranchInst::Create(copybb, cleanupbb); return cleanupbb; } From 3858976cff72b8e2e76acf39b17c3da55862d0c0 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Wed, 30 Dec 2015 17:34:32 +0100 Subject: [PATCH 05/19] fix build for LLVM < 3.8 --- ir/irfunction.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ir/irfunction.h b/ir/irfunction.h index c59061b7ae7..2ec142621f8 100644 --- a/ir/irfunction.h +++ b/ir/irfunction.h @@ -251,6 +251,7 @@ class ScopeStack { size_t currentCatchScope() { return catchScopes.size(); } +#if LDC_LLVM_VER >= 308 /// MSVC: catch and cleanup code is emitted as funclets and need /// to be referenced from inner pads and calls void pushFunclet(llvm::Value *funclet) { @@ -268,6 +269,7 @@ class ScopeStack { return funclets.empty() ? llvm::ConstantTokenNone::get(irs->context()) : funclets.back(); } +#endif /// Registers a loop statement to be used as a target for break/continue /// statements in the current scope. From d2e9a807daea32329804b099a4e9085ac8299f32 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Thu, 31 Dec 2015 10:47:52 +0100 Subject: [PATCH 06/19] avoid replacing library functions in funclet, it doesn't copy the "funclet" token --- gen/ms-cxx-helper.cpp | 14 +++++++++++--- gen/tocall.cpp | 4 ++++ ir/irfunction.h | 12 ++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/gen/ms-cxx-helper.cpp b/gen/ms-cxx-helper.cpp index 9d26c8da8cf..7b11fe574f7 100644 --- a/gen/ms-cxx-helper.cpp +++ b/gen/ms-cxx-helper.cpp @@ -100,11 +100,19 @@ void cloneBlocks(const std::vector &srcblocks, llvm::Instruction *newInst = nullptr; if (funclet && !llvm::isa(Inst)) { // IntrinsicInst? if (auto IInst = llvm::dyn_cast (Inst)) { - newInst = llvm::InvokeInst::Create( + auto invoke = llvm::InvokeInst::Create( IInst, llvm::OperandBundleDef("funclet", funclet)); + // do not replace functions in optimizer, they lose the "funclet" token + invoke->addAttribute(llvm::AttributeSet::FunctionIndex, + llvm::Attribute::NoBuiltin); + newInst = invoke; } else if (auto CInst = llvm::dyn_cast (Inst)) { - newInst = llvm::CallInst::Create( - CInst, llvm::OperandBundleDef("funclet", funclet)); + auto call = llvm::CallInst::Create( + CInst, llvm::OperandBundleDef("funclet", funclet)); + // do not replace functions in optimizer, they lose the "funclet" token + call->addAttribute(llvm::AttributeSet::FunctionIndex, + llvm::Attribute::NoBuiltin); + newInst = call; } } if (funclet && llvm::isa(Inst)) { diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 49acfe3e5ba..a4ed2171f5d 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -987,6 +987,10 @@ DValue *DtoCallFunction(Loc &loc, Type *resulttype, DValue *fnval, } else { call.setCallingConv(callconv); } + // merge in function attributes set in callOrInvoke + attrlist = attrlist.addAttributes( + gIR->context(), llvm::AttributeSet::FunctionIndex, call.getAttributes()); + call.setAttributes(attrlist); // Special case for struct constructor calls: For temporaries, using the diff --git a/ir/irfunction.h b/ir/irfunction.h index 2ec142621f8..b30222a3be2 100644 --- a/ir/irfunction.h +++ b/ir/irfunction.h @@ -425,6 +425,12 @@ llvm::CallSite ScopeStack::callOrInvoke(llvm::Value *callee, const T &args, if (calleeFn) { call->setAttributes(calleeFn->getAttributes()); } +#if LDC_LLVM_VER >= 308 + if (!BundleList.empty()) + // do not replace functions in optimizer, they lose the "funclet" token + call->addAttribute(llvm::AttributeSet::FunctionIndex, + llvm::Attribute::NoBuiltin); +#endif return call; } @@ -443,6 +449,12 @@ llvm::CallSite ScopeStack::callOrInvoke(llvm::Value *callee, const T &args, if (calleeFn) { invoke->setAttributes(calleeFn->getAttributes()); } +#if LDC_LLVM_VER >= 308 + if (!BundleList.empty()) + // do not replace functions in optimizer, they lose the "funclet" token + invoke->addAttribute(llvm::AttributeSet::FunctionIndex, + llvm::Attribute::NoBuiltin); +#endif irs->scope() = IRScope(postinvoke); return invoke; From b939952673b2400810f175c8bac893cb6b4074b2 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Wed, 6 Jan 2016 08:14:10 +0100 Subject: [PATCH 07/19] remove setting NoBuiltin attribute, fixed in LLVM --- gen/ms-cxx-helper.cpp | 6 ------ ir/irfunction.h | 12 ------------ 2 files changed, 18 deletions(-) diff --git a/gen/ms-cxx-helper.cpp b/gen/ms-cxx-helper.cpp index 7b11fe574f7..988d8699a4c 100644 --- a/gen/ms-cxx-helper.cpp +++ b/gen/ms-cxx-helper.cpp @@ -102,16 +102,10 @@ void cloneBlocks(const std::vector &srcblocks, if (auto IInst = llvm::dyn_cast (Inst)) { auto invoke = llvm::InvokeInst::Create( IInst, llvm::OperandBundleDef("funclet", funclet)); - // do not replace functions in optimizer, they lose the "funclet" token - invoke->addAttribute(llvm::AttributeSet::FunctionIndex, - llvm::Attribute::NoBuiltin); newInst = invoke; } else if (auto CInst = llvm::dyn_cast (Inst)) { auto call = llvm::CallInst::Create( CInst, llvm::OperandBundleDef("funclet", funclet)); - // do not replace functions in optimizer, they lose the "funclet" token - call->addAttribute(llvm::AttributeSet::FunctionIndex, - llvm::Attribute::NoBuiltin); newInst = call; } } diff --git a/ir/irfunction.h b/ir/irfunction.h index b30222a3be2..2ec142621f8 100644 --- a/ir/irfunction.h +++ b/ir/irfunction.h @@ -425,12 +425,6 @@ llvm::CallSite ScopeStack::callOrInvoke(llvm::Value *callee, const T &args, if (calleeFn) { call->setAttributes(calleeFn->getAttributes()); } -#if LDC_LLVM_VER >= 308 - if (!BundleList.empty()) - // do not replace functions in optimizer, they lose the "funclet" token - call->addAttribute(llvm::AttributeSet::FunctionIndex, - llvm::Attribute::NoBuiltin); -#endif return call; } @@ -449,12 +443,6 @@ llvm::CallSite ScopeStack::callOrInvoke(llvm::Value *callee, const T &args, if (calleeFn) { invoke->setAttributes(calleeFn->getAttributes()); } -#if LDC_LLVM_VER >= 308 - if (!BundleList.empty()) - // do not replace functions in optimizer, they lose the "funclet" token - invoke->addAttribute(llvm::AttributeSet::FunctionIndex, - llvm::Attribute::NoBuiltin); -#endif irs->scope() = IRScope(postinvoke); return invoke; From a8a45500fc3cab3507b52a54e6ccc02179eb849b Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Fri, 8 Jan 2016 18:25:03 +0100 Subject: [PATCH 08/19] use catch-all and rethrow instead of cleanup pads, they don't work completely the same as C++ --- ir/irfunction.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/ir/irfunction.cpp b/ir/irfunction.cpp index c130a19e4c4..c74d71dcb12 100644 --- a/ir/irfunction.cpp +++ b/ir/irfunction.cpp @@ -267,6 +267,49 @@ llvm::BasicBlock *ScopeStack::runCleanupPad(CleanupCursor scope, } } +#if 1 + // translate cleanup to catch-all-rethrow: + // %switch = catchswitch within %funclet [label %catch] unwind to %unwindTo + llvm::BasicBlock *cleanupbb = + llvm::BasicBlock::Create(irs->context(), "cleanuppad", irs->topfunc()); + auto catchswitch = + llvm::CatchSwitchInst::Create(getFuncletToken(), unwindTo, 1, "", cleanupbb); + + // %catch = catchpad within %switch [nullptr, 0, &caughtObject] + llvm::BasicBlock *catchpadbb = + llvm::BasicBlock::Create(irs->context(), "cleanupcatch", irs->topfunc()); + int flags = 64; // just mimicking clang here + LLValue* nulltype = llvm::Constant::getNullValue(getVoidPtrType()); + LLValue* excptObj = irs->func()->getOrCreateEhPtrSlot(); + LLValue *args[] = { nulltype, DtoConstUint(flags), excptObj }; + auto catchpad = llvm::CatchPadInst::Create( + catchswitch, llvm::ArrayRef(args), "", catchpadbb); + catchswitch->addHandler(catchpadbb); + + + llvm::BasicBlock *cleanupret = + llvm::BasicBlock::Create(irs->context(), "cleanupret", irs->topfunc()); + const auto resumeFn = + getRuntimeFunction(Loc(), irs->module, "_d_eh_resume_unwind"); + auto excptn = new llvm::LoadInst(excptObj, "excptn", cleanupret); + if (unwindTo) { + llvm::BasicBlock *unreachable = + llvm::BasicBlock::Create(irs->context(), "unreachable", irs->topfunc()); + new llvm::UnreachableInst(irs->context(), unreachable); + llvm::InvokeInst::Create(resumeFn, unreachable, unwindTo, excptn, + {llvm::OperandBundleDef("funclet", catchpad)}, "", + cleanupret); + } else { + llvm::CallInst::Create(resumeFn, excptn, + {llvm::OperandBundleDef("funclet", catchpad)}, "", + cleanupret); + new llvm::UnreachableInst(irs->context(), cleanupret); + } + + auto copybb = executeCleanupCopying(irs, cleanupScopes[scope], catchpadbb, + cleanupret, unwindTo, catchpad); + llvm::BranchInst::Create(copybb, catchpadbb); +#else // 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.: @@ -293,6 +336,7 @@ llvm::BasicBlock *ScopeStack::runCleanupPad(CleanupCursor scope, auto copybb = executeCleanupCopying(irs, cleanupScopes[scope], cleanupbb, cleanupret, unwindTo, cleanuppad); llvm::BranchInst::Create(copybb, cleanupbb); +#endif return cleanupbb; } #endif @@ -510,12 +554,14 @@ llvm::BasicBlock *ScopeStack::emitLandingPadMSVCEH(CleanupCursor scope) { currentFunction->setPersonalityFn(personalityFn); } +#if 0 // WinEHPrepare pass fails after optimizations, so disable these for now if (!currentFunction->getFnAttribute("disable-tail-calls") .isStringAttribute()) { currentFunction->addFnAttr("disable-tail-calls", "true"); currentFunction->addFnAttr(llvm::Attribute::NoInline); } +#endif if (scope == 0) return runCleanupPad(scope, nullptr); From f13f12112a4dae4cb1d7b83835dd4ea847e956a6 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Sun, 10 Jan 2016 09:37:27 +0100 Subject: [PATCH 09/19] - add a global condition to cleanup pads to avoid them being inferred as "noreturn" by the optimizer - do not inline functions into the cleanup funclets, it can break exception handling - even if no var given, retrieve the caught exception to pass it to _d_eh_enter_catch --- gen/irstate.h | 1 + gen/ms-cxx-helper.cpp | 13 +++++++++ gen/ms-cxx-helper.h | 2 ++ gen/statements.cpp | 12 ++++----- ir/irfunction.cpp | 61 +++++-------------------------------------- 5 files changed, 28 insertions(+), 61 deletions(-) diff --git a/gen/irstate.h b/gen/irstate.h index deb93866498..2a9b55fc6d1 100644 --- a/gen/irstate.h +++ b/gen/irstate.h @@ -187,6 +187,7 @@ struct IRState { // MS C++ compatible type descriptors llvm::DenseMap TypeDescriptorTypeMap; llvm::DenseMap TypeDescriptorMap; + llvm::GlobalVariable *SkipCleanupVar = nullptr; #endif }; diff --git a/gen/ms-cxx-helper.cpp b/gen/ms-cxx-helper.cpp index 988d8699a4c..9e62ec11f39 100644 --- a/gen/ms-cxx-helper.cpp +++ b/gen/ms-cxx-helper.cpp @@ -78,6 +78,7 @@ void remapBlocksValue(std::vector &blocks, // - redirect srcTarget to continueWith // - set "funclet" attribute inside catch/cleanup pads // - inside funclets, replace "unreachable" with "branch cleanupret" +// - disable inlining inside a funclet void cloneBlocks(const std::vector &srcblocks, std::vector &blocks, llvm::BasicBlock *continueWith, llvm::BasicBlock *unwindTo, @@ -102,10 +103,12 @@ void cloneBlocks(const std::vector &srcblocks, if (auto IInst = llvm::dyn_cast (Inst)) { auto invoke = llvm::InvokeInst::Create( IInst, llvm::OperandBundleDef("funclet", funclet)); + invoke->setIsNoInline(); newInst = invoke; } else if (auto CInst = llvm::dyn_cast (Inst)) { auto call = llvm::CallInst::Create( CInst, llvm::OperandBundleDef("funclet", funclet)); + call->setIsNoInline(); newInst = call; } } @@ -184,6 +187,16 @@ llvm::GlobalVariable *getTypeDescriptor(IRState &irs, ClassDeclaration *cd) { return Var; } +llvm::GlobalVariable *getSkipCleanupVar(IRState &irs) { + if (!irs.SkipCleanupVar) { + auto int1Ty = LLType::getInt1Ty(gIR->context()); + irs.SkipCleanupVar = new llvm::GlobalVariable( + gIR->module, int1Ty, false, LLGlobalVariable::ExternalLinkage, + nullptr, "_d_skipCleanup"); + } + return irs.SkipCleanupVar; +} + #if 0 // currently unused, information built at runtime ATM llvm::StructType *BaseClassDescriptorType; diff --git a/gen/ms-cxx-helper.h b/gen/ms-cxx-helper.h index 28633df435c..c701b177c71 100644 --- a/gen/ms-cxx-helper.h +++ b/gen/ms-cxx-helper.h @@ -17,6 +17,8 @@ llvm::StructType *getTypeDescriptorType(IRState &irs, llvm::StringRef TypeInfoString); llvm::GlobalVariable *getTypeDescriptor(IRState &irs, ClassDeclaration *cd); +llvm::GlobalVariable *getSkipCleanupVar(IRState &irs); + void findSuccessors(std::vector &blocks, llvm::BasicBlock *bb, llvm::BasicBlock *ebb); diff --git a/gen/statements.cpp b/gen/statements.cpp index 981ee653722..f6723d74b11 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -741,11 +741,12 @@ class ToIRVisitor : public Visitor { cpyObj = exnObj; exnObj = DtoAlloca(var->type, "exnObj"); } - irs->scope() = save; - - } else { + } else if (ctch->type) { // catch without var + exnObj = DtoAlloca(ctch->type, "exnObj"); + } else { + // catch all exnObj = llvm::Constant::getNullValue(getVoidPtrType()); } @@ -773,9 +774,8 @@ class ToIRVisitor : public Visitor { } const auto enterCatchFn = getRuntimeFunction(Loc(), irs->module, "_d_eh_enter_catch"); - auto throwableObj = - irs->ir->CreateCall(enterCatchFn, DtoBitCast(exnObj, getVoidPtrType()), - {llvm::OperandBundleDef("funclet", catchpad)}); + irs->ir->CreateCall(enterCatchFn, DtoBitCast(exnObj, getVoidPtrType()), + {llvm::OperandBundleDef("funclet", catchpad)}); // The code generator will extract the catch handler to funclets // so it needs to know the end of the code executed in the handler. diff --git a/ir/irfunction.cpp b/ir/irfunction.cpp index c74d71dcb12..c8042751223 100644 --- a/ir/irfunction.cpp +++ b/ir/irfunction.cpp @@ -267,56 +267,14 @@ llvm::BasicBlock *ScopeStack::runCleanupPad(CleanupCursor scope, } } -#if 1 - // translate cleanup to catch-all-rethrow: - // %switch = catchswitch within %funclet [label %catch] unwind to %unwindTo - llvm::BasicBlock *cleanupbb = - llvm::BasicBlock::Create(irs->context(), "cleanuppad", irs->topfunc()); - auto catchswitch = - llvm::CatchSwitchInst::Create(getFuncletToken(), unwindTo, 1, "", cleanupbb); - - // %catch = catchpad within %switch [nullptr, 0, &caughtObject] - llvm::BasicBlock *catchpadbb = - llvm::BasicBlock::Create(irs->context(), "cleanupcatch", irs->topfunc()); - int flags = 64; // just mimicking clang here - LLValue* nulltype = llvm::Constant::getNullValue(getVoidPtrType()); - LLValue* excptObj = irs->func()->getOrCreateEhPtrSlot(); - LLValue *args[] = { nulltype, DtoConstUint(flags), excptObj }; - auto catchpad = llvm::CatchPadInst::Create( - catchswitch, llvm::ArrayRef(args), "", catchpadbb); - catchswitch->addHandler(catchpadbb); - - - llvm::BasicBlock *cleanupret = - llvm::BasicBlock::Create(irs->context(), "cleanupret", irs->topfunc()); - const auto resumeFn = - getRuntimeFunction(Loc(), irs->module, "_d_eh_resume_unwind"); - auto excptn = new llvm::LoadInst(excptObj, "excptn", cleanupret); - if (unwindTo) { - llvm::BasicBlock *unreachable = - llvm::BasicBlock::Create(irs->context(), "unreachable", irs->topfunc()); - new llvm::UnreachableInst(irs->context(), unreachable); - llvm::InvokeInst::Create(resumeFn, unreachable, unwindTo, excptn, - {llvm::OperandBundleDef("funclet", catchpad)}, "", - cleanupret); - } else { - llvm::CallInst::Create(resumeFn, excptn, - {llvm::OperandBundleDef("funclet", catchpad)}, "", - cleanupret); - new llvm::UnreachableInst(irs->context(), cleanupret); - } - - auto copybb = executeCleanupCopying(irs, cleanupScopes[scope], catchpadbb, - cleanupret, unwindTo, catchpad); - llvm::BranchInst::Create(copybb, catchpadbb); -#else // 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[] - // br label %copy + // if (_d_skipCleanup) br label %cleanupret ; avoid LLVM inferring noreturn + // else br label %copy // // copy: // invoke _dtor to %cleanupret unwind %unwindTo [ "funclet"(token %0) ] @@ -335,8 +293,10 @@ llvm::BasicBlock *ScopeStack::runCleanupPad(CleanupCursor scope, auto copybb = executeCleanupCopying(irs, cleanupScopes[scope], cleanupbb, cleanupret, unwindTo, cleanuppad); - llvm::BranchInst::Create(copybb, cleanupbb); -#endif + + auto skipVar = getSkipCleanupVar(*irs); + auto skip = new llvm::LoadInst(skipVar, "skipCleanup", cleanupbb); + llvm::BranchInst::Create(cleanupret, copybb, skip, cleanupbb); return cleanupbb; } #endif @@ -554,15 +514,6 @@ llvm::BasicBlock *ScopeStack::emitLandingPadMSVCEH(CleanupCursor scope) { currentFunction->setPersonalityFn(personalityFn); } -#if 0 - // WinEHPrepare pass fails after optimizations, so disable these for now - if (!currentFunction->getFnAttribute("disable-tail-calls") - .isStringAttribute()) { - currentFunction->addFnAttr("disable-tail-calls", "true"); - currentFunction->addFnAttr(llvm::Attribute::NoInline); - } -#endif - if (scope == 0) return runCleanupPad(scope, nullptr); From 996281d44c3fd557f6e05a2409bd407e6fdf7c69 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Fri, 15 Jan 2016 09:07:59 +0100 Subject: [PATCH 10/19] replace _d_skipCleanup var with callbacks _d_enter_cleanup, _d_leave_cleanup --- gen/irstate.h | 1 - gen/ms-cxx-helper.cpp | 10 ---------- gen/ms-cxx-helper.h | 2 -- gen/runtime.cpp | 5 +++++ ir/irfunction.cpp | 27 ++++++++++++++++++++++----- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/gen/irstate.h b/gen/irstate.h index 2a9b55fc6d1..deb93866498 100644 --- a/gen/irstate.h +++ b/gen/irstate.h @@ -187,7 +187,6 @@ struct IRState { // MS C++ compatible type descriptors llvm::DenseMap TypeDescriptorTypeMap; llvm::DenseMap TypeDescriptorMap; - llvm::GlobalVariable *SkipCleanupVar = nullptr; #endif }; diff --git a/gen/ms-cxx-helper.cpp b/gen/ms-cxx-helper.cpp index 9e62ec11f39..b1a6e08e44a 100644 --- a/gen/ms-cxx-helper.cpp +++ b/gen/ms-cxx-helper.cpp @@ -187,16 +187,6 @@ llvm::GlobalVariable *getTypeDescriptor(IRState &irs, ClassDeclaration *cd) { return Var; } -llvm::GlobalVariable *getSkipCleanupVar(IRState &irs) { - if (!irs.SkipCleanupVar) { - auto int1Ty = LLType::getInt1Ty(gIR->context()); - irs.SkipCleanupVar = new llvm::GlobalVariable( - gIR->module, int1Ty, false, LLGlobalVariable::ExternalLinkage, - nullptr, "_d_skipCleanup"); - } - return irs.SkipCleanupVar; -} - #if 0 // currently unused, information built at runtime ATM llvm::StructType *BaseClassDescriptorType; diff --git a/gen/ms-cxx-helper.h b/gen/ms-cxx-helper.h index c701b177c71..28633df435c 100644 --- a/gen/ms-cxx-helper.h +++ b/gen/ms-cxx-helper.h @@ -17,8 +17,6 @@ llvm::StructType *getTypeDescriptorType(IRState &irs, llvm::StringRef TypeInfoString); llvm::GlobalVariable *getTypeDescriptor(IRState &irs, ClassDeclaration *cd); -llvm::GlobalVariable *getSkipCleanupVar(IRState &irs); - void findSuccessors(std::vector &blocks, llvm::BasicBlock *bb, llvm::BasicBlock *ebb); diff --git a/gen/runtime.cpp b/gen/runtime.cpp index bc0b0b7c37e..696ecc720bc 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -638,6 +638,11 @@ static void buildRuntimeModule() { } } + if (useMSVCEH()) { + createFwdDecl(LINKc, boolTy, {"_d_enter_cleanup"}, {voidPtrTy}); + createFwdDecl(LINKc, voidTy, {"_d_leave_cleanup"}, {voidPtrTy}); + } + // void _d_eh_resume_unwind(ptr) createFwdDecl(LINKc, voidTy, {"_d_eh_resume_unwind"}, {voidPtrTy}); diff --git a/ir/irfunction.cpp b/ir/irfunction.cpp index c8042751223..9328da8c4ac 100644 --- a/ir/irfunction.cpp +++ b/ir/irfunction.cpp @@ -273,13 +273,15 @@ llvm::BasicBlock *ScopeStack::runCleanupPad(CleanupCursor scope, // // cleanuppad: // %0 = cleanuppad within %funclet[] - // if (_d_skipCleanup) br label %cleanupret ; avoid LLVM inferring noreturn - // else br label %copy + // %frame = alloca(byte[16]) + // 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 = @@ -289,14 +291,29 @@ llvm::BasicBlock *ScopeStack::runCleanupPad(CleanupCursor scope, llvm::BasicBlock *cleanupret = llvm::BasicBlock::Create(irs->context(), "cleanupret", irs->topfunc()); + + // allocate some space on the stack where _d_enter_cleanup can place an exception frame + auto int8Ty = LLType::getInt8Ty(gIR->context()); + auto frametype = llvm::ArrayType::get(int8Ty, 16); + auto frameai = DtoRawAlloca(frametype, 0, "cleanup.frame"); + auto frame = + new llvm::BitCastInst(frameai, getPtrToType(int8Ty), "", cleanupbb); + + auto endFn = getRuntimeFunction(Loc(), irs->module, "_d_leave_cleanup"); + llvm::CallInst::Create(endFn, frame, + {llvm::OperandBundleDef("funclet", cleanuppad)}, "", + cleanupret); llvm::CleanupReturnInst::Create(cleanuppad, unwindTo, cleanupret); auto copybb = executeCleanupCopying(irs, cleanupScopes[scope], cleanupbb, cleanupret, unwindTo, cleanuppad); - auto skipVar = getSkipCleanupVar(*irs); - auto skip = new llvm::LoadInst(skipVar, "skipCleanup", cleanupbb); - llvm::BranchInst::Create(cleanupret, copybb, skip, cleanupbb); + auto beginFn = getRuntimeFunction(Loc(), irs->module, "_d_enter_cleanup"); + auto exec = llvm::CallInst::Create( + beginFn, frame, {llvm::OperandBundleDef("funclet", cleanuppad)}, "", + cleanupbb); + llvm::BranchInst::Create(copybb, cleanupret, exec, cleanupbb); + return cleanupbb; } #endif From 7c4c106e395d3ff8c8e56dd5c736d11c64ce4e3d Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Fri, 22 Jan 2016 23:31:58 +0100 Subject: [PATCH 11/19] - add classinfo argument to _d_eh_enter_catch --- gen/runtime.cpp | 20 ++++++++++++++------ gen/statements.cpp | 12 ++++++++---- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/gen/runtime.cpp b/gen/runtime.cpp index 696ecc720bc..3c9eb7c5549 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -639,16 +639,24 @@ static void buildRuntimeModule() { } if (useMSVCEH()) { + // _d_enter_cleanup(ptr frame) createFwdDecl(LINKc, boolTy, {"_d_enter_cleanup"}, {voidPtrTy}); + + // _d_leave_cleanup(ptr frame) createFwdDecl(LINKc, voidTy, {"_d_leave_cleanup"}, {voidPtrTy}); - } - // void _d_eh_resume_unwind(ptr) - createFwdDecl(LINKc, voidTy, {"_d_eh_resume_unwind"}, {voidPtrTy}); + // Object _d_eh_enter_catch(ptr exception, ClassInfo catchType) + createFwdDecl(LINKc, objectTy, {"_d_eh_enter_catch"}, + {voidPtrTy, classInfoTy}, {}, Attr_NoUnwind); + } else { - // Object _d_eh_enter_catch(ptr) - createFwdDecl(LINKc, objectTy, {"_d_eh_enter_catch"}, {voidPtrTy}, {}, - Attr_NoUnwind); + // void _d_eh_resume_unwind(ptr) + createFwdDecl(LINKc, voidTy, {"_d_eh_resume_unwind"}, {voidPtrTy}); + + // Object _d_eh_enter_catch(ptr) + createFwdDecl(LINKc, objectTy, {"_d_eh_enter_catch"}, {voidPtrTy}, {}, + Attr_NoUnwind); + } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// diff --git a/gen/statements.cpp b/gen/statements.cpp index f6723d74b11..266e597cd42 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -720,6 +720,7 @@ class ToIRVisitor : public Visitor { LLValue *exnObj = nullptr; LLValue *cpyObj = nullptr; LLValue *typeDesc = nullptr; + LLValue *clssInfo = nullptr; if (var) { // alloca storage for the variable, it always needs a place on the stack // do not initialize, this will be done by the C++ exception handler @@ -747,15 +748,17 @@ class ToIRVisitor : public Visitor { exnObj = DtoAlloca(ctch->type, "exnObj"); } else { // catch all - exnObj = llvm::Constant::getNullValue(getVoidPtrType()); + exnObj = LLConstant::getNullValue(getVoidPtrType()); } if (ctch->type) { ClassDeclaration *cd = ctch->type->toBasetype()->isClassHandle(); typeDesc = getTypeDescriptor(*irs, cd); + clssInfo = getIrAggr(cd)->getClassInfoSymbol(); } else { // catch all - typeDesc = llvm::Constant::getNullValue(getVoidPtrType()); + typeDesc = LLConstant::getNullValue(getVoidPtrType()); + clssInfo = LLConstant::getNullValue(DtoType(Type::typeinfoclass->type)); } // "catchpad within %switch [TypeDescriptor, 0, &caughtObject]" must be @@ -772,9 +775,10 @@ class ToIRVisitor : public Visitor { irs->ir->CreateStore(val, cpyObj); exnObj = cpyObj; } - const auto enterCatchFn = + auto enterCatchFn = getRuntimeFunction(Loc(), irs->module, "_d_eh_enter_catch"); - irs->ir->CreateCall(enterCatchFn, DtoBitCast(exnObj, getVoidPtrType()), + irs->ir->CreateCall(enterCatchFn, + {DtoBitCast(exnObj, getVoidPtrType()), clssInfo}, {llvm::OperandBundleDef("funclet", catchpad)}); // The code generator will extract the catch handler to funclets From da817ae9499d445ba236dcb5807d0bf3556162de Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Sat, 13 Feb 2016 11:03:37 +0100 Subject: [PATCH 12/19] eh unwinding: don't alloc data in parent frame, just use nullptr for now --- ir/irfunction.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/ir/irfunction.cpp b/ir/irfunction.cpp index 9328da8c4ac..d953d4d7a9b 100644 --- a/ir/irfunction.cpp +++ b/ir/irfunction.cpp @@ -273,7 +273,7 @@ llvm::BasicBlock *ScopeStack::runCleanupPad(CleanupCursor scope, // // cleanuppad: // %0 = cleanuppad within %funclet[] - // %frame = alloca(byte[16]) + // %frame = nullptr // if (!_d_enter_cleanup(%frame)) br label %cleanupret // else br label %copy // @@ -292,12 +292,9 @@ llvm::BasicBlock *ScopeStack::runCleanupPad(CleanupCursor scope, llvm::BasicBlock *cleanupret = llvm::BasicBlock::Create(irs->context(), "cleanupret", irs->topfunc()); - // allocate some space on the stack where _d_enter_cleanup can place an exception frame - auto int8Ty = LLType::getInt8Ty(gIR->context()); - auto frametype = llvm::ArrayType::get(int8Ty, 16); - auto frameai = DtoRawAlloca(frametype, 0, "cleanup.frame"); - auto frame = - new llvm::BitCastInst(frameai, getPtrToType(int8Ty), "", cleanupbb); + // 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 endFn = getRuntimeFunction(Loc(), irs->module, "_d_leave_cleanup"); llvm::CallInst::Create(endFn, frame, From 9c6e894bb6a0960cdf4a988e11e46d096644f6b7 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Mon, 15 Feb 2016 09:40:23 +0100 Subject: [PATCH 13/19] avoid outlining catch handler --- gen/statements.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/gen/statements.cpp b/gen/statements.cpp index 266e597cd42..3927e63f747 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -775,8 +775,20 @@ class ToIRVisitor : public Visitor { irs->ir->CreateStore(val, cpyObj); exnObj = cpyObj; } + auto enterCatchFn = getRuntimeFunction(Loc(), irs->module, "_d_eh_enter_catch"); +#if 1 + // Exceptions are never rethrown by D code (but thrown again), so + // we can leave the catch handler right away and continue execution + // outside the catch funclet + llvm::BasicBlock *catchhandler = + llvm::BasicBlock::Create(irs->context(), "catchhandler", irs->topfunc()); + llvm::CatchReturnInst::Create(catchpad, catchhandler, irs->scopebb()); + irs->scope() = IRScope(catchhandler); + irs->ir->CreateCall(enterCatchFn, + {DtoBitCast(exnObj, getVoidPtrType()), clssInfo}); +#else irs->ir->CreateCall(enterCatchFn, {DtoBitCast(exnObj, getVoidPtrType()), clssInfo}, {llvm::OperandBundleDef("funclet", catchpad)}); @@ -789,6 +801,7 @@ class ToIRVisitor : public Visitor { llvm::BasicBlock::Create(irs->context(), "catchret", irs->topfunc()); llvm::CatchReturnInst::Create(catchpad, endbb, retbb); irs->func()->scopes->pushCleanup(retbb, retbb); +#endif } #endif @@ -842,7 +855,7 @@ class ToIRVisitor : public Visitor { CleanupCursor currentScope = scopes->currentCleanupScope(); emitBeginCatchMSVCEH(*it, endbb, catchSwitchInst); - scopes->pushFunclet(&catchBB->front()); + //scopes->pushFunclet(&catchBB->front()); // Emit handler, if there is one. The handler is zero, for instance, // when building 'catch { debug foo(); }' in non-debug mode. @@ -854,7 +867,7 @@ class ToIRVisitor : public Visitor { scopes->runCleanups(currentScope, endbb); } scopes->popCleanups(currentScope); - scopes->popFunclet(); + //scopes->popFunclet(); irs->DBuilder.EmitBlockEnd(); From d2e3942bbf9ad6be029d351488eb88ef266a56e2 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Fri, 19 Feb 2016 10:36:08 +0100 Subject: [PATCH 14/19] _d_enter_catch can throw, so it might have to be invoked --- gen/runtime.cpp | 2 +- gen/statements.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/gen/runtime.cpp b/gen/runtime.cpp index 3c9eb7c5549..f4804d004da 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -647,7 +647,7 @@ static void buildRuntimeModule() { // Object _d_eh_enter_catch(ptr exception, ClassInfo catchType) createFwdDecl(LINKc, objectTy, {"_d_eh_enter_catch"}, - {voidPtrTy, classInfoTy}, {}, Attr_NoUnwind); + {voidPtrTy, classInfoTy}, {}); } else { // void _d_eh_resume_unwind(ptr) diff --git a/gen/statements.cpp b/gen/statements.cpp index 3927e63f747..13982b6f691 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -786,8 +786,7 @@ class ToIRVisitor : public Visitor { llvm::BasicBlock::Create(irs->context(), "catchhandler", irs->topfunc()); llvm::CatchReturnInst::Create(catchpad, catchhandler, irs->scopebb()); irs->scope() = IRScope(catchhandler); - irs->ir->CreateCall(enterCatchFn, - {DtoBitCast(exnObj, getVoidPtrType()), clssInfo}); + irs->CreateCallOrInvoke(enterCatchFn, DtoBitCast(exnObj, getVoidPtrType()), clssInfo); #else irs->ir->CreateCall(enterCatchFn, {DtoBitCast(exnObj, getVoidPtrType()), clssInfo}, From ae110f81fed56b9b81ab275fef990ed27f08101c Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Sat, 27 Feb 2016 09:00:39 +0100 Subject: [PATCH 15/19] fix compilation after merge --- gen/statements.cpp | 2 +- ir/irfunction.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gen/statements.cpp b/gen/statements.cpp index 13982b6f691..4940b496cd1 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -724,7 +724,7 @@ class ToIRVisitor : public Visitor { if (var) { // alloca storage for the variable, it always needs a place on the stack // do not initialize, this will be done by the C++ exception handler - var->init = nullptr; + var->_init = nullptr; // redirect scope to avoid the generation of debug info before the // catchpad diff --git a/ir/irfunction.cpp b/ir/irfunction.cpp index d953d4d7a9b..d44b33a267e 100644 --- a/ir/irfunction.cpp +++ b/ir/irfunction.cpp @@ -34,8 +34,8 @@ CatchScope::CatchScope(llvm::Constant *classInfoPtr, bool useMSVCEH() { #if LDC_LLVM_VER >= 308 - return global.params.targetTriple.isWindowsMSVCEnvironment() && - !global.params.targetTriple.isArch64Bit(); + return global.params.targetTriple->isWindowsMSVCEnvironment() && + !global.params.targetTriple->isArch64Bit(); #else return false; #endif From b4df086be8df5b3b0e000d340c4b851bb2d3dcc8 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Tue, 1 Mar 2016 15:15:54 +0100 Subject: [PATCH 16/19] cleanup obsolete changes --- gen/attributes.cpp | 5 --- gen/attributes.h | 1 - gen/ms-cxx-helper.cpp | 85 ++----------------------------------------- gen/statements.cpp | 26 ++----------- ir/irfunction.cpp | 12 ------ ir/irfunction.h | 3 -- 6 files changed, 6 insertions(+), 126 deletions(-) diff --git a/gen/attributes.cpp b/gen/attributes.cpp index ebd81b1af38..ef2688c96da 100644 --- a/gen/attributes.cpp +++ b/gen/attributes.cpp @@ -29,11 +29,6 @@ AttrBuilder &AttrBuilder::add(LLAttribute attribute) { return *this; } -AttrBuilder &AttrBuilder::add(llvm::StringRef A, llvm::StringRef V) { - builder.addAttribute(A, V); - return *this; -} - AttrBuilder &AttrBuilder::remove(LLAttribute attribute) { // never remove 'None' explicitly if (attribute) { diff --git a/gen/attributes.h b/gen/attributes.h index 86dddd8c250..8c6164dde68 100644 --- a/gen/attributes.h +++ b/gen/attributes.h @@ -27,7 +27,6 @@ class AttrBuilder { AttrBuilder &add(LLAttribute attribute); AttrBuilder &remove(LLAttribute attribute); AttrBuilder &merge(const AttrBuilder &other); - AttrBuilder &add(llvm::StringRef A, llvm::StringRef V = llvm::StringRef()); AttrBuilder &addAlignment(unsigned alignment); AttrBuilder &addByVal(unsigned alignment); diff --git a/gen/ms-cxx-helper.cpp b/gen/ms-cxx-helper.cpp index b1a6e08e44a..4b900f3a9fa 100644 --- a/gen/ms-cxx-helper.cpp +++ b/gen/ms-cxx-helper.cpp @@ -110,11 +110,10 @@ void cloneBlocks(const std::vector &srcblocks, CInst, llvm::OperandBundleDef("funclet", funclet)); call->setIsNoInline(); newInst = call; + } else if (funclet && llvm::isa(Inst)) { + newInst = llvm::BranchInst::Create(continueWith); // to cleanupret } } - if (funclet && llvm::isa(Inst)) { - newInst = llvm::BranchInst::Create(continueWith); // cleanupret - } if (!newInst) newInst = Inst->clone(); @@ -140,7 +139,7 @@ bool isCatchSwitchBlock(llvm::BasicBlock* bb) { // copy from clang/.../MicrosoftCXXABI.cpp -// 5 routines for constructing the llvm types for MS RTTI structs. +// routines for constructing the llvm types for MS RTTI structs. llvm::StructType *getTypeDescriptorType(IRState &irs, llvm::Constant *classInfoPtr, llvm::StringRef TypeInfoString) { @@ -187,82 +186,4 @@ llvm::GlobalVariable *getTypeDescriptor(IRState &irs, ClassDeclaration *cd) { return Var; } -#if 0 -// currently unused, information built at runtime ATM -llvm::StructType *BaseClassDescriptorType; -llvm::StructType *ClassHierarchyDescriptorType; -llvm::StructType *CompleteObjectLocatorType; - -bool isImageRelative() { - return global.params.targetTriple.isArch64Bit(); -} - -llvm::Type *getImageRelativeType(llvm::Type *PtrType) { - if (!isImageRelative()) - return PtrType; - return LLType::getInt32Ty(gIR->context()); -} - -llvm::StructType *getClassHierarchyDescriptorType(); - -llvm::StructType *getBaseClassDescriptorType() { - if (BaseClassDescriptorType) - return BaseClassDescriptorType; - auto intTy = LLType::getInt32Ty(gIR->context()); - auto int8Ty = LLType::getInt8Ty(gIR->context()); - llvm::Type *FieldTypes[] = { - getImageRelativeType(getPtrToType(int8Ty)), - intTy, - intTy, - intTy, - intTy, - intTy, - getImageRelativeType(getClassHierarchyDescriptorType()->getPointerTo()), - }; - BaseClassDescriptorType = llvm::StructType::create( - gIR->context(), FieldTypes, "rtti.BaseClassDescriptor"); - return BaseClassDescriptorType; -} - -llvm::StructType *getClassHierarchyDescriptorType() { - if (ClassHierarchyDescriptorType) - return ClassHierarchyDescriptorType; - // Forward-declare RTTIClassHierarchyDescriptor to break a cycle. - ClassHierarchyDescriptorType = llvm::StructType::create( - gIR->context(), "rtti.ClassHierarchyDescriptor"); - auto intTy = LLType::getInt32Ty(gIR->context()); - llvm::Type *FieldTypes[] = { - intTy, - intTy, - intTy, - getImageRelativeType( - getBaseClassDescriptorType()->getPointerTo()->getPointerTo()), - }; - ClassHierarchyDescriptorType->setBody(FieldTypes); - return ClassHierarchyDescriptorType; -} - -llvm::StructType *getCompleteObjectLocatorType() { - if (CompleteObjectLocatorType) - return CompleteObjectLocatorType; - CompleteObjectLocatorType = llvm::StructType::create( - gIR->context(), "rtti.CompleteObjectLocator"); - auto intTy = LLType::getInt32Ty(gIR->context()); - auto int8Ty = LLType::getInt8Ty(gIR->context()); - llvm::Type *FieldTypes[] = { - intTy, - intTy, - intTy, - getImageRelativeType(getPtrToType(int8Ty)), - getImageRelativeType(getClassHierarchyDescriptorType()->getPointerTo()), - getImageRelativeType(CompleteObjectLocatorType), - }; - llvm::ArrayRef FieldTypesRef(FieldTypes); - if (!isImageRelative()) - FieldTypesRef = FieldTypesRef.drop_back(); - CompleteObjectLocatorType->setBody(FieldTypesRef); - return CompleteObjectLocatorType; -} -#endif - #endif // LDC_LLVM_VER >= 308 diff --git a/gen/statements.cpp b/gen/statements.cpp index 4940b496cd1..9ddd1f52000 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -776,9 +776,6 @@ class ToIRVisitor : public Visitor { exnObj = cpyObj; } - auto enterCatchFn = - getRuntimeFunction(Loc(), irs->module, "_d_eh_enter_catch"); -#if 1 // Exceptions are never rethrown by D code (but thrown again), so // we can leave the catch handler right away and continue execution // outside the catch funclet @@ -786,21 +783,9 @@ class ToIRVisitor : public Visitor { llvm::BasicBlock::Create(irs->context(), "catchhandler", irs->topfunc()); llvm::CatchReturnInst::Create(catchpad, catchhandler, irs->scopebb()); irs->scope() = IRScope(catchhandler); + auto enterCatchFn = + getRuntimeFunction(Loc(), irs->module, "_d_eh_enter_catch"); irs->CreateCallOrInvoke(enterCatchFn, DtoBitCast(exnObj, getVoidPtrType()), clssInfo); -#else - irs->ir->CreateCall(enterCatchFn, - {DtoBitCast(exnObj, getVoidPtrType()), clssInfo}, - {llvm::OperandBundleDef("funclet", catchpad)}); - - // The code generator will extract the catch handler to funclets - // so it needs to know the end of the code executed in the handler. - // This is marked by a catch return instruction that is created here - // as a cleanup so it appears in all code paths exiting the catch block - llvm::BasicBlock *retbb = - llvm::BasicBlock::Create(irs->context(), "catchret", irs->topfunc()); - llvm::CatchReturnInst::Create(catchpad, endbb, retbb); - irs->func()->scopes->pushCleanup(retbb, retbb); -#endif } #endif @@ -851,10 +836,7 @@ class ToIRVisitor : public Visitor { irs->scope() = IRScope(catchBB); irs->DBuilder.EmitBlockStart((*it)->loc); - CleanupCursor currentScope = scopes->currentCleanupScope(); - emitBeginCatchMSVCEH(*it, endbb, catchSwitchInst); - //scopes->pushFunclet(&catchBB->front()); // Emit handler, if there is one. The handler is zero, for instance, // when building 'catch { debug foo(); }' in non-debug mode. @@ -863,10 +845,8 @@ class ToIRVisitor : public Visitor { } if (!irs->scopereturned()) { - scopes->runCleanups(currentScope, endbb); + irs->ir->CreateBr(endbb); } - scopes->popCleanups(currentScope); - //scopes->popFunclet(); irs->DBuilder.EmitBlockEnd(); diff --git a/ir/irfunction.cpp b/ir/irfunction.cpp index d44b33a267e..d710ee6757b 100644 --- a/ir/irfunction.cpp +++ b/ir/irfunction.cpp @@ -184,7 +184,6 @@ void executeCleanup(IRState *irs, CleanupScope &scope, scope.exitTargets.push_back(CleanupExitTarget(continueWith)); scope.exitTargets.back().sourceBlocks.push_back(sourceBlock); } - } ScopeStack::~ScopeStack() { @@ -255,17 +254,6 @@ llvm::BasicBlock *ScopeStack::runCleanupPad(CleanupCursor scope, if (isCatchSwitchBlock(cleanupScopes[scope].beginBlock)) return cleanupScopes[scope].beginBlock; - // when hitting a catch return instruction during cleanup, - // just unwind to the corresponding catchswitch block instead - if (!cleanupScopes[scope].beginBlock->empty()) { - if (auto catchret = llvm::dyn_cast( - &cleanupScopes[scope].beginBlock->front())) { - llvm::BasicBlock* endcatch = nullptr; - auto catchpad = catchret->getCatchPad(); - auto catchswitch = catchpad->getCatchSwitch(); - return catchswitch->getUnwindDest(); - } - } // each cleanup block is bracketed by a pair of cleanuppad/cleanupret // instructions, any unwinding should also just continue at the next diff --git a/ir/irfunction.h b/ir/irfunction.h index 2ec142621f8..cbd751d2dd8 100644 --- a/ir/irfunction.h +++ b/ir/irfunction.h @@ -416,7 +416,6 @@ llvm::CallSite ScopeStack::callOrInvoke(llvm::Value *callee, const T &args, #endif if (doesNotThrow || (cleanupScopes.empty() && catchScopes.empty())) { -L_call: llvm::CallInst *call = irs->ir->CreateCall(callee, args, #if LDC_LLVM_VER >= 308 BundleList, @@ -429,8 +428,6 @@ llvm::CallSite ScopeStack::callOrInvoke(llvm::Value *callee, const T &args, } llvm::BasicBlock* landingPad = getLandingPad(); - if (!landingPad) - goto L_call; llvm::BasicBlock *postinvoke = llvm::BasicBlock::Create( irs->context(), "postinvoke", irs->topfunc(), landingPad); From bc43f7fa76b6f36f9e1fe1d19d59c07704236814 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Thu, 3 Mar 2016 08:11:47 +0100 Subject: [PATCH 17/19] fix ordering of overloads --- gen/target.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gen/target.cpp b/gen/target.cpp index 2002983037b..c4bee629b8c 100644 --- a/gen/target.cpp +++ b/gen/target.cpp @@ -41,9 +41,8 @@ void Target::_init() { c_longsize = global.params.is64bit ? 8 : 4; c_long_doublesize = realsize; - // according to DMD, only for 32-bit MSVC++: - reverseCppOverloads = !global.params.is64bit && - global.params.targetTriple->isWindowsMSVCEnvironment(); + // according to DMD, only for MSVC++: + reverseCppOverloads = global.params.targetTriple->isWindowsMSVCEnvironment(); } /****************************** From 7e5431f5ff510aebcf72017852e81770c5123d44 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Fri, 4 Mar 2016 09:27:38 +0100 Subject: [PATCH 18/19] add debug info to thunks as a workaround for https://llvm.org/bugs/show_bug.cgi?id=26833 --- gen/dibuilder.cpp | 69 ++++++++++++++++++++++++++++++++++++++--------- gen/dibuilder.h | 7 +++++ ir/irclass.cpp | 24 +++++++++++++++++ 3 files changed, 88 insertions(+), 12 deletions(-) diff --git a/gen/dibuilder.cpp b/gen/dibuilder.cpp index b6a9cc44be2..50910ee3570 100644 --- a/gen/dibuilder.cpp +++ b/gen/dibuilder.cpp @@ -647,21 +647,66 @@ ldc::DISubprogram ldc::DIBuilder::EmitSubProgram(FuncDeclaration *fd) { // Create subroutine type ldc::DISubroutineType DIFnType = - CreateFunctionType(static_cast(fd->type)); + CreateFunctionType(static_cast(fd->type)); // FIXME: duplicates? return DBuilder.createFunction( - CU, // context - fd->toPrettyChars(), // name - mangleExact(fd), // linkage name - file, // file - fd->loc.linnum, // line no - DIFnType, // type - fd->protection.kind == PROTprivate, // is local to unit - true, // isdefinition - fd->loc.linnum, // FIXME: scope line - DIFlags::FlagPrototyped, // Flags - isOptimizationEnabled() // isOptimized + CU, // context + fd->toPrettyChars(), // name + mangleExact(fd), // linkage name + file, // file + fd->loc.linnum, // line no + DIFnType, // type + fd->protection.kind == PROTprivate, // is local to unit + true, // isdefinition + fd->loc.linnum, // FIXME: scope line + DIFlags::FlagPrototyped, // Flags + isOptimizationEnabled() // isOptimized +#if LDC_LLVM_VER < 308 + , + getIrFunc(fd)->func +#endif + ); +} + +ldc::DISubprogram ldc::DIBuilder::EmitThunk(llvm::Function *Thunk, + FuncDeclaration *fd) { + if (!global.params.symdebug) { +#if LDC_LLVM_VER >= 307 + return nullptr; +#else + return llvm::DISubprogram(); +#endif + } + + Logger::println("Thunk to dwarf subprogram"); + LOG_SCOPE; + + ldc::DICompileUnit CU(GetCU()); + assert(CU && + "Compilation unit missing or corrupted in DIBuilder::EmitThunk"); + + ldc::DIFile file(CreateFile(fd->loc)); + + // Create subroutine type (thunk has same type as wrapped function) + ldc::DISubroutineType DIFnType = CreateFunctionType(fd->type); + + std::string name(fd->toPrettyChars()); + name.append(".__thunk"); + + // FIXME: duplicates? + return DBuilder.createFunction( + CU, // context + name, // name + Thunk->getName(), // linkage name + file, // file + fd->loc.linnum, // line no + DIFnType, // type + fd->protection.kind == PROTprivate, // is local to unit + true, // isdefinition + fd->loc.linnum, // FIXME: scope line + DIFlags::FlagPrototyped, // Flags + isOptimizationEnabled() // isOptimized #if LDC_LLVM_VER < 308 , getIrFunc(fd)->func diff --git a/gen/dibuilder.h b/gen/dibuilder.h index 14fba85198b..73d27d3e609 100644 --- a/gen/dibuilder.h +++ b/gen/dibuilder.h @@ -99,6 +99,13 @@ class DIBuilder { /// \returns the Dwarf subprogram global. DISubprogram EmitSubProgram(FuncDeclaration *fd); // FIXME + /// \brief Emit the Dwarf subprogram global for a thunk. + /// \param Thunk llvm::Function pointer. + /// \param fd The function wrapped by this thunk. + /// \returns the Dwarf subprogram global. + DISubprogram EmitThunk(llvm::Function *Thunk, + FuncDeclaration *fd); // FIXME + /// \brief Emit the Dwarf subprogram global for a module ctor. /// This is used for generated functions like moduleinfoctors, /// module ctors/dtors and unittests. diff --git a/ir/irclass.cpp b/ir/irclass.cpp index 6356c69f526..eb4b21e448e 100644 --- a/ir/irclass.cpp +++ b/ir/irclass.cpp @@ -355,11 +355,27 @@ llvm::GlobalVariable *IrAggr::getInterfaceVtbl(BaseClass *b, bool new_instance, 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)], fd, sizeof(FuncDeclaration))); + thunkFd->ir = new IrDsymbol(); + auto thunkFunc = getIrFunc(thunkFd, true); // create the IrFunction + gIR->functions.push_back(thunkFunc); + + // 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). @@ -381,6 +397,10 @@ llvm::GlobalVariable *IrAggr::getInterfaceVtbl(BaseClass *b, bool new_instance, thisArg = DtoGEP1(thisArg, DtoConstInt(-b->offset), 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::CallSite call = gIR->ir->CreateCall(irFunc->func, args); call.setCallingConv(irFunc->func->getCallingConv()); @@ -393,8 +413,12 @@ llvm::GlobalVariable *IrAggr::getInterfaceVtbl(BaseClass *b, bool new_instance, beginbb); } + gIR->DBuilder.EmitFuncEnd(thunkFd); + // clean up gIR->scopes.pop_back(); + + gIR->functions.pop_back(); } constants.push_back(thunk); From 0de73a83d3fa318808bb65eb0200b681964757c2 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Sat, 5 Mar 2016 10:35:47 +0100 Subject: [PATCH 19/19] fix function name mangling for debug info --- gen/dibuilder.cpp | 4 +++- ir/irclass.cpp | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/gen/dibuilder.cpp b/gen/dibuilder.cpp index 50910ee3570..f9a09f65adf 100644 --- a/gen/dibuilder.cpp +++ b/gen/dibuilder.cpp @@ -645,6 +645,8 @@ ldc::DISubprogram ldc::DIBuilder::EmitSubProgram(FuncDeclaration *fd) { ldc::DIFile file(CreateFile(fd->loc)); + std::string mangledName(getIrFunc(fd)->func->getName()); + // Create subroutine type ldc::DISubroutineType DIFnType = CreateFunctionType(static_cast(fd->type)); @@ -653,7 +655,7 @@ ldc::DISubprogram ldc::DIBuilder::EmitSubProgram(FuncDeclaration *fd) { return DBuilder.createFunction( CU, // context fd->toPrettyChars(), // name - mangleExact(fd), // linkage name + mangledName, // linkage name file, // file fd->loc.linnum, // line no DIFnType, // type diff --git a/ir/irclass.cpp b/ir/irclass.cpp index eb4b21e448e..e37941c03ec 100644 --- a/ir/irclass.cpp +++ b/ir/irclass.cpp @@ -364,6 +364,8 @@ llvm::GlobalVariable *IrAggr::getInterfaceVtbl(BaseClass *b, bool new_instance, new char[sizeof(FuncDeclaration)], fd, sizeof(FuncDeclaration))); thunkFd->ir = new IrDsymbol(); auto thunkFunc = getIrFunc(thunkFd, true); // create the IrFunction + thunkFunc->func = thunk; + thunkFunc->type = irFunc->type; gIR->functions.push_back(thunkFunc); // debug info