MSVC/x86 EH support using __CxxFrameHandler3 personality

This commit is contained in:
Rainer Schuetze 2015-12-21 21:34:00 +01:00 committed by Martin
parent c970d5da3f
commit aad27069ca
7 changed files with 779 additions and 63 deletions

View file

@ -182,6 +182,12 @@ struct IRState {
#else #else
llvm::SmallVector<llvm::Value *, 5> LinkerMetadataArgs; llvm::SmallVector<llvm::Value *, 5> LinkerMetadataArgs;
#endif #endif
#if LDC_LLVM_VER >= 308
// MS C++ compatible type descriptors
llvm::DenseMap<size_t, llvm::StructType *> TypeDescriptorTypeMap;
llvm::DenseMap<llvm::Constant *, llvm::GlobalVariable *> TypeDescriptorMap;
#endif
}; };
void Statement_toIR(Statement *s, IRState *irs); void Statement_toIR(Statement *s, IRState *irs);

265
gen/ms-cxx-helper.cpp Normal file
View file

@ -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<llvm::InvokeInst> (I))
return II->getUnwindDest();
else if (auto CSI = llvm::dyn_cast<llvm::CatchSwitchInst> (I))
return CSI->getUnwindDest();
else if (auto CRPI = llvm::dyn_cast<llvm::CleanupReturnInst> (I))
return CRPI->getUnwindDest();
return nullptr;
}
void mapFunclet(llvm::Instruction *I, llvm::ValueToValueMapTy &VMap, llvm::Value *funclet) {
if (auto II = llvm::dyn_cast<llvm::InvokeInst> (I)) {
auto bundle = II->getOperandBundle(llvm::LLVMContext::OB_funclet);
if (bundle)
VMap[bundle->Inputs[0].get()] = funclet;
} else if (auto CI = llvm::dyn_cast<llvm::CallInst> (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<llvm::BasicBlock *> &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<llvm::BasicBlock *> &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<llvm::BasicBlock *> &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<llvm::BasicBlock *> &srcblocks,
std::vector<llvm::BasicBlock *> &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<llvm::DbgInfoIntrinsic>(Inst)) {
if (auto IInst = llvm::dyn_cast<llvm::InvokeInst> (Inst)) {
newInst = llvm::InvokeInst::Create(
IInst, llvm::OperandBundleDef("funclet", funclet));
} else if (auto CInst = llvm::dyn_cast<llvm::CallInst> (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<llvm::Type *> FieldTypesRef(FieldTypes);
if (!isImageRelative())
FieldTypesRef = FieldTypesRef.drop_back();
CompleteObjectLocatorType->setBody(FieldTypesRef);
return CompleteObjectLocatorType;
}
#endif
#endif // LDC_LLVM_VER >= 308

31
gen/ms-cxx-helper.h Normal file
View file

@ -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<llvm::BasicBlock *> &blocks,
llvm::BasicBlock *bb, llvm::BasicBlock *ebb);
void remapBlocksValue(std::vector<llvm::BasicBlock *> &blocks,
llvm::Value *from, llvm::Value *to);
void cloneBlocks(const std::vector<llvm::BasicBlock *> &srcblocks,
std::vector<llvm::BasicBlock *> &blocks,
llvm::BasicBlock *continueWith, llvm::BasicBlock *unwindTo,
llvm::Value *funclet);
#endif // LDC_GEN_MS_CXX_HELPER_H

View file

@ -24,6 +24,7 @@
#include "gen/llvmhelpers.h" #include "gen/llvmhelpers.h"
#include "gen/logger.h" #include "gen/logger.h"
#include "gen/tollvm.h" #include "gen/tollvm.h"
#include "ir/irfunction.h"
#include "ir/irtype.h" #include "ir/irtype.h"
#include "ir/irtypefunction.h" #include "ir/irtypefunction.h"
#include "llvm/Bitcode/ReaderWriter.h" #include "llvm/Bitcode/ReaderWriter.h"
@ -620,9 +621,11 @@ static void buildRuntimeModule() {
// int _d_eh_personality(...) // int _d_eh_personality(...)
{ {
if (global.params.targetTriple->isWindowsMSVCEnvironment()) { if (global.params.targetTriple->isWindowsMSVCEnvironment()) {
const char *fname =
useMSVCEH() ? "__CxxFrameHandler3" : "_d_eh_personality";
// (ptr ExceptionRecord, ptr EstablisherFrame, ptr ContextRecord, // (ptr ExceptionRecord, ptr EstablisherFrame, ptr ContextRecord,
// ptr DispatcherContext) // ptr DispatcherContext)
createFwdDecl(LINKc, intTy, {"_d_eh_personality"}, createFwdDecl(LINKc, intTy, {fname},
{voidPtrTy, voidPtrTy, voidPtrTy, voidPtrTy}); {voidPtrTy, voidPtrTy, voidPtrTy, voidPtrTy});
} else if (global.params.targetTriple->getArch() == llvm::Triple::arm) { } else if (global.params.targetTriple->getArch() == llvm::Triple::arm) {
// (int state, ptr ucb, ptr context) // (int state, ptr ucb, ptr context)

View file

@ -23,6 +23,7 @@
#include "gen/logger.h" #include "gen/logger.h"
#include "gen/runtime.h" #include "gen/runtime.h"
#include "gen/tollvm.h" #include "gen/tollvm.h"
#include "gen/ms-cxx-helper.h"
#include "ir/irfunction.h" #include "ir/irfunction.h"
#include "ir/irmodule.h" #include "ir/irmodule.h"
#include "llvm/IR/CFG.h" #include "llvm/IR/CFG.h"
@ -709,6 +710,88 @@ public:
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
#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<LLValue *>(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 { void visit(TryCatchStatement *stmt) LLVM_OVERRIDE {
IF_LOG Logger::println("TryCatchStatement::toIR(): %s", IF_LOG Logger::println("TryCatchStatement::toIR(): %s",
stmt->loc.toChars()); stmt->loc.toChars());
@ -733,59 +816,118 @@ public:
CatchBlocks catchBlocks; CatchBlocks catchBlocks;
catchBlocks.reserve(stmt->catches->dim); catchBlocks.reserve(stmt->catches->dim);
for (Catches::reverse_iterator it = stmt->catches->rbegin(), #if LDC_LLVM_VER >= 308
end = stmt->catches->rend(); if (useMSVCEH()) {
it != end; ++it) { ScopeStack *scopes = irs->func()->scopes;
llvm::BasicBlock *catchBB = llvm::BasicBlock::Create( 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->context(), llvm::Twine("catch.") + (*it)->type->toChars(),
irs->topfunc(), endbb); irs->topfunc(), endbb);
irs->scope() = IRScope(catchBB); irs->scope() = IRScope(catchBB);
irs->DBuilder.EmitBlockStart((*it)->loc); irs->DBuilder.EmitBlockStart((*it)->loc);
const auto enterCatchFn = CleanupCursor currentScope = scopes->currentCleanupScope();
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. emitBeginCatchMSVC(*it, catchBB, endbb, catchSwitchInst);
// We will set it in the code that branches from the landing pads scopes->pushFunclet(&catchBB->front());
// (there might be more than one) to catchBB.
auto var = (*it)->var; // Emit handler, if there is one. The handler is zero, for instance,
if (var) { // when building 'catch { debug foo(); }' in non-debug mode.
// This will alloca if we haven't already and take care of nested refs if ((*it)->handler) {
// if there are any. Statement_toIR((*it)->handler, irs);
DtoDeclarationExp(var); }
if (!irs->scopereturned()) {
scopes->runCleanups(currentScope, endbb);
}
scopes->popCleanups(currentScope);
scopes->popFunclet();
irs->DBuilder.EmitBlockEnd();
// Copy the exception reference over from the _d_eh_enter_catch return
// value.
DtoStore(DtoBitCast(throwableObj, DtoType((*it)->var->type)),
getIrLocal(var)->value);
} }
// 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( catchBlocks.push_back(
std::make_pair((*it)->type->toBasetype()->isClassHandle(), catchBB)); std::make_pair(nullptr, catchSwitchBlock)); // just for cleanup
} scopes->pushCatch(nullptr, catchSwitchBlock);
// Only after emitting all the catch bodies, register the catch scopes. // if no landing pad is created, the catch blocks are unused, but
// This is so that (re)throwing inside a catch does not match later // the verifier complains if there are catchpads without personality
// catches. // so we can just set it unconditionally
for (const auto &pair : catchBlocks) { if (!irs->func()->func->hasPersonalityFn()) {
DtoResolveClass(pair.first); const char *personality = "__CxxFrameHandler3";
irs->func()->scopes->pushCatch( LLFunction *personalityFn =
getIrAggr(pair.first)->getClassInfoSymbol(), pair.second); 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);
}
// 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. // Emit the try block.

View file

@ -12,6 +12,7 @@
#include "gen/irstate.h" #include "gen/irstate.h"
#include "gen/runtime.h" #include "gen/runtime.h"
#include "gen/tollvm.h" #include "gen/tollvm.h"
#include "gen/ms-cxx-helper.h"
#include "ir/irdsymbol.h" #include "ir/irdsymbol.h"
#include "ir/irfunction.h" #include "ir/irfunction.h"
#include <sstream> #include <sstream>
@ -31,10 +32,74 @@ CatchScope::CatchScope(llvm::Constant *classInfoPtr,
: classInfoPtr(classInfoPtr), bodyBlock(bodyBlock), : classInfoPtr(classInfoPtr), bodyBlock(bodyBlock),
cleanupScope(cleanupScope) {} cleanupScope(cleanupScope) {}
bool useMSVCEH() {
return global.params.targetTriple.isWindowsMSVCEnvironment() &&
!global.params.targetTriple.isArch64Bit();
}
namespace { 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, void executeCleanup(IRState *irs, CleanupScope &scope,
llvm::BasicBlock *sourceBlock, llvm::BasicBlock *sourceBlock,
llvm::BasicBlock *continueWith) { llvm::BasicBlock *continueWith) {
assert(!useMSVCEH()); // should always use executeCleanupCopying
if (scope.exitTargets.empty() || if (scope.exitTargets.empty() ||
(scope.exitTargets.size() == 1 && (scope.exitTargets.size() == 1 &&
scope.exitTargets[0].branchTarget == continueWith)) { scope.exitTargets[0].branchTarget == continueWith)) {
@ -134,6 +199,12 @@ void ScopeStack::pushCleanup(llvm::BasicBlock *beginBlock,
void ScopeStack::runCleanups(CleanupCursor sourceScope, void ScopeStack::runCleanups(CleanupCursor sourceScope,
CleanupCursor targetScope, CleanupCursor targetScope,
llvm::BasicBlock *continueWith) { llvm::BasicBlock *continueWith) {
#if LDC_LLVM_VER >= 308
if (useMSVCEH()) {
runCleanupCopies(sourceScope, targetScope, continueWith, false);
return;
}
#endif
assert(targetScope <= sourceScope); assert(targetScope <= sourceScope);
if (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<llvm::CatchReturnInst>(
&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) { void ScopeStack::runAllCleanups(llvm::BasicBlock *continueWith) {
runCleanups(0, continueWith); runCleanups(0, continueWith);
} }
@ -170,11 +311,24 @@ void ScopeStack::popCleanups(CleanupCursor targetScope) {
for (const auto &gotoJump : currentUnresolvedGotos()) { for (const auto &gotoJump : currentUnresolvedGotos()) {
// Make the source resp. last cleanup branch to this one. // Make the source resp. last cleanup branch to this one.
llvm::BasicBlock *tentative = gotoJump.tentativeTarget; 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 // And continue execution with the tentative target (we simply reuse
// it because there is no reason not to). // it because there is no reason not to).
executeCleanup(irs, cleanupScopes[i], gotoJump.sourceBlock, tentative); executeCleanup(irs, cleanupScopes[i], gotoJump.sourceBlock, tentative);
}
} }
std::vector<GotoJump> &nextUnresolved = std::vector<GotoJump> &nextUnresolved =
@ -282,6 +436,25 @@ std::vector<llvm::BasicBlock *> &ScopeStack::currentLandingPads() {
: cleanupScopes.back().landingPads; : 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 { namespace {
llvm::LandingPadInst *createLandingPadInst(IRState *irs) { llvm::LandingPadInst *createLandingPadInst(IRState *irs) {
LLType *retType = 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<CatchScope>::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<llvm::CatchSwitchInst>(*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() { llvm::BasicBlock *ScopeStack::emitLandingPad() {
// save and rewrite scope // save and rewrite scope
IRScope savedIRScope = irs->scope(); IRScope savedIRScope = irs->scope();

View file

@ -98,6 +98,9 @@ struct CleanupExitTarget {
/// stores to the branch selector variable when converting from one to two /// stores to the branch selector variable when converting from one to two
/// targets. /// targets.
std::vector<llvm::BasicBlock *> sourceBlocks; std::vector<llvm::BasicBlock *> sourceBlocks;
/// The basic blocks that are executed when going this route
std::vector<llvm::BasicBlock *> cleanupBlocks;
}; };
/// Represents a scope (in abstract terms, not curly braces) that requires a /// Represents a scope (in abstract terms, not curly braces) that requires a
@ -148,6 +151,9 @@ public:
/// and popped again once it is left. If the corresponding landing pad has /// 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. /// not been generated yet (this is done lazily), the pointer is null.
std::vector<llvm::BasicBlock *> landingPads; std::vector<llvm::BasicBlock *> landingPads;
/// The original basic blocks that are executed for beginBlock to endBlock
std::vector<llvm::BasicBlock *> cleanupBlocks;
}; };
/// Stores information to be able to branch to a catch clause if it matches. /// Stores information to be able to branch to a catch clause if it matches.
@ -210,6 +216,11 @@ public:
/// reached. /// reached.
void runAllCleanups(llvm::BasicBlock *continueWith); 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. /// Pops all the cleanups between the current scope and the target cursor.
/// ///
/// This does not insert any cleanup calls, use #runCleanups() beforehand. /// This does not insert any cleanup calls, use #runCleanups() beforehand.
@ -235,6 +246,23 @@ public:
/// Unregisters the last registered catch block. /// Unregisters the last registered catch block.
void popCatch(); 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 /// Registers a loop statement to be used as a target for break/continue
/// statements in the current scope. /// statements in the current scope.
void pushLoopTarget(Statement *loopStatement, void pushLoopTarget(Statement *loopStatement,
@ -295,6 +323,9 @@ public:
/// the way there. /// the way there.
void breakToClosest() { jumpToClosest(breakTargets); } void breakToClosest() { jumpToClosest(breakTargets); }
/// get exisiting or emit new landing pad
llvm::BasicBlock *getLandingPad();
private: private:
/// Internal version that allows specifying the scope at which to start /// Internal version that allows specifying the scope at which to start
/// emitting the cleanups. /// emitting the cleanups.
@ -308,6 +339,10 @@ private:
/// Emits a landing pad to honor all the active cleanups and catches. /// Emits a landing pad to honor all the active cleanups and catches.
llvm::BasicBlock *emitLandingPad(); llvm::BasicBlock *emitLandingPad();
#if LDC_LLVM_VER >= 308
llvm::BasicBlock *emitWin32LandingPad();
#endif
/// Unified implementation for labeled break/continue. /// Unified implementation for labeled break/continue.
void jumpToStatement(std::vector<JumpTarget> &targets, void jumpToStatement(std::vector<JumpTarget> &targets,
Statement *loopOrSwitchStatement); Statement *loopOrSwitchStatement);
@ -347,6 +382,9 @@ private:
/// (null if not yet emitted, one element is pushed to/popped from the back /// (null if not yet emitted, one element is pushed to/popped from the back
/// on entering/leaving a catch block). /// on entering/leaving a catch block).
std::vector<llvm::BasicBlock *> topLevelLandingPads; std::vector<llvm::BasicBlock *> topLevelLandingPads;
/// stack of currently built catch clauses
std::vector<llvm::Value*> funclets;
}; };
template <typename T> template <typename T>
@ -360,31 +398,35 @@ llvm::CallSite ScopeStack::callOrInvoke(llvm::Value *callee, const T &args,
const bool doesNotThrow = const bool doesNotThrow =
calleeFn && (calleeFn->isIntrinsic() || calleeFn->doesNotThrow()); calleeFn && (calleeFn->isIntrinsic() || calleeFn->doesNotThrow());
#if LDC_LLVM_VER >= 308
// calls inside a funclet must be annotated with its value
llvm::SmallVector<llvm::OperandBundleDef, 2> BundleList;
if (auto funclet = getFunclet())
BundleList.push_back(llvm::OperandBundleDef("funclet", funclet));
#endif
if (doesNotThrow || (cleanupScopes.empty() && catchScopes.empty())) { 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) { if (calleeFn) {
call->setAttributes(calleeFn->getAttributes()); call->setAttributes(calleeFn->getAttributes());
} }
return call; return call;
} }
if (currentLandingPads().empty()) { llvm::BasicBlock* landingPad = getLandingPad();
// 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 *postinvoke = llvm::BasicBlock::Create( llvm::BasicBlock *postinvoke = llvm::BasicBlock::Create(
irs->context(), "postinvoke", irs->topfunc(), landingPad); irs->context(), "postinvoke", irs->topfunc(), landingPad);
llvm::InvokeInst *invoke = 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) { if (calleeFn) {
invoke->setAttributes(calleeFn->getAttributes()); invoke->setAttributes(calleeFn->getAttributes());
} }
@ -474,5 +516,6 @@ private:
IrFunction *getIrFunc(FuncDeclaration *decl, bool create = false); IrFunction *getIrFunc(FuncDeclaration *decl, bool create = false);
bool isIrFuncCreated(FuncDeclaration *decl); bool isIrFuncCreated(FuncDeclaration *decl);
bool useMSVCEH();
#endif #endif