r228222 - Implement IRGen for SEH __finally and AbnormalTermination

Reid Kleckner reid at kleckner.net
Wed Feb 4 14:37:07 PST 2015


Author: rnk
Date: Wed Feb  4 16:37:07 2015
New Revision: 228222

URL: http://llvm.org/viewvc/llvm-project?rev=228222&view=rev
Log:
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.

Modified:
    cfe/trunk/include/clang/Basic/Builtins.def
    cfe/trunk/lib/CodeGen/CGBuiltin.cpp
    cfe/trunk/lib/CodeGen/CGException.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.h
    cfe/trunk/test/CodeGen/exceptions-seh-finally.c
    cfe/trunk/test/CodeGen/exceptions-seh.c
    cfe/trunk/test/Sema/__try.c

Modified: cfe/trunk/include/clang/Basic/Builtins.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Builtins.def?rev=228222&r1=228221&r2=228222&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Builtins.def (original)
+++ cfe/trunk/include/clang/Basic/Builtins.def Wed Feb  4 16:37:07 2015
@@ -699,6 +699,8 @@ LANGBUILTIN(__exception_code, "ULi", "n"
 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)

Modified: cfe/trunk/lib/CodeGen/CGBuiltin.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGBuiltin.cpp?rev=228222&r1=228221&r2=228222&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGBuiltin.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGBuiltin.cpp Wed Feb  4 16:37:07 2015
@@ -1663,6 +1663,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(
   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};

Modified: cfe/trunk/lib/CodeGen/CGException.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGException.cpp?rev=228222&r1=228221&r2=228222&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGException.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGException.cpp Wed Feb  4 16:37:07 2015
@@ -451,6 +451,12 @@ llvm::Value *CodeGenFunction::getSelecto
   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(con
     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::EmitSEHExc
   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(co
   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());
 

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.cpp?rev=228222&r1=228221&r2=228222&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.cpp (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.cpp Wed Feb  4 16:37:07 2015
@@ -43,7 +43,8 @@ CodeGenFunction::CodeGenFunction(CodeGen
       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),

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=228222&r1=228221&r2=228222&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Wed Feb  4 16:37:07 2015
@@ -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);

Modified: cfe/trunk/test/CodeGen/exceptions-seh-finally.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/exceptions-seh-finally.c?rev=228222&r1=228221&r2=228222&view=diff
==============================================================================
--- cfe/trunk/test/CodeGen/exceptions-seh-finally.c (original)
+++ cfe/trunk/test/CodeGen/exceptions-seh-finally.c Wed Feb  4 16:37:07 2015
@@ -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: [[resumecont]]
+// CHECK: br label %[[ehresume:[^ ]*]]
 //
-// CHECK: landingpad
-// CHECK-NEXT: catch i8* null
-// CHECK: call void @abort()
-
-// FIXME: This crashes.
-#if 0
-void basic_finally(void) {
+// CHECK: [[ehresume]]
+// CHECK: resume { i8*, i32 }
+
+// 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 }

Modified: cfe/trunk/test/CodeGen/exceptions-seh.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/exceptions-seh.c?rev=228222&r1=228221&r2=228222&view=diff
==============================================================================
--- cfe/trunk/test/CodeGen/exceptions-seh.c (original)
+++ cfe/trunk/test/CodeGen/exceptions-seh.c Wed Feb  4 16:37:07 2015
@@ -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);

Modified: cfe/trunk/test/Sema/__try.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/__try.c?rev=228222&r1=228221&r2=228222&view=diff
==============================================================================
--- cfe/trunk/test/Sema/__try.c (original)
+++ cfe/trunk/test/Sema/__try.c Wed Feb  4 16:37:07 2015
@@ -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





More information about the cfe-commits mailing list