#include "CodeGenFunction.h"
#include "CGCleanup.h"
#include "CGObjCRuntime.h"
#include "TargetInfo.h"
#include "clang/AST/StmtCXX.h"
#include "llvm/Intrinsics.h"
#include "llvm/Support/CallSite.h"
using namespace clang;
using namespace CodeGen;
static llvm::Constant *getAllocateExceptionFn(CodeGenFunction &CGF) {
llvm::FunctionType *FTy =
llvm::FunctionType::get(CGF.Int8PtrTy, CGF.SizeTy, false);
return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_allocate_exception");
}
static llvm::Constant *getFreeExceptionFn(CodeGenFunction &CGF) {
llvm::FunctionType *FTy =
llvm::FunctionType::get(CGF.VoidTy, CGF.Int8PtrTy, false);
return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_free_exception");
}
static llvm::Constant *getThrowFn(CodeGenFunction &CGF) {
llvm::Type *Args[3] = { CGF.Int8PtrTy, CGF.Int8PtrTy, CGF.Int8PtrTy };
llvm::FunctionType *FTy =
llvm::FunctionType::get(CGF.VoidTy, Args, false);
return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_throw");
}
static llvm::Constant *getReThrowFn(CodeGenFunction &CGF) {
llvm::FunctionType *FTy =
llvm::FunctionType::get(CGF.VoidTy, false);
return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_rethrow");
}
static llvm::Constant *getGetExceptionPtrFn(CodeGenFunction &CGF) {
llvm::FunctionType *FTy =
llvm::FunctionType::get(CGF.Int8PtrTy, CGF.Int8PtrTy, false);
return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_get_exception_ptr");
}
static llvm::Constant *getBeginCatchFn(CodeGenFunction &CGF) {
llvm::FunctionType *FTy =
llvm::FunctionType::get(CGF.Int8PtrTy, CGF.Int8PtrTy, false);
return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_begin_catch");
}
static llvm::Constant *getEndCatchFn(CodeGenFunction &CGF) {
llvm::FunctionType *FTy =
llvm::FunctionType::get(CGF.VoidTy, false);
return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_end_catch");
}
static llvm::Constant *getUnexpectedFn(CodeGenFunction &CGF) {
llvm::FunctionType *FTy =
llvm::FunctionType::get(CGF.VoidTy, CGF.Int8PtrTy, false);
return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_call_unexpected");
}
llvm::Constant *CodeGenFunction::getUnwindResumeFn() {
llvm::FunctionType *FTy =
llvm::FunctionType::get(VoidTy, Int8PtrTy, false);
if (CGM.getLangOptions().SjLjExceptions)
return CGM.CreateRuntimeFunction(FTy, "_Unwind_SjLj_Resume");
return CGM.CreateRuntimeFunction(FTy, "_Unwind_Resume");
}
llvm::Constant *CodeGenFunction::getUnwindResumeOrRethrowFn() {
llvm::FunctionType *FTy =
llvm::FunctionType::get(VoidTy, Int8PtrTy, false);
if (CGM.getLangOptions().SjLjExceptions)
return CGM.CreateRuntimeFunction(FTy, "_Unwind_SjLj_Resume_or_Rethrow");
return CGM.CreateRuntimeFunction(FTy, "_Unwind_Resume_or_Rethrow");
}
static llvm::Constant *getTerminateFn(CodeGenFunction &CGF) {
llvm::FunctionType *FTy =
llvm::FunctionType::get(CGF.VoidTy, false);
StringRef name;
if (CGF.getLangOptions().CPlusPlus)
name = "_ZSt9terminatev"; else if (CGF.getLangOptions().ObjC1 &&
CGF.CGM.getCodeGenOpts().ObjCRuntimeHasTerminate)
name = "objc_terminate";
else
name = "abort";
return CGF.CGM.CreateRuntimeFunction(FTy, name);
}
static llvm::Constant *getCatchallRethrowFn(CodeGenFunction &CGF,
StringRef Name) {
llvm::FunctionType *FTy =
llvm::FunctionType::get(CGF.VoidTy, CGF.Int8PtrTy, false);
return CGF.CGM.CreateRuntimeFunction(FTy, Name);
}
namespace {
struct EHPersonality {
const char *PersonalityFn;
const char *CatchallRethrowFn;
static const EHPersonality &get(const LangOptions &Lang);
static const EHPersonality GNU_C;
static const EHPersonality GNU_C_SJLJ;
static const EHPersonality GNU_ObjC;
static const EHPersonality GNU_ObjCXX;
static const EHPersonality NeXT_ObjC;
static const EHPersonality GNU_CPlusPlus;
static const EHPersonality GNU_CPlusPlus_SJLJ;
};
}
const EHPersonality EHPersonality::GNU_C = { "__gcc_personality_v0", 0 };
const EHPersonality EHPersonality::GNU_C_SJLJ = { "__gcc_personality_sj0", 0 };
const EHPersonality EHPersonality::NeXT_ObjC = { "__objc_personality_v0", 0 };
const EHPersonality EHPersonality::GNU_CPlusPlus = { "__gxx_personality_v0", 0};
const EHPersonality
EHPersonality::GNU_CPlusPlus_SJLJ = { "__gxx_personality_sj0", 0 };
const EHPersonality
EHPersonality::GNU_ObjC = {"__gnu_objc_personality_v0", "objc_exception_throw"};
const EHPersonality
EHPersonality::GNU_ObjCXX = { "__gnustep_objcxx_personality_v0", 0 };
static const EHPersonality &getCPersonality(const LangOptions &L) {
if (L.SjLjExceptions)
return EHPersonality::GNU_C_SJLJ;
return EHPersonality::GNU_C;
}
static const EHPersonality &getObjCPersonality(const LangOptions &L) {
if (L.NeXTRuntime) {
if (L.ObjCNonFragileABI) return EHPersonality::NeXT_ObjC;
else return getCPersonality(L);
} else {
return EHPersonality::GNU_ObjC;
}
}
static const EHPersonality &getCXXPersonality(const LangOptions &L) {
if (L.SjLjExceptions)
return EHPersonality::GNU_CPlusPlus_SJLJ;
else
return EHPersonality::GNU_CPlusPlus;
}
static const EHPersonality &getObjCXXPersonality(const LangOptions &L) {
if (L.NeXTRuntime) {
if (L.ObjCNonFragileABI)
return EHPersonality::NeXT_ObjC;
else
return getCXXPersonality(L);
}
return EHPersonality::GNU_ObjCXX;
}
const EHPersonality &EHPersonality::get(const LangOptions &L) {
if (L.CPlusPlus && L.ObjC1)
return getObjCXXPersonality(L);
else if (L.CPlusPlus)
return getCXXPersonality(L);
else if (L.ObjC1)
return getObjCPersonality(L);
else
return getCPersonality(L);
}
static llvm::Constant *getPersonalityFn(CodeGenModule &CGM,
const EHPersonality &Personality) {
llvm::Constant *Fn =
CGM.CreateRuntimeFunction(llvm::FunctionType::get(CGM.Int32Ty, true),
Personality.PersonalityFn);
return Fn;
}
static llvm::Constant *getOpaquePersonalityFn(CodeGenModule &CGM,
const EHPersonality &Personality) {
llvm::Constant *Fn = getPersonalityFn(CGM, Personality);
return llvm::ConstantExpr::getBitCast(Fn, CGM.Int8PtrTy);
}
static bool PersonalityHasOnlyCXXUses(llvm::Constant *Fn) {
for (llvm::Constant::use_iterator
I = Fn->use_begin(), E = Fn->use_end(); I != E; ++I) {
llvm::User *User = *I;
if (llvm::ConstantExpr *CE = dyn_cast<llvm::ConstantExpr>(User)) {
if (CE->getOpcode() != llvm::Instruction::BitCast) return false;
if (!PersonalityHasOnlyCXXUses(CE))
return false;
continue;
}
llvm::LandingPadInst *LPI = dyn_cast<llvm::LandingPadInst>(User);
if (!LPI) return false;
for (unsigned I = 0, E = LPI->getNumClauses(); I != E; ++I) {
llvm::Value *Val = LPI->getClause(I)->stripPointerCasts();
if (LPI->isCatch(I)) {
if (llvm::GlobalVariable *GV = dyn_cast<llvm::GlobalVariable>(Val))
if (GV->getName().startswith("OBJC_EHTYPE"))
return false;
} else {
llvm::Constant *CVal = cast<llvm::Constant>(Val);
for (llvm::User::op_iterator
II = CVal->op_begin(), IE = CVal->op_end(); II != IE; ++II) {
if (llvm::GlobalVariable *GV =
cast<llvm::GlobalVariable>((*II)->stripPointerCasts()))
if (GV->getName().startswith("OBJC_EHTYPE"))
return false;
}
}
}
}
return true;
}
void CodeGenModule::SimplifyPersonality() {
if (!Context.getTargetInfo().getTriple().isOSDarwin())
return;
if (!Features.CPlusPlus || !Features.ObjC1 || !Features.Exceptions)
return;
const EHPersonality &ObjCXX = EHPersonality::get(Features);
const EHPersonality &CXX = getCXXPersonality(Features);
if (&ObjCXX == &CXX)
return;
assert(std::strcmp(ObjCXX.PersonalityFn, CXX.PersonalityFn) != 0 &&
"Different EHPersonalities using the same personality function.");
llvm::Function *Fn = getModule().getFunction(ObjCXX.PersonalityFn);
if (!Fn || Fn->use_empty()) return;
if (!PersonalityHasOnlyCXXUses(Fn)) return;
llvm::Constant *CXXFn = getPersonalityFn(*this, CXX);
if (Fn->getType() != CXXFn->getType()) return;
Fn->replaceAllUsesWith(CXXFn);
Fn->eraseFromParent();
}
static llvm::Constant *getCatchAllValue(CodeGenFunction &CGF) {
return llvm::ConstantPointerNull::get(CGF.Int8PtrTy);
}
namespace {
struct FreeException : EHScopeStack::Cleanup {
llvm::Value *exn;
FreeException(llvm::Value *exn) : exn(exn) {}
void Emit(CodeGenFunction &CGF, Flags flags) {
CGF.Builder.CreateCall(getFreeExceptionFn(CGF), exn)
->setDoesNotThrow();
}
};
}
static void EmitAnyExprToExn(CodeGenFunction &CGF, const Expr *e,
llvm::Value *addr) {
CGF.pushFullExprCleanup<FreeException>(EHCleanup, addr);
EHScopeStack::stable_iterator cleanup = CGF.EHStack.stable_begin();
llvm::Type *ty = CGF.ConvertTypeForMem(e->getType())->getPointerTo();
llvm::Value *typedAddr = CGF.Builder.CreateBitCast(addr, ty);
CGF.EmitAnyExprToMem(e, typedAddr, e->getType().getQualifiers(),
true);
CGF.DeactivateCleanupBlock(cleanup, cast<llvm::Instruction>(typedAddr));
}
llvm::Value *CodeGenFunction::getExceptionSlot() {
if (!ExceptionSlot)
ExceptionSlot = CreateTempAlloca(Int8PtrTy, "exn.slot");
return ExceptionSlot;
}
llvm::Value *CodeGenFunction::getEHSelectorSlot() {
if (!EHSelectorSlot)
EHSelectorSlot = CreateTempAlloca(Int32Ty, "ehselector.slot");
return EHSelectorSlot;
}
llvm::Value *CodeGenFunction::getExceptionFromSlot() {
return Builder.CreateLoad(getExceptionSlot(), "exn");
}
llvm::Value *CodeGenFunction::getSelectorFromSlot() {
return Builder.CreateLoad(getEHSelectorSlot(), "sel");
}
void CodeGenFunction::EmitCXXThrowExpr(const CXXThrowExpr *E) {
if (!E->getSubExpr()) {
if (getInvokeDest()) {
Builder.CreateInvoke(getReThrowFn(*this),
getUnreachableBlock(),
getInvokeDest())
->setDoesNotReturn();
} else {
Builder.CreateCall(getReThrowFn(*this))->setDoesNotReturn();
Builder.CreateUnreachable();
}
EmitBlock(createBasicBlock("throw.cont"));
return;
}
QualType ThrowType = E->getSubExpr()->getType();
llvm::Type *SizeTy = ConvertType(getContext().getSizeType());
uint64_t TypeSize = getContext().getTypeSizeInChars(ThrowType).getQuantity();
llvm::Constant *AllocExceptionFn = getAllocateExceptionFn(*this);
llvm::CallInst *ExceptionPtr =
Builder.CreateCall(AllocExceptionFn,
llvm::ConstantInt::get(SizeTy, TypeSize),
"exception");
ExceptionPtr->setDoesNotThrow();
EmitAnyExprToExn(*this, E->getSubExpr(), ExceptionPtr);
llvm::Constant *TypeInfo = CGM.GetAddrOfRTTIDescriptor(ThrowType,
true);
llvm::Constant *Dtor = 0;
if (const RecordType *RecordTy = ThrowType->getAs<RecordType>()) {
CXXRecordDecl *Record = cast<CXXRecordDecl>(RecordTy->getDecl());
if (!Record->hasTrivialDestructor()) {
CXXDestructorDecl *DtorD = Record->getDestructor();
Dtor = CGM.GetAddrOfCXXDestructor(DtorD, Dtor_Complete);
Dtor = llvm::ConstantExpr::getBitCast(Dtor, Int8PtrTy);
}
}
if (!Dtor) Dtor = llvm::Constant::getNullValue(Int8PtrTy);
if (getInvokeDest()) {
llvm::InvokeInst *ThrowCall =
Builder.CreateInvoke3(getThrowFn(*this),
getUnreachableBlock(), getInvokeDest(),
ExceptionPtr, TypeInfo, Dtor);
ThrowCall->setDoesNotReturn();
} else {
llvm::CallInst *ThrowCall =
Builder.CreateCall3(getThrowFn(*this), ExceptionPtr, TypeInfo, Dtor);
ThrowCall->setDoesNotReturn();
Builder.CreateUnreachable();
}
EmitBlock(createBasicBlock("throw.cont"));
}
void CodeGenFunction::EmitStartEHSpec(const Decl *D) {
if (!CGM.getLangOptions().CXXExceptions)
return;
const FunctionDecl* FD = dyn_cast_or_null<FunctionDecl>(D);
if (FD == 0)
return;
const FunctionProtoType *Proto = FD->getType()->getAs<FunctionProtoType>();
if (Proto == 0)
return;
ExceptionSpecificationType EST = Proto->getExceptionSpecType();
if (isNoexceptExceptionSpec(EST)) {
if (Proto->getNoexceptSpec(getContext()) == FunctionProtoType::NR_Nothrow) {
EHStack.pushTerminate();
}
} else if (EST == EST_Dynamic || EST == EST_DynamicNone) {
unsigned NumExceptions = Proto->getNumExceptions();
EHFilterScope *Filter = EHStack.pushFilter(NumExceptions);
for (unsigned I = 0; I != NumExceptions; ++I) {
QualType Ty = Proto->getExceptionType(I);
QualType ExceptType = Ty.getNonReferenceType().getUnqualifiedType();
llvm::Value *EHType = CGM.GetAddrOfRTTIDescriptor(ExceptType,
true);
Filter->setFilter(I, EHType);
}
}
}
static void emitFilterDispatchBlock(CodeGenFunction &CGF,
EHFilterScope &filterScope) {
llvm::BasicBlock *dispatchBlock = filterScope.getCachedEHDispatchBlock();
if (!dispatchBlock) return;
if (dispatchBlock->use_empty()) {
delete dispatchBlock;
return;
}
CGF.EmitBlockAfterUses(dispatchBlock);
if (filterScope.getNumFilters()) {
llvm::Value *selector = CGF.getSelectorFromSlot();
llvm::BasicBlock *unexpectedBB = CGF.createBasicBlock("ehspec.unexpected");
llvm::Value *zero = CGF.Builder.getInt32(0);
llvm::Value *failsFilter =
CGF.Builder.CreateICmpSLT(selector, zero, "ehspec.fails");
CGF.Builder.CreateCondBr(failsFilter, unexpectedBB, CGF.getEHResumeBlock());
CGF.EmitBlock(unexpectedBB);
}
llvm::Value *exn = CGF.getExceptionFromSlot();
CGF.Builder.CreateCall(getUnexpectedFn(CGF), exn)
->setDoesNotReturn();
CGF.Builder.CreateUnreachable();
}
void CodeGenFunction::EmitEndEHSpec(const Decl *D) {
if (!CGM.getLangOptions().CXXExceptions)
return;
const FunctionDecl* FD = dyn_cast_or_null<FunctionDecl>(D);
if (FD == 0)
return;
const FunctionProtoType *Proto = FD->getType()->getAs<FunctionProtoType>();
if (Proto == 0)
return;
ExceptionSpecificationType EST = Proto->getExceptionSpecType();
if (isNoexceptExceptionSpec(EST)) {
if (Proto->getNoexceptSpec(getContext()) == FunctionProtoType::NR_Nothrow) {
EHStack.popTerminate();
}
} else if (EST == EST_Dynamic || EST == EST_DynamicNone) {
EHFilterScope &filterScope = cast<EHFilterScope>(*EHStack.begin());
emitFilterDispatchBlock(*this, filterScope);
EHStack.popFilter();
}
}
void CodeGenFunction::EmitCXXTryStmt(const CXXTryStmt &S) {
EnterCXXTryStmt(S);
EmitStmt(S.getTryBlock());
ExitCXXTryStmt(S);
}
void CodeGenFunction::EnterCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) {
unsigned NumHandlers = S.getNumHandlers();
EHCatchScope *CatchScope = EHStack.pushCatch(NumHandlers);
for (unsigned I = 0; I != NumHandlers; ++I) {
const CXXCatchStmt *C = S.getHandler(I);
llvm::BasicBlock *Handler = createBasicBlock("catch");
if (C->getExceptionDecl()) {
QualType CaughtType = C->getCaughtType();
CaughtType = CaughtType.getNonReferenceType().getUnqualifiedType();
llvm::Value *TypeInfo = 0;
if (CaughtType->isObjCObjectPointerType())
TypeInfo = CGM.getObjCRuntime().GetEHType(CaughtType);
else
TypeInfo = CGM.GetAddrOfRTTIDescriptor(CaughtType, true);
CatchScope->setHandler(I, TypeInfo, Handler);
} else {
CatchScope->setCatchAllHandler(I, Handler);
}
}
}
llvm::BasicBlock *
CodeGenFunction::getEHDispatchBlock(EHScopeStack::stable_iterator si) {
if (si == EHStack.stable_end())
return getEHResumeBlock();
EHScope &scope = *EHStack.find(si);
llvm::BasicBlock *dispatchBlock = scope.getCachedEHDispatchBlock();
if (!dispatchBlock) {
switch (scope.getKind()) {
case EHScope::Catch: {
EHCatchScope &catchScope = cast<EHCatchScope>(scope);
if (catchScope.getNumHandlers() == 1 &&
catchScope.getHandler(0).isCatchAll()) {
dispatchBlock = catchScope.getHandler(0).Block;
} else {
dispatchBlock = createBasicBlock("catch.dispatch");
}
break;
}
case EHScope::Cleanup:
dispatchBlock = createBasicBlock("ehcleanup");
break;
case EHScope::Filter:
dispatchBlock = createBasicBlock("filter.dispatch");
break;
case EHScope::Terminate:
dispatchBlock = getTerminateHandler();
break;
}
scope.setCachedEHDispatchBlock(dispatchBlock);
}
return dispatchBlock;
}
static bool isNonEHScope(const EHScope &S) {
switch (S.getKind()) {
case EHScope::Cleanup:
return !cast<EHCleanupScope>(S).isEHCleanup();
case EHScope::Filter:
case EHScope::Catch:
case EHScope::Terminate:
return false;
}
llvm_unreachable("Invalid EHScope Kind!");
}
llvm::BasicBlock *CodeGenFunction::getInvokeDestImpl() {
assert(EHStack.requiresLandingPad());
assert(!EHStack.empty());
if (!CGM.getLangOptions().Exceptions)
return 0;
llvm::BasicBlock *LP = EHStack.begin()->getCachedLandingPad();
if (LP) return LP;
LP = EmitLandingPad();
assert(LP);
for (EHScopeStack::iterator ir = EHStack.begin(); true; ++ir) {
ir->setCachedLandingPad(LP);
if (!isNonEHScope(*ir)) break;
}
return LP;
}
enum CleanupHackLevel_t {
CHL_MandatoryCatchall,
CHL_MandatoryCleanup,
CHL_Ideal
};
const CleanupHackLevel_t CleanupHackLevel = CHL_MandatoryCleanup;
llvm::BasicBlock *CodeGenFunction::EmitLandingPad() {
assert(EHStack.requiresLandingPad());
EHScope &innermostEHScope = *EHStack.find(EHStack.getInnermostEHScope());
switch (innermostEHScope.getKind()) {
case EHScope::Terminate:
return getTerminateLandingPad();
case EHScope::Catch:
case EHScope::Cleanup:
case EHScope::Filter:
if (llvm::BasicBlock *lpad = innermostEHScope.getCachedLandingPad())
return lpad;
}
CGBuilderTy::InsertPoint savedIP = Builder.saveAndClearIP();
const EHPersonality &personality = EHPersonality::get(getLangOptions());
llvm::BasicBlock *lpad = createBasicBlock("lpad");
EmitBlock(lpad);
llvm::LandingPadInst *LPadInst =
Builder.CreateLandingPad(llvm::StructType::get(Int8PtrTy, Int32Ty, NULL),
getOpaquePersonalityFn(CGM, personality), 0);
llvm::Value *LPadExn = Builder.CreateExtractValue(LPadInst, 0);
Builder.CreateStore(LPadExn, getExceptionSlot());
llvm::Value *LPadSel = Builder.CreateExtractValue(LPadInst, 1);
Builder.CreateStore(LPadSel, getEHSelectorSlot());
bool hasCatchAll = false;
bool hasCleanup = false;
bool hasFilter = false;
SmallVector<llvm::Value*, 4> filterTypes;
llvm::SmallPtrSet<llvm::Value*, 4> catchTypes;
for (EHScopeStack::iterator I = EHStack.begin(), E = EHStack.end();
I != E; ++I) {
switch (I->getKind()) {
case EHScope::Cleanup:
hasCleanup = (hasCleanup || cast<EHCleanupScope>(*I).isEHCleanup());
continue;
case EHScope::Filter: {
assert(I.next() == EHStack.end() && "EH filter is not end of EH stack");
assert(!hasCatchAll && "EH filter reached after catch-all");
EHFilterScope &filter = cast<EHFilterScope>(*I);
hasFilter = true;
for (unsigned i = 0, e = filter.getNumFilters(); i != e; ++i)
filterTypes.push_back(filter.getFilter(i));
goto done;
}
case EHScope::Terminate:
assert(!hasCatchAll);
hasCatchAll = true;
goto done;
case EHScope::Catch:
break;
}
EHCatchScope &catchScope = cast<EHCatchScope>(*I);
for (unsigned hi = 0, he = catchScope.getNumHandlers(); hi != he; ++hi) {
EHCatchScope::Handler handler = catchScope.getHandler(hi);
if (!handler.Type) {
assert(!hasCatchAll);
hasCatchAll = true;
goto done;
}
if (catchTypes.insert(handler.Type))
LPadInst->addClause(handler.Type);
}
}
done:
assert(!(hasCatchAll && hasFilter));
if (hasCatchAll) {
LPadInst->addClause(getCatchAllValue(*this));
} else if (hasFilter) {
llvm::SmallVector<llvm::Constant*, 8> Filters;
llvm::ArrayType *AType =
llvm::ArrayType::get(!filterTypes.empty() ?
filterTypes[0]->getType() : Int8PtrTy,
filterTypes.size());
for (unsigned i = 0, e = filterTypes.size(); i != e; ++i)
Filters.push_back(cast<llvm::Constant>(filterTypes[i]));
llvm::Constant *FilterArray = llvm::ConstantArray::get(AType, Filters);
LPadInst->addClause(FilterArray);
if (hasCleanup)
LPadInst->setCleanup(true);
} else if (CleanupHackLevel == CHL_MandatoryCatchall || hasCleanup) {
if (CleanupHackLevel == CHL_MandatoryCatchall)
LPadInst->addClause(getCatchAllValue(*this));
else
LPadInst->setCleanup(true);
}
assert((LPadInst->getNumClauses() > 0 || LPadInst->isCleanup()) &&
"landingpad instruction has no clauses!");
Builder.CreateBr(getEHDispatchBlock(EHStack.getInnermostEHScope()));
Builder.restoreIP(savedIP);
return lpad;
}
namespace {
struct CallEndCatch : EHScopeStack::Cleanup {
CallEndCatch(bool MightThrow) : MightThrow(MightThrow) {}
bool MightThrow;
void Emit(CodeGenFunction &CGF, Flags flags) {
if (!MightThrow) {
CGF.Builder.CreateCall(getEndCatchFn(CGF))->setDoesNotThrow();
return;
}
CGF.EmitCallOrInvoke(getEndCatchFn(CGF));
}
};
}
static llvm::Value *CallBeginCatch(CodeGenFunction &CGF,
llvm::Value *Exn,
bool EndMightThrow) {
llvm::CallInst *Call = CGF.Builder.CreateCall(getBeginCatchFn(CGF), Exn);
Call->setDoesNotThrow();
CGF.EHStack.pushCleanup<CallEndCatch>(NormalAndEHCleanup, EndMightThrow);
return Call;
}
static void InitCatchParam(CodeGenFunction &CGF,
const VarDecl &CatchParam,
llvm::Value *ParamAddr) {
llvm::Value *Exn = CGF.getExceptionFromSlot();
CanQualType CatchType =
CGF.CGM.getContext().getCanonicalType(CatchParam.getType());
llvm::Type *LLVMCatchTy = CGF.ConvertTypeForMem(CatchType);
if (isa<ReferenceType>(CatchType)) {
QualType CaughtType = cast<ReferenceType>(CatchType)->getPointeeType();
bool EndCatchMightThrow = CaughtType->isRecordType();
llvm::Value *AdjustedExn = CallBeginCatch(CGF, Exn, EndCatchMightThrow);
if (const PointerType *PT = dyn_cast<PointerType>(CaughtType)) {
QualType PointeeType = PT->getPointeeType();
if (!PointeeType->isRecordType()) {
unsigned HeaderSize =
CGF.CGM.getTargetCodeGenInfo().getSizeOfUnwindException();
AdjustedExn = CGF.Builder.CreateConstGEP1_32(Exn, HeaderSize);
} else {
llvm::Type *PtrTy =
cast<llvm::PointerType>(LLVMCatchTy)->getElementType();
llvm::Value *ExnPtrTmp = CGF.CreateTempAlloca(PtrTy, "exn.byref.tmp");
llvm::Value *Casted = CGF.Builder.CreateBitCast(AdjustedExn, PtrTy);
CGF.Builder.CreateStore(Casted, ExnPtrTmp);
AdjustedExn = ExnPtrTmp;
}
}
llvm::Value *ExnCast =
CGF.Builder.CreateBitCast(AdjustedExn, LLVMCatchTy, "exn.byref");
CGF.Builder.CreateStore(ExnCast, ParamAddr);
return;
}
bool IsComplex = false;
if (!CGF.hasAggregateLLVMType(CatchType) ||
(IsComplex = CatchType->isAnyComplexType())) {
llvm::Value *AdjustedExn = CallBeginCatch(CGF, Exn, false);
if (CatchType->hasPointerRepresentation()) {
llvm::Value *CastExn =
CGF.Builder.CreateBitCast(AdjustedExn, LLVMCatchTy, "exn.casted");
switch (CatchType.getQualifiers().getObjCLifetime()) {
case Qualifiers::OCL_Strong:
CastExn = CGF.EmitARCRetainNonBlock(CastExn);
case Qualifiers::OCL_None:
case Qualifiers::OCL_ExplicitNone:
case Qualifiers::OCL_Autoreleasing:
CGF.Builder.CreateStore(CastExn, ParamAddr);
return;
case Qualifiers::OCL_Weak:
CGF.EmitARCInitWeak(ParamAddr, CastExn);
return;
}
llvm_unreachable("bad ownership qualifier!");
}
llvm::Type *PtrTy = LLVMCatchTy->getPointerTo(0); llvm::Value *Cast = CGF.Builder.CreateBitCast(AdjustedExn, PtrTy);
if (IsComplex) {
CGF.StoreComplexToAddr(CGF.LoadComplexFromAddr(Cast, false),
ParamAddr, false);
} else {
unsigned Alignment =
CGF.getContext().getDeclAlign(&CatchParam).getQuantity();
llvm::Value *ExnLoad = CGF.Builder.CreateLoad(Cast, "exn.scalar");
CGF.EmitStoreOfScalar(ExnLoad, ParamAddr, false, Alignment,
CatchType);
}
return;
}
assert(isa<RecordType>(CatchType) && "unexpected catch type!");
llvm::Type *PtrTy = LLVMCatchTy->getPointerTo(0);
const Expr *copyExpr = CatchParam.getInit();
if (!copyExpr) {
llvm::Value *rawAdjustedExn = CallBeginCatch(CGF, Exn, true);
llvm::Value *adjustedExn = CGF.Builder.CreateBitCast(rawAdjustedExn, PtrTy);
CGF.EmitAggregateCopy(ParamAddr, adjustedExn, CatchType);
return;
}
llvm::CallInst *rawAdjustedExn =
CGF.Builder.CreateCall(getGetExceptionPtrFn(CGF), Exn);
rawAdjustedExn->setDoesNotThrow();
llvm::Value *adjustedExn = CGF.Builder.CreateBitCast(rawAdjustedExn, PtrTy);
CodeGenFunction::OpaqueValueMapping
opaque(CGF, OpaqueValueExpr::findInCopyConstruct(copyExpr),
CGF.MakeAddrLValue(adjustedExn, CatchParam.getType()));
CGF.EHStack.pushTerminate();
CharUnits Alignment = CGF.getContext().getDeclAlign(&CatchParam);
CGF.EmitAggExpr(copyExpr,
AggValueSlot::forAddr(ParamAddr, Alignment, Qualifiers(),
AggValueSlot::IsNotDestructed,
AggValueSlot::DoesNotNeedGCBarriers,
AggValueSlot::IsNotAliased));
CGF.EHStack.popTerminate();
opaque.pop();
CallBeginCatch(CGF, Exn, true);
}
static void BeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *S) {
VarDecl *CatchParam = S->getExceptionDecl();
if (!CatchParam) {
llvm::Value *Exn = CGF.getExceptionFromSlot();
CallBeginCatch(CGF, Exn, true);
return;
}
CodeGenFunction::AutoVarEmission var = CGF.EmitAutoVarAlloca(*CatchParam);
InitCatchParam(CGF, *CatchParam, var.getObjectAddress(CGF));
CGF.EmitAutoVarCleanups(var);
}
namespace {
struct CallRethrow : EHScopeStack::Cleanup {
void Emit(CodeGenFunction &CGF, Flags flags) {
CGF.EmitCallOrInvoke(getReThrowFn(CGF));
}
};
}
static void emitCatchDispatchBlock(CodeGenFunction &CGF,
EHCatchScope &catchScope) {
llvm::BasicBlock *dispatchBlock = catchScope.getCachedEHDispatchBlock();
assert(dispatchBlock);
if (catchScope.getNumHandlers() == 1 &&
catchScope.getHandler(0).isCatchAll()) {
assert(dispatchBlock == catchScope.getHandler(0).Block);
return;
}
CGBuilderTy::InsertPoint savedIP = CGF.Builder.saveIP();
CGF.EmitBlockAfterUses(dispatchBlock);
llvm::Value *llvm_eh_typeid_for =
CGF.CGM.getIntrinsic(llvm::Intrinsic::eh_typeid_for);
llvm::Value *selector = CGF.getSelectorFromSlot();
for (unsigned i = 0, e = catchScope.getNumHandlers(); ; ++i) {
assert(i < e && "ran off end of handlers!");
const EHCatchScope::Handler &handler = catchScope.getHandler(i);
llvm::Value *typeValue = handler.Type;
assert(typeValue && "fell into catch-all case!");
typeValue = CGF.Builder.CreateBitCast(typeValue, CGF.Int8PtrTy);
bool nextIsEnd;
llvm::BasicBlock *nextBlock;
if (i + 1 == e) {
nextBlock = CGF.getEHDispatchBlock(catchScope.getEnclosingEHScope());
nextIsEnd = true;
} else if (catchScope.getHandler(i+1).isCatchAll()) {
nextBlock = catchScope.getHandler(i+1).Block;
nextIsEnd = true;
} else {
nextBlock = CGF.createBasicBlock("catch.fallthrough");
nextIsEnd = false;
}
llvm::CallInst *typeIndex =
CGF.Builder.CreateCall(llvm_eh_typeid_for, typeValue);
typeIndex->setDoesNotThrow();
llvm::Value *matchesTypeIndex =
CGF.Builder.CreateICmpEQ(selector, typeIndex, "matches");
CGF.Builder.CreateCondBr(matchesTypeIndex, handler.Block, nextBlock);
if (nextIsEnd) {
CGF.Builder.restoreIP(savedIP);
return;
}
CGF.EmitBlock(nextBlock);
}
}
void CodeGenFunction::popCatchScope() {
EHCatchScope &catchScope = cast<EHCatchScope>(*EHStack.begin());
if (catchScope.hasEHBranches())
emitCatchDispatchBlock(*this, catchScope);
EHStack.popCatch();
}
void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) {
unsigned NumHandlers = S.getNumHandlers();
EHCatchScope &CatchScope = cast<EHCatchScope>(*EHStack.begin());
assert(CatchScope.getNumHandlers() == NumHandlers);
if (!CatchScope.hasEHBranches()) {
EHStack.popCatch();
return;
}
emitCatchDispatchBlock(*this, CatchScope);
SmallVector<EHCatchScope::Handler, 8> Handlers(NumHandlers);
memcpy(Handlers.data(), CatchScope.begin(),
NumHandlers * sizeof(EHCatchScope::Handler));
EHStack.popCatch();
llvm::BasicBlock *ContBB = createBasicBlock("try.cont");
if (HaveInsertPoint())
Builder.CreateBr(ContBB);
bool ImplicitRethrow = false;
if (IsFnTryBlock)
ImplicitRethrow = isa<CXXDestructorDecl>(CurCodeDecl) ||
isa<CXXConstructorDecl>(CurCodeDecl);
for (unsigned I = NumHandlers; I != 0; --I) {
llvm::BasicBlock *CatchBlock = Handlers[I-1].Block;
EmitBlockAfterUses(CatchBlock);
const CXXCatchStmt *C = S.getHandler(I-1);
RunCleanupsScope CatchScope(*this);
BeginCatch(*this, C);
if (ImplicitRethrow)
EHStack.pushCleanup<CallRethrow>(NormalCleanup);
EmitStmt(C->getHandlerBlock());
CatchScope.ForceCleanup();
if (HaveInsertPoint())
Builder.CreateBr(ContBB);
}
EmitBlock(ContBB);
}
namespace {
struct CallEndCatchForFinally : EHScopeStack::Cleanup {
llvm::Value *ForEHVar;
llvm::Value *EndCatchFn;
CallEndCatchForFinally(llvm::Value *ForEHVar, llvm::Value *EndCatchFn)
: ForEHVar(ForEHVar), EndCatchFn(EndCatchFn) {}
void Emit(CodeGenFunction &CGF, Flags flags) {
llvm::BasicBlock *EndCatchBB = CGF.createBasicBlock("finally.endcatch");
llvm::BasicBlock *CleanupContBB =
CGF.createBasicBlock("finally.cleanup.cont");
llvm::Value *ShouldEndCatch =
CGF.Builder.CreateLoad(ForEHVar, "finally.endcatch");
CGF.Builder.CreateCondBr(ShouldEndCatch, EndCatchBB, CleanupContBB);
CGF.EmitBlock(EndCatchBB);
CGF.EmitCallOrInvoke(EndCatchFn); CGF.EmitBlock(CleanupContBB);
}
};
struct PerformFinally : EHScopeStack::Cleanup {
const Stmt *Body;
llvm::Value *ForEHVar;
llvm::Value *EndCatchFn;
llvm::Value *RethrowFn;
llvm::Value *SavedExnVar;
PerformFinally(const Stmt *Body, llvm::Value *ForEHVar,
llvm::Value *EndCatchFn,
llvm::Value *RethrowFn, llvm::Value *SavedExnVar)
: Body(Body), ForEHVar(ForEHVar), EndCatchFn(EndCatchFn),
RethrowFn(RethrowFn), SavedExnVar(SavedExnVar) {}
void Emit(CodeGenFunction &CGF, Flags flags) {
if (EndCatchFn)
CGF.EHStack.pushCleanup<CallEndCatchForFinally>(NormalAndEHCleanup,
ForEHVar, EndCatchFn);
llvm::Value *SavedCleanupDest =
CGF.Builder.CreateLoad(CGF.getNormalCleanupDestSlot(),
"cleanup.dest.saved");
CGF.EmitStmt(Body);
if (CGF.HaveInsertPoint()) {
llvm::BasicBlock *RethrowBB = CGF.createBasicBlock("finally.rethrow");
llvm::BasicBlock *ContBB = CGF.createBasicBlock("finally.cont");
llvm::Value *ShouldRethrow =
CGF.Builder.CreateLoad(ForEHVar, "finally.shouldthrow");
CGF.Builder.CreateCondBr(ShouldRethrow, RethrowBB, ContBB);
CGF.EmitBlock(RethrowBB);
if (SavedExnVar) {
CGF.EmitCallOrInvoke(RethrowFn, CGF.Builder.CreateLoad(SavedExnVar));
} else {
CGF.EmitCallOrInvoke(RethrowFn);
}
CGF.Builder.CreateUnreachable();
CGF.EmitBlock(ContBB);
CGF.Builder.CreateStore(SavedCleanupDest,
CGF.getNormalCleanupDestSlot());
}
if (EndCatchFn) {
CGBuilderTy::InsertPoint SavedIP = CGF.Builder.saveAndClearIP();
CGF.PopCleanupBlock();
CGF.Builder.restoreIP(SavedIP);
}
CGF.EnsureInsertPoint();
}
};
}
void CodeGenFunction::FinallyInfo::enter(CodeGenFunction &CGF,
const Stmt *body,
llvm::Constant *beginCatchFn,
llvm::Constant *endCatchFn,
llvm::Constant *rethrowFn) {
assert((beginCatchFn != 0) == (endCatchFn != 0) &&
"begin/end catch functions not paired");
assert(rethrowFn && "rethrow function is required");
BeginCatchFn = beginCatchFn;
llvm::FunctionType *rethrowFnTy =
cast<llvm::FunctionType>(
cast<llvm::PointerType>(rethrowFn->getType())->getElementType());
SavedExnVar = 0;
if (rethrowFnTy->getNumParams())
SavedExnVar = CGF.CreateTempAlloca(CGF.Int8PtrTy, "finally.exn");
RethrowDest = CGF.getJumpDestInCurrentScope(CGF.getUnreachableBlock());
ForEHVar = CGF.CreateTempAlloca(CGF.Builder.getInt1Ty(), "finally.for-eh");
CGF.Builder.CreateStore(CGF.Builder.getFalse(), ForEHVar);
CGF.EHStack.pushCleanup<PerformFinally>(NormalCleanup, body,
ForEHVar, endCatchFn,
rethrowFn, SavedExnVar);
llvm::BasicBlock *catchBB = CGF.createBasicBlock("finally.catchall");
EHCatchScope *catchScope = CGF.EHStack.pushCatch(1);
catchScope->setCatchAllHandler(0, catchBB);
}
void CodeGenFunction::FinallyInfo::exit(CodeGenFunction &CGF) {
EHCatchScope &catchScope = cast<EHCatchScope>(*CGF.EHStack.begin());
llvm::BasicBlock *catchBB = catchScope.getHandler(0).Block;
CGF.popCatchScope();
if (catchBB->use_empty()) {
delete catchBB;
} else {
CGBuilderTy::InsertPoint savedIP = CGF.Builder.saveAndClearIP();
CGF.EmitBlock(catchBB);
llvm::Value *exn = 0;
if (BeginCatchFn) {
exn = CGF.getExceptionFromSlot();
CGF.Builder.CreateCall(BeginCatchFn, exn)->setDoesNotThrow();
}
if (SavedExnVar) {
if (!exn) exn = CGF.getExceptionFromSlot();
CGF.Builder.CreateStore(exn, SavedExnVar);
}
CGF.Builder.CreateStore(CGF.Builder.getTrue(), ForEHVar);
CGF.EmitBranchThroughCleanup(RethrowDest);
CGF.Builder.restoreIP(savedIP);
}
CGF.PopCleanupBlock();
}
llvm::BasicBlock *CodeGenFunction::getTerminateLandingPad() {
if (TerminateLandingPad)
return TerminateLandingPad;
CGBuilderTy::InsertPoint SavedIP = Builder.saveAndClearIP();
TerminateLandingPad = createBasicBlock("terminate.lpad");
Builder.SetInsertPoint(TerminateLandingPad);
const EHPersonality &Personality = EHPersonality::get(CGM.getLangOptions());
llvm::LandingPadInst *LPadInst =
Builder.CreateLandingPad(llvm::StructType::get(Int8PtrTy, Int32Ty, NULL),
getOpaquePersonalityFn(CGM, Personality), 0);
LPadInst->addClause(getCatchAllValue(*this));
llvm::CallInst *TerminateCall = Builder.CreateCall(getTerminateFn(*this));
TerminateCall->setDoesNotReturn();
TerminateCall->setDoesNotThrow();
Builder.CreateUnreachable();
Builder.restoreIP(SavedIP);
return TerminateLandingPad;
}
llvm::BasicBlock *CodeGenFunction::getTerminateHandler() {
if (TerminateHandler)
return TerminateHandler;
CGBuilderTy::InsertPoint SavedIP = Builder.saveAndClearIP();
TerminateHandler = createBasicBlock("terminate.handler");
Builder.SetInsertPoint(TerminateHandler);
llvm::CallInst *TerminateCall = Builder.CreateCall(getTerminateFn(*this));
TerminateCall->setDoesNotReturn();
TerminateCall->setDoesNotThrow();
Builder.CreateUnreachable();
Builder.restoreIP(SavedIP);
return TerminateHandler;
}
llvm::BasicBlock *CodeGenFunction::getEHResumeBlock() {
if (EHResumeBlock) return EHResumeBlock;
CGBuilderTy::InsertPoint SavedIP = Builder.saveIP();
EHResumeBlock = createBasicBlock("eh.resume");
Builder.SetInsertPoint(EHResumeBlock);
const EHPersonality &Personality = EHPersonality::get(CGM.getLangOptions());
const char *RethrowName = Personality.CatchallRethrowFn;
if (RethrowName != 0) {
Builder.CreateCall(getCatchallRethrowFn(*this, RethrowName),
getExceptionFromSlot())
->setDoesNotReturn();
} else {
switch (CleanupHackLevel) {
case CHL_MandatoryCatchall:
Builder.CreateCall(getUnwindResumeOrRethrowFn(),
getExceptionFromSlot())
->setDoesNotReturn();
break;
case CHL_MandatoryCleanup: {
llvm::Value *Exn = getExceptionFromSlot();
llvm::Value *Sel = getSelectorFromSlot();
llvm::Type *LPadType = llvm::StructType::get(Exn->getType(),
Sel->getType(), NULL);
llvm::Value *LPadVal = llvm::UndefValue::get(LPadType);
LPadVal = Builder.CreateInsertValue(LPadVal, Exn, 0, "lpad.val");
LPadVal = Builder.CreateInsertValue(LPadVal, Sel, 1, "lpad.val");
Builder.CreateResume(LPadVal);
Builder.restoreIP(SavedIP);
return EHResumeBlock;
}
case CHL_Ideal:
Builder.CreateCall(getUnwindResumeFn(), getExceptionFromSlot())
->setDoesNotReturn();
break;
}
}
Builder.CreateUnreachable();
Builder.restoreIP(SavedIP);
return EHResumeBlock;
}