mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-04 17:11:44 +03:00
MSVC/x86 EH support using __CxxFrameHandler3 personality
This commit is contained in:
parent
c970d5da3f
commit
aad27069ca
7 changed files with 779 additions and 63 deletions
|
@ -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
265
gen/ms-cxx-helper.cpp
Normal 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
31
gen/ms-cxx-helper.h
Normal 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
|
|
@ -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)
|
||||||
|
|
|
@ -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,6 +816,65 @@ public:
|
||||||
CatchBlocks catchBlocks;
|
CatchBlocks catchBlocks;
|
||||||
catchBlocks.reserve(stmt->catches->dim);
|
catchBlocks.reserve(stmt->catches->dim);
|
||||||
|
|
||||||
|
#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);
|
||||||
|
|
||||||
|
CleanupCursor currentScope = scopes->currentCleanupScope();
|
||||||
|
|
||||||
|
emitBeginCatchMSVC(*it, catchBB, 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.
|
||||||
|
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(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(),
|
for (Catches::reverse_iterator it = stmt->catches->rbegin(),
|
||||||
end = stmt->catches->rend();
|
end = stmt->catches->rend();
|
||||||
it != end; ++it) {
|
it != end; ++it) {
|
||||||
|
@ -763,8 +905,8 @@ public:
|
||||||
getIrLocal(var)->value);
|
getIrLocal(var)->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit handler, if there is one. The handler is zero, for instance, when
|
// Emit handler, if there is one. The handler is zero, for instance,
|
||||||
// building 'catch { debug foo(); }' in non-debug mode.
|
// when building 'catch { debug foo(); }' in non-debug mode.
|
||||||
if ((*it)->handler) {
|
if ((*it)->handler) {
|
||||||
Statement_toIR((*it)->handler, irs);
|
Statement_toIR((*it)->handler, irs);
|
||||||
}
|
}
|
||||||
|
@ -775,10 +917,9 @@ public:
|
||||||
|
|
||||||
irs->DBuilder.EmitBlockEnd();
|
irs->DBuilder.EmitBlockEnd();
|
||||||
|
|
||||||
catchBlocks.push_back(
|
catchBlocks.push_back(std::make_pair(
|
||||||
std::make_pair((*it)->type->toBasetype()->isClassHandle(), catchBB));
|
(*it)->type->toBasetype()->isClassHandle(), catchBB));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only after emitting all the catch bodies, register the catch scopes.
|
// Only after emitting all the catch bodies, register the catch scopes.
|
||||||
// This is so that (re)throwing inside a catch does not match later
|
// This is so that (re)throwing inside a catch does not match later
|
||||||
// catches.
|
// catches.
|
||||||
|
@ -787,6 +928,7 @@ public:
|
||||||
irs->func()->scopes->pushCatch(
|
irs->func()->scopes->pushCatch(
|
||||||
getIrAggr(pair.first)->getClassInfoSymbol(), pair.second);
|
getIrAggr(pair.first)->getClassInfoSymbol(), pair.second);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Emit the try block.
|
// Emit the try block.
|
||||||
irs->scope() = IRScope(trybb);
|
irs->scope() = IRScope(trybb);
|
||||||
|
|
|
@ -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,12 +311,25 @@ 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;
|
||||||
|
#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);
|
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 =
|
||||||
(i == 0) ? topLevelUnresolvedGotos
|
(i == 0) ? topLevelUnresolvedGotos
|
||||||
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue