]> granicus.if.org Git - clang/commitdiff
[WinEH] Mark calls inside cleanups as noinline
authorReid Kleckner <rnk@google.com>
Wed, 28 Oct 2015 23:06:42 +0000 (23:06 +0000)
committerReid Kleckner <rnk@google.com>
Wed, 28 Oct 2015 23:06:42 +0000 (23:06 +0000)
This works around PR25162. The MSVC tables make it very difficult to
correctly inline a C++ destructor that contains try / catch.  We've
attempted to address PR25162 in LLVM's backend, but it feels pretty
infeasible.  MSVC and ICC both appear to avoid inlining such complex
destructors.

Long term, we want to fix this by making the inliner smart enough to
know when it is inlining into a cleanup, so it can inline simple
destructors (~unique_ptr and ~vector) while avoiding destructors
containing try / catch.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@251576 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/CGCall.cpp
lib/CodeGen/CGCleanup.cpp
lib/CodeGen/CodeGenFunction.h
test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp

index 8a2006f18e797af397e9b9895bbee40807036202..05fbfa0e9ff362e0e0941a8b3b14eeef37fbd947 100644 (file)
@@ -3440,8 +3440,14 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
         Attrs.addAttribute(getLLVMContext(), llvm::AttributeSet::FunctionIndex,
                            llvm::Attribute::AlwaysInline);
 
-  // Disable inlining inside SEH __try blocks.
-  if (isSEHTryScope())
+  // Disable inlining inside SEH __try blocks and cleanup funclets. None of the
+  // funclet EH personalities that clang supports have tables that are
+  // expressive enough to describe catching an exception inside a cleanup.
+  // __CxxFrameHandler3, for example, will terminate the program without
+  // catching it.
+  // FIXME: Move this decision to the LLVM inliner. Before we can do that, the
+  // inliner needs to know if a given call site is part of a cleanuppad.
+  if (isSEHTryScope() || isCleanupPadScope())
     Attrs =
         Attrs.addAttribute(getLLVMContext(), llvm::AttributeSet::FunctionIndex,
                            llvm::Attribute::NoInline);
index 5217e24a52f6a15ce0d9fdaecb42b003ead3df2b..f1f6a8b8f32c73a0fd550be5138175e2ce3f7836 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "CGCleanup.h"
 #include "CodeGenFunction.h"
+#include "llvm/Support/SaveAndRestore.h"
 
 using namespace clang;
 using namespace CodeGen;
@@ -902,19 +903,19 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
 
     EmitBlock(EHEntry);
 
-    // Push terminate scopes around the potentially throwing destructor calls.
-    // We don't emit these when using funclets, because the runtime does it for
-    // us as part of unwinding out of a cleanuppad.
+    llvm::BasicBlock *NextAction = getEHDispatchBlock(EHParent);
+
+    // Push a terminate scope or cleanupendpad scope around the potentially
+    // throwing cleanups. For funclet EH personalities, the cleanupendpad models
+    // program termination when cleanups throw.
     bool PushedTerminate = false;
+    SaveAndRestore<bool> RestoreIsCleanupPadScope(IsCleanupPadScope);
+    llvm::CleanupPadInst *CPI = nullptr;
+    llvm::BasicBlock *CleanupEndBB = nullptr;
     if (!EHPersonality::get(*this).usesFuncletPads()) {
       EHStack.pushTerminate();
       PushedTerminate = true;
-    }
-
-    llvm::CleanupPadInst *CPI = nullptr;
-    llvm::BasicBlock *CleanupEndBB = nullptr;
-    llvm::BasicBlock *NextAction = getEHDispatchBlock(EHParent);
-    if (EHPersonality::get(*this).usesFuncletPads()) {
+    } else {
       CPI = Builder.CreateCleanupPad({});
 
       // Build a cleanupendpad to unwind through. Our insertion point should be
@@ -922,6 +923,10 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
       CleanupEndBB = createBasicBlock("ehcleanup.end");
       CGBuilderTy(*this, CleanupEndBB).CreateCleanupEndPad(CPI, NextAction);
       EHStack.pushPadEnd(CleanupEndBB);
+
+      // Mark that we're inside a cleanuppad to block inlining.
+      // FIXME: Remove this once the inliner knows when it's safe to do so.
+      IsCleanupPadScope = true;
     }
 
     // We only actually emit the cleanup code if the cleanup is either
index 5d5c7f50536ef92e03e3196c926ba58f901e703c..214fd45205a8e963abeb1022adafb41d7a77af70 100644 (file)
@@ -279,6 +279,8 @@ public:
   /// finally block or filter expression.
   bool IsOutlinedSEHHelper;
 
+  bool IsCleanupPadScope = false;
+
   const CodeGen::CGBlockInfo *BlockInfo;
   llvm::Value *BlockPointer;
 
@@ -372,6 +374,9 @@ public:
   /// Returns true inside SEH __try blocks.
   bool isSEHTryScope() const { return !SEHTryEpilogueStack.empty(); }
 
+  /// Returns true while emitting a cleanuppad.
+  bool isCleanupPadScope() const { return IsCleanupPadScope; }
+
   /// 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.
index 96d8ffe02feb4c7962e29748c35489d525aa8a82..c4a0d1ce649106226b64f60e904d370eb23574ad 100644 (file)
@@ -26,7 +26,8 @@ void HasEHCleanup() {
 // WIN32:   ret void
 //
 //    There should be one dtor call for unwinding from the second getA.
-// WIN32:   call x86_thiscallcc void @"\01??1A@@QAE@XZ"
+// WIN32:   cleanuppad
+// WIN32:   call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}}) #[[noinline:[0-9]+]]
 // WIN32-NOT: @"\01??1A@@QAE@XZ"
 // WIN32: }
 
@@ -54,14 +55,14 @@ int HasDeactivatedCleanups() {
 //
 // WIN32:   invoke i32 @"\01?TakesTwo@@YAHUA@@0@Z"([[argmem_ty]]* inalloca %[[argmem]])
 //        Destroy the two const ref temporaries.
-// WIN32:   call x86_thiscallcc void @"\01??1A@@QAE@XZ"
-// WIN32:   call x86_thiscallcc void @"\01??1A@@QAE@XZ"
+// WIN32:   call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}})
+// WIN32:   call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}})
 // WIN32:   ret i32
 //
 //        Conditionally destroy arg1.
 // WIN32:   %[[cond:.*]] = load i1, i1* %[[isactive]]
 // WIN32:   br i1 %[[cond]]
-// WIN32:   call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* %[[arg1]])
+// WIN32:   call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* %[[arg1]]) #[[noinline]]
 // WIN32: }
 
 // Test putting the cleanups inside a conditional.
@@ -84,7 +85,7 @@ int HasConditionalCleanup(bool cond) {
 // WIN32:   call i32 @"\01?CouldThrow@@YAHXZ"()
 //
 //        Only one dtor in the invoke for arg1
-// WIN32:   call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}})
+// WIN32:   call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}}) #[[noinline]]
 // WIN32-NOT: invoke x86_thiscallcc void @"\01??1A@@QAE@XZ"
 // WIN32: }
 
@@ -118,14 +119,14 @@ int HasConditionalDeactivatedCleanups(bool cond) {
 //        False condition.
 // WIN32:   invoke i32 @"\01?CouldThrow@@YAHXZ"()
 //        Two normal cleanups for TakeRef args.
-// WIN32:   call x86_thiscallcc void @"\01??1A@@QAE@XZ"
+// WIN32:   call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}})
 // WIN32-NOT:   invoke x86_thiscallcc void @"\01??1A@@QAE@XZ"
 // WIN32:   ret i32
 //
 //        Somewhere in the landing pad soup, we conditionally destroy arg1.
 // WIN32:   %[[isactive:.*]] = load i1, i1* %[[arg1_cond]]
 // WIN32:   br i1 %[[isactive]]
-// WIN32:   call x86_thiscallcc void @"\01??1A@@QAE@XZ"
+// WIN32:   call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}}) #[[noinline]]
 // WIN32: }
 
 namespace crash_on_partial_destroy {
@@ -163,7 +164,7 @@ C::C() { foo(); }
 // WIN32:      getelementptr inbounds i8, i8* %{{.*}}, i32 4
 // WIN32-NOT:  load
 // WIN32:      bitcast i8* %{{.*}} to %"struct.crash_on_partial_destroy::A"*
-// WIN32:      call x86_thiscallcc void @"\01??1A@crash_on_partial_destroy@@UAE@XZ"
+// WIN32:      call x86_thiscallcc void @"\01??1A@crash_on_partial_destroy@@UAE@XZ"({{.*}}) #[[noinline]]
 // WIN32: }
 }
 
@@ -186,7 +187,7 @@ void f() {
 //
 // WIN32: [[lpad]]
 // WIN32-NEXT: cleanuppad
-// WIN32: call x86_thiscallcc void @"\01??1C@dont_call_terminate@@QAE@XZ"({{.*}})
+// WIN32: call x86_thiscallcc void @"\01??1C@dont_call_terminate@@QAE@XZ"({{.*}}) #[[noinline]]
 }
 
 namespace noexcept_false_dtor {
@@ -203,6 +204,9 @@ void f() {
 // WIN32: invoke i32 @"\01?CouldThrow@@YAHXZ"()
 // WIN32: call x86_thiscallcc void @"\01??1D@noexcept_false_dtor@@QAE@XZ"(%"struct.noexcept_false_dtor::D"* %{{.*}})
 // WIN32: cleanuppad
-// WIN32: invoke x86_thiscallcc void @"\01??1D@noexcept_false_dtor@@QAE@XZ"(%"struct.noexcept_false_dtor::D"* %{{.*}})
+// WIN32: invoke x86_thiscallcc void @"\01??1D@noexcept_false_dtor@@QAE@XZ"(%"struct.noexcept_false_dtor::D"* %{{.*}}) #[[invoke_noinline:[0-9]+]]
 // WIN32: cleanupret
 // WIN32: cleanupendpad
+
+// WIN32: attributes #[[noinline]] = { noinline nounwind }
+// WIN32: attributes #[[invoke_noinline]] = { noinline }