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:
Reid Kleckner 2015-02-04 22:37:07 +00:00
parent ba77ad75d3
commit aca01db706
8 changed files with 196 additions and 28 deletions

View File

@ -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)

View File

@ -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};

View File

@ -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());

View File

@ -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),

View File

@ -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);

View File

@ -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 }

View File

@ -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);

View File

@ -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