forked from OSchip/llvm-project
Implement IRGen for SEH __finally and AbnormalTermination
Previously we would simply double-emit the body of the __finally block, but that doesn't work when it contains any kind of Decl, which we can't double emit. This fixes that by emitting the block once and branching into a shared code region and then branching back out. llvm-svn: 228222
This commit is contained in:
parent
ba77ad75d3
commit
aca01db706
|
@ -699,6 +699,8 @@ LANGBUILTIN(__exception_code, "ULi", "n", ALL_MS_LANGUAGES)
|
|||
LANGBUILTIN(_exception_code, "ULi", "n", ALL_MS_LANGUAGES)
|
||||
LANGBUILTIN(__exception_info, "v*", "n", ALL_MS_LANGUAGES)
|
||||
LANGBUILTIN(_exception_info, "v*", "n", ALL_MS_LANGUAGES)
|
||||
LANGBUILTIN(__abnormal_termination, "i", "n", ALL_MS_LANGUAGES)
|
||||
LANGBUILTIN(_abnormal_termination, "i", "n", ALL_MS_LANGUAGES)
|
||||
LANGBUILTIN(_InterlockedCompareExchange, "LiLiD*LiLi", "n", ALL_MS_LANGUAGES)
|
||||
LANGBUILTIN(_InterlockedCompareExchangePointer, "v*v*D*v*v*", "n", ALL_MS_LANGUAGES)
|
||||
LANGBUILTIN(_InterlockedDecrement, "LiLiD*", "n", ALL_MS_LANGUAGES)
|
||||
|
|
|
@ -1663,6 +1663,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
|
|||
case Builtin::BI__exception_info:
|
||||
case Builtin::BI_exception_info:
|
||||
return RValue::get(EmitSEHExceptionInfo());
|
||||
case Builtin::BI__abnormal_termination:
|
||||
case Builtin::BI_abnormal_termination:
|
||||
return RValue::get(EmitSEHAbnormalTermination());
|
||||
case Builtin::BI_setjmpex: {
|
||||
if (getTarget().getTriple().isOSMSVCRT()) {
|
||||
llvm::Type *ArgTypes[] = {Int8PtrTy, Int8PtrTy};
|
||||
|
|
|
@ -451,6 +451,12 @@ llvm::Value *CodeGenFunction::getSelectorFromSlot() {
|
|||
return Builder.CreateLoad(getEHSelectorSlot(), "sel");
|
||||
}
|
||||
|
||||
llvm::Value *CodeGenFunction::getAbnormalTerminationSlot() {
|
||||
if (!AbnormalTerminationSlot)
|
||||
AbnormalTerminationSlot = CreateTempAlloca(Int8Ty, "abnormal.termination.slot");
|
||||
return AbnormalTerminationSlot;
|
||||
}
|
||||
|
||||
void CodeGenFunction::EmitCXXThrowExpr(const CXXThrowExpr *E,
|
||||
bool KeepInsertionPoint) {
|
||||
if (!E->getSubExpr()) {
|
||||
|
@ -1686,18 +1692,45 @@ void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) {
|
|||
return;
|
||||
}
|
||||
|
||||
EnterSEHTryStmt(S);
|
||||
SEHFinallyInfo FI;
|
||||
EnterSEHTryStmt(S, FI);
|
||||
EmitStmt(S.getTryBlock());
|
||||
ExitSEHTryStmt(S);
|
||||
ExitSEHTryStmt(S, FI);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct PerformSEHFinally : EHScopeStack::Cleanup {
|
||||
Stmt *Block;
|
||||
PerformSEHFinally(Stmt *Block) : Block(Block) {}
|
||||
CodeGenFunction::SEHFinallyInfo *FI;
|
||||
PerformSEHFinally(CodeGenFunction::SEHFinallyInfo *FI) : FI(FI) {}
|
||||
|
||||
void Emit(CodeGenFunction &CGF, Flags F) override {
|
||||
// FIXME: Don't double-emit LabelDecls.
|
||||
CGF.EmitStmt(Block);
|
||||
// Cleanups are emitted at most twice: once for normal control flow and once
|
||||
// for exception control flow. Branch into the finally block, and remember
|
||||
// the continuation block so we can branch out later.
|
||||
if (!FI->FinallyBB) {
|
||||
FI->FinallyBB = CGF.createBasicBlock("__finally");
|
||||
FI->FinallyBB->insertInto(CGF.CurFn);
|
||||
FI->FinallyBB->moveAfter(CGF.Builder.GetInsertBlock());
|
||||
}
|
||||
|
||||
// Set the termination status and branch in.
|
||||
CGF.Builder.CreateStore(
|
||||
llvm::ConstantInt::get(CGF.Int8Ty, F.isForEHCleanup()),
|
||||
CGF.getAbnormalTerminationSlot());
|
||||
CGF.Builder.CreateBr(FI->FinallyBB);
|
||||
|
||||
// Create a continuation block for normal or exceptional control.
|
||||
if (F.isForEHCleanup()) {
|
||||
assert(!FI->ResumeBB && "double emission for EH");
|
||||
FI->ResumeBB = CGF.createBasicBlock("__finally.resume");
|
||||
CGF.EmitBlock(FI->ResumeBB);
|
||||
} else {
|
||||
assert(F.isForNormalCleanup() && !FI->ContBB && "double normal emission");
|
||||
FI->ContBB = CGF.createBasicBlock("__finally.cont");
|
||||
CGF.EmitBlock(FI->ContBB);
|
||||
// Try to keep source order.
|
||||
FI->ContBB->moveAfter(FI->FinallyBB);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1827,11 +1860,17 @@ llvm::Value *CodeGenFunction::EmitSEHExceptionCode() {
|
|||
return Builder.CreateTrunc(Code, CGM.Int32Ty);
|
||||
}
|
||||
|
||||
void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S) {
|
||||
llvm::Value *CodeGenFunction::EmitSEHAbnormalTermination() {
|
||||
// Load from the abnormal termination slot. It will be uninitialized outside
|
||||
// of __finally blocks, which we should warn or error on.
|
||||
llvm::Value *IsEH = Builder.CreateLoad(getAbnormalTerminationSlot());
|
||||
return Builder.CreateZExt(IsEH, Int32Ty);
|
||||
}
|
||||
|
||||
void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S, SEHFinallyInfo &FI) {
|
||||
if (SEHFinallyStmt *Finally = S.getFinallyHandler()) {
|
||||
// Push a cleanup for __finally blocks.
|
||||
EHStack.pushCleanup<PerformSEHFinally>(NormalAndEHCleanup,
|
||||
Finally->getBlock());
|
||||
EHStack.pushCleanup<PerformSEHFinally>(NormalAndEHCleanup, &FI);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1859,15 +1898,34 @@ void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S) {
|
|||
CatchScope->setHandler(0, OpaqueFunc, createBasicBlock("__except"));
|
||||
}
|
||||
|
||||
void CodeGenFunction::ExitSEHTryStmt(const SEHTryStmt &S) {
|
||||
void CodeGenFunction::ExitSEHTryStmt(const SEHTryStmt &S, SEHFinallyInfo &FI) {
|
||||
// Just pop the cleanup if it's a __finally block.
|
||||
if (S.getFinallyHandler()) {
|
||||
if (const SEHFinallyStmt *Finally = S.getFinallyHandler()) {
|
||||
PopCleanupBlock();
|
||||
|
||||
// Emit the code into FinallyBB.
|
||||
Builder.SetInsertPoint(FI.FinallyBB);
|
||||
EmitStmt(Finally->getBlock());
|
||||
|
||||
assert(FI.ContBB);
|
||||
if (FI.ResumeBB) {
|
||||
llvm::Value *IsEH = Builder.CreateLoad(getAbnormalTerminationSlot(),
|
||||
"abnormal.termination");
|
||||
IsEH = Builder.CreateICmpEQ(IsEH, llvm::ConstantInt::get(Int8Ty, 0));
|
||||
Builder.CreateCondBr(IsEH, FI.ContBB, FI.ResumeBB);
|
||||
} else {
|
||||
// There was nothing exceptional in the try body, so we only have normal
|
||||
// control flow.
|
||||
Builder.CreateBr(FI.ContBB);
|
||||
}
|
||||
|
||||
Builder.SetInsertPoint(FI.ContBB);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, we must have an __except block.
|
||||
SEHExceptStmt *Except = S.getExceptHandler();
|
||||
const SEHExceptStmt *Except = S.getExceptHandler();
|
||||
assert(Except && "__try must have __finally xor __except");
|
||||
EHCatchScope &CatchScope = cast<EHCatchScope>(*EHStack.begin());
|
||||
|
||||
|
|
|
@ -43,7 +43,8 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext)
|
|||
BlockInfo(nullptr), BlockPointer(nullptr),
|
||||
LambdaThisCaptureField(nullptr), NormalCleanupDest(nullptr),
|
||||
NextCleanupDestIndex(1), FirstBlockInfo(nullptr), EHResumeBlock(nullptr),
|
||||
ExceptionSlot(nullptr), EHSelectorSlot(nullptr), SEHPointersDecl(nullptr),
|
||||
ExceptionSlot(nullptr), EHSelectorSlot(nullptr),
|
||||
AbnormalTerminationSlot(nullptr), SEHPointersDecl(nullptr),
|
||||
DebugInfo(CGM.getModuleDebugInfo()), DisableDebugInfo(false),
|
||||
DidCallStackSave(false), IndirectBranch(nullptr), PGO(cgm),
|
||||
SwitchInsn(nullptr), SwitchWeights(nullptr), CaseRangeBlock(nullptr),
|
||||
|
|
|
@ -306,6 +306,8 @@ public:
|
|||
/// write the current selector value into this alloca.
|
||||
llvm::AllocaInst *EHSelectorSlot;
|
||||
|
||||
llvm::AllocaInst *AbnormalTerminationSlot;
|
||||
|
||||
/// The implicit parameter to SEH filter functions of type
|
||||
/// 'EXCEPTION_POINTERS*'.
|
||||
ImplicitParamDecl *SEHPointersDecl;
|
||||
|
@ -348,6 +350,17 @@ public:
|
|||
void exit(CodeGenFunction &CGF);
|
||||
};
|
||||
|
||||
/// Cleanups can be emitted for two reasons: normal control leaving a region
|
||||
/// exceptional control flow leaving a region.
|
||||
struct SEHFinallyInfo {
|
||||
SEHFinallyInfo()
|
||||
: FinallyBB(nullptr), ContBB(nullptr), ResumeBB(nullptr) {}
|
||||
|
||||
llvm::BasicBlock *FinallyBB;
|
||||
llvm::BasicBlock *ContBB;
|
||||
llvm::BasicBlock *ResumeBB;
|
||||
};
|
||||
|
||||
/// pushFullExprCleanup - Push a cleanup to be run at the end of the
|
||||
/// current full-expression. Safe against the possibility that
|
||||
/// we're currently inside a conditionally-evaluated expression.
|
||||
|
@ -1085,6 +1098,10 @@ public:
|
|||
llvm::Value *getExceptionSlot();
|
||||
llvm::Value *getEHSelectorSlot();
|
||||
|
||||
/// Stack slot that contains whether a __finally block is being executed as an
|
||||
/// EH cleanup or as a normal cleanup.
|
||||
llvm::Value *getAbnormalTerminationSlot();
|
||||
|
||||
/// Returns the contents of the function's exception object and selector
|
||||
/// slots.
|
||||
llvm::Value *getExceptionFromSlot();
|
||||
|
@ -2007,8 +2024,8 @@ public:
|
|||
void EmitCXXTryStmt(const CXXTryStmt &S);
|
||||
void EmitSEHTryStmt(const SEHTryStmt &S);
|
||||
void EmitSEHLeaveStmt(const SEHLeaveStmt &S);
|
||||
void EnterSEHTryStmt(const SEHTryStmt &S);
|
||||
void ExitSEHTryStmt(const SEHTryStmt &S);
|
||||
void EnterSEHTryStmt(const SEHTryStmt &S, SEHFinallyInfo &FI);
|
||||
void ExitSEHTryStmt(const SEHTryStmt &S, SEHFinallyInfo &FI);
|
||||
|
||||
llvm::Function *GenerateSEHFilterFunction(CodeGenFunction &ParentCGF,
|
||||
const SEHExceptStmt &Except);
|
||||
|
@ -2016,6 +2033,7 @@ public:
|
|||
void EmitSEHExceptionCodeSave();
|
||||
llvm::Value *EmitSEHExceptionCode();
|
||||
llvm::Value *EmitSEHExceptionInfo();
|
||||
llvm::Value *EmitSEHAbnormalTermination();
|
||||
|
||||
void EmitCXXForRangeStmt(const CXXForRangeStmt &S,
|
||||
ArrayRef<const Attr *> Attrs = None);
|
||||
|
|
|
@ -13,19 +13,44 @@ void basic_finally(void) {
|
|||
|
||||
// CHECK-LABEL: define void @basic_finally()
|
||||
// CHECK: invoke void @might_crash()
|
||||
// CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
|
||||
//
|
||||
// CHECK: [[invoke_cont]]
|
||||
// CHECK: store i8 0, i8* %[[abnormal:[^ ]*]]
|
||||
// CHECK: br label %[[finally:[^ ]*]]
|
||||
//
|
||||
// CHECK: [[finally]]
|
||||
// CHECK: call void @cleanup()
|
||||
// CHECK: load i8* %[[abnormal]]
|
||||
// CHECK: icmp eq
|
||||
// CHECK: br i1 %{{.*}}, label %[[finallycont:[^ ]*]], label %[[resumecont:[^ ]*]]
|
||||
//
|
||||
// CHECK: landingpad
|
||||
// CHECK: [[finallycont]]
|
||||
// CHECK-NEXT: ret void
|
||||
//
|
||||
// CHECK: [[lpad]]
|
||||
// CHECK-NEXT: landingpad
|
||||
// CHECK-NEXT: cleanup
|
||||
// CHECK: invoke void @cleanup()
|
||||
// CHECK: store i8 1, i8* %[[abnormal]]
|
||||
// CHECK: br label %[[finally]]
|
||||
//
|
||||
// CHECK: landingpad
|
||||
// CHECK-NEXT: catch i8* null
|
||||
// CHECK: call void @abort()
|
||||
// CHECK: [[resumecont]]
|
||||
// CHECK: br label %[[ehresume:[^ ]*]]
|
||||
//
|
||||
// CHECK: [[ehresume]]
|
||||
// CHECK: resume { i8*, i32 }
|
||||
|
||||
// FIXME: This crashes.
|
||||
#if 0
|
||||
void basic_finally(void) {
|
||||
// Mostly check that we don't double emit 'r' which would crash.
|
||||
void decl_in_finally(void) {
|
||||
__try {
|
||||
might_crash();
|
||||
} __finally {
|
||||
int r;
|
||||
}
|
||||
}
|
||||
|
||||
// Ditto, don't crash double emitting 'l'.
|
||||
void label_in_finally(void) {
|
||||
__try {
|
||||
might_crash();
|
||||
} __finally {
|
||||
|
@ -35,4 +60,60 @@ l:
|
|||
goto l;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// CHECK-LABEL: define void @label_in_finally()
|
||||
// CHECK: invoke void @might_crash()
|
||||
// CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
|
||||
//
|
||||
// CHECK: [[invoke_cont]]
|
||||
// CHECK: store i8 0, i8* %[[abnormal:[^ ]*]]
|
||||
// CHECK: br label %[[finally:[^ ]*]]
|
||||
//
|
||||
// CHECK: [[finally]]
|
||||
// CHECK: br label %[[l:[^ ]*]]
|
||||
//
|
||||
// CHECK: [[l]]
|
||||
// CHECK: call void @cleanup()
|
||||
// CHECK: call i32 @check_condition()
|
||||
// CHECK: br i1 {{.*}}, label
|
||||
// CHECK: br label %[[l]]
|
||||
|
||||
int crashed;
|
||||
void use_abnormal_termination(void) {
|
||||
__try {
|
||||
might_crash();
|
||||
} __finally {
|
||||
crashed = __abnormal_termination();
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define void @use_abnormal_termination()
|
||||
// CHECK: invoke void @might_crash()
|
||||
// CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
|
||||
//
|
||||
// CHECK: [[invoke_cont]]
|
||||
// CHECK: store i8 0, i8* %[[abnormal:[^ ]*]]
|
||||
// CHECK: br label %[[finally:[^ ]*]]
|
||||
//
|
||||
// CHECK: [[finally]]
|
||||
// CHECK: load i8* %[[abnormal]]
|
||||
// CHECK: zext i8 %{{.*}} to i32
|
||||
// CHECK: store i32 %{{.*}}, i32* @crashed
|
||||
// CHECK: load i8* %[[abnormal]]
|
||||
// CHECK: icmp eq
|
||||
// CHECK: br i1 %{{.*}}, label %[[finallycont:[^ ]*]], label %[[resumecont:[^ ]*]]
|
||||
//
|
||||
// CHECK: [[finallycont]]
|
||||
// CHECK-NEXT: ret void
|
||||
//
|
||||
// CHECK: [[lpad]]
|
||||
// CHECK-NEXT: landingpad
|
||||
// CHECK-NEXT: cleanup
|
||||
// CHECK: store i8 1, i8* %[[abnormal]]
|
||||
// CHECK: br label %[[finally]]
|
||||
//
|
||||
// CHECK: [[resumecont]]
|
||||
// CHECK: br label %[[ehresume:[^ ]*]]
|
||||
//
|
||||
// CHECK: [[ehresume]]
|
||||
// CHECK: resume { i8*, i32 }
|
||||
|
|
|
@ -138,17 +138,22 @@ void basic_finally(void) {
|
|||
// CHECK: to label %[[cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
|
||||
//
|
||||
// CHECK: [[cont]]
|
||||
// CHECK: br label %[[finally:[^ ]*]]
|
||||
//
|
||||
// CHECK: [[finally]]
|
||||
// CHECK: load i32* @g
|
||||
// CHECK: add i32 %{{.*}}, -1
|
||||
// CHECK: store i32 %{{.*}}, i32* @g
|
||||
// CHECK: icmp eq
|
||||
// CHECK: br i1 %{{.*}}, label
|
||||
//
|
||||
// CHECK: ret void
|
||||
//
|
||||
// CHECK: [[lpad]]
|
||||
// CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
|
||||
// CHECK-NEXT: cleanup
|
||||
// CHECK: load i32* @g
|
||||
// CHECK: add i32 %{{.*}}, -1
|
||||
// CHECK: store i32 %{{.*}}, i32* @g
|
||||
// CHECK: br label %[[finally]]
|
||||
//
|
||||
// CHECK: resume
|
||||
|
||||
int returns_int(void);
|
||||
|
|
|
@ -15,7 +15,7 @@ unsigned long __exception_code();
|
|||
#ifdef BORLAND
|
||||
struct EXCEPTION_INFO* __exception_info();
|
||||
#endif
|
||||
void __abnormal_termination();
|
||||
int __abnormal_termination();
|
||||
|
||||
#define GetExceptionCode __exception_code
|
||||
#define GetExceptionInformation __exception_info
|
||||
|
|
Loading…
Reference in New Issue