r234447 - Reland "[SEH] Implement filter capturing in CodeGen"

Reid Kleckner reid at kleckner.net
Wed Apr 8 15:23:48 PDT 2015


Author: rnk
Date: Wed Apr  8 17:23:48 2015
New Revision: 234447

URL: http://llvm.org/viewvc/llvm-project?rev=234447&view=rev
Log:
Reland "[SEH] Implement filter capturing in CodeGen"

The test should be fixed. It was failing in NDEBUG builds due to a
missing '*' character in a regex. In asserts builds, the pattern matched
a single digit value, which became a double digit value in NDEBUG
builds. Go figure.

This reverts commit r234261.

Added:
    cfe/trunk/test/CodeGenCXX/exceptions-seh-filter-captures.cpp
Modified:
    cfe/trunk/lib/CodeGen/CGException.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.h

Modified: cfe/trunk/lib/CodeGen/CGException.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGException.cpp?rev=234447&r1=234446&r2=234447&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGException.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGException.cpp Wed Apr  8 17:23:48 2015
@@ -19,8 +19,10 @@
 #include "clang/AST/Mangle.h"
 #include "clang/AST/StmtCXX.h"
 #include "clang/AST/StmtObjC.h"
+#include "clang/AST/StmtVisitor.h"
 #include "llvm/IR/CallSite.h"
 #include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicInst.h"
 
 using namespace clang;
 using namespace CodeGen;
@@ -1339,6 +1341,110 @@ struct PerformSEHFinally : EHScopeStack:
 };
 }
 
+namespace {
+/// Find all local variable captures in the statement.
+struct CaptureFinder : ConstStmtVisitor<CaptureFinder> {
+  CodeGenFunction &ParentCGF;
+  const VarDecl *ParentThis;
+  SmallVector<const VarDecl *, 4> Captures;
+  CaptureFinder(CodeGenFunction &ParentCGF, const VarDecl *ParentThis)
+      : ParentCGF(ParentCGF), ParentThis(ParentThis) {}
+
+  void Visit(const Stmt *S) {
+    // See if this is a capture, then recurse.
+    ConstStmtVisitor<CaptureFinder>::Visit(S);
+    for (const Stmt *Child : S->children())
+      Visit(Child);
+  }
+
+  void VisitDeclRefExpr(const DeclRefExpr *E) {
+    // If this is already a capture, just make sure we capture 'this'.
+    if (E->refersToEnclosingVariableOrCapture()) {
+      Captures.push_back(ParentThis);
+      return;
+    }
+
+    const auto *D = dyn_cast<VarDecl>(E->getDecl());
+    if (D && D->isLocalVarDeclOrParm() && D->hasLocalStorage())
+      Captures.push_back(D);
+  }
+
+  void VisitCXXThisExpr(const CXXThisExpr *E) {
+    Captures.push_back(ParentThis);
+  }
+};
+}
+
+void CodeGenFunction::EmitCapturedLocals(CodeGenFunction &ParentCGF,
+                                         const Stmt *OutlinedStmt,
+                                         llvm::Value *ParentFP) {
+  // Find all captures in the Stmt.
+  CaptureFinder Finder(ParentCGF, ParentCGF.CXXABIThisDecl);
+  Finder.Visit(OutlinedStmt);
+
+  // Typically there are no captures and we can exit early.
+  if (Finder.Captures.empty())
+    return;
+
+  // Prepare the first two arguments to llvm.framerecover.
+  llvm::Function *FrameRecoverFn = llvm::Intrinsic::getDeclaration(
+      &CGM.getModule(), llvm::Intrinsic::framerecover);
+  llvm::Constant *ParentI8Fn =
+      llvm::ConstantExpr::getBitCast(ParentCGF.CurFn, Int8PtrTy);
+
+  // Create llvm.framerecover calls for all captures.
+  for (const VarDecl *VD : Finder.Captures) {
+    if (isa<ImplicitParamDecl>(VD)) {
+      CGM.ErrorUnsupported(VD, "'this' captured by SEH");
+      CXXThisValue = llvm::UndefValue::get(ConvertTypeForMem(VD->getType()));
+      continue;
+    }
+    if (VD->getType()->isVariablyModifiedType()) {
+      CGM.ErrorUnsupported(VD, "VLA captured by SEH");
+      continue;
+    }
+
+    assert((isa<ImplicitParamDecl>(VD) || VD->isLocalVarDeclOrParm()) &&
+           "captured non-local variable");
+
+    llvm::Value *ParentVar = ParentCGF.LocalDeclMap[VD];
+    assert(ParentVar && "capture was not a local decl");
+    llvm::CallInst *RecoverCall = nullptr;
+    CGBuilderTy Builder(AllocaInsertPt);
+    if (auto *ParentAlloca = dyn_cast<llvm::AllocaInst>(ParentVar)) {
+      // Mark the variable escaped if nobody else referenced it and compute the
+      // frameescape index.
+      auto InsertPair =
+          ParentCGF.EscapedLocals.insert(std::make_pair(ParentAlloca, -1));
+      if (InsertPair.second)
+        InsertPair.first->second = ParentCGF.EscapedLocals.size() - 1;
+      int FrameEscapeIdx = InsertPair.first->second;
+      // call i8* @llvm.framerecover(i8* bitcast(@parentFn), i8* %fp, i32 N)
+      RecoverCall =
+          Builder.CreateCall3(FrameRecoverFn, ParentI8Fn, ParentFP,
+                              llvm::ConstantInt::get(Int32Ty, FrameEscapeIdx));
+
+    } else {
+      // If the parent didn't have an alloca, we're doing some nested outlining.
+      // Just clone the existing framerecover call, but tweak the FP argument to
+      // use our FP value. All other arguments are constants.
+      auto *ParentRecover =
+          cast<llvm::IntrinsicInst>(ParentVar->stripPointerCasts());
+      assert(ParentRecover->getIntrinsicID() == llvm::Intrinsic::framerecover &&
+             "expected alloca or framerecover in parent LocalDeclMap");
+      RecoverCall = cast<llvm::CallInst>(ParentRecover->clone());
+      RecoverCall->setArgOperand(1, ParentFP);
+      RecoverCall->insertBefore(AllocaInsertPt);
+    }
+
+    // Bitcast the variable, rename it, and insert it in the local decl map.
+    llvm::Value *ChildVar =
+        Builder.CreateBitCast(RecoverCall, ParentVar->getType());
+    ChildVar->setName(ParentVar->getName());
+    LocalDeclMap[VD] = ChildVar;
+  }
+}
+
 /// Create a stub filter function that will ultimately hold the code of the
 /// filter expression. The EH preparation passes in LLVM will outline the code
 /// from the main function body into this stub.
@@ -1392,15 +1498,9 @@ CodeGenFunction::GenerateSEHFilterFuncti
 
   EmitSEHExceptionCodeSave();
 
-  // Insert dummy allocas for every local variable in scope. We'll initialize
-  // them and prune the unused ones after we find out which ones were
-  // referenced.
-  for (const auto &DeclPtrs : ParentCGF.LocalDeclMap) {
-    const Decl *VD = DeclPtrs.first;
-    llvm::Value *Ptr = DeclPtrs.second;
-    auto *ValTy = cast<llvm::PointerType>(Ptr->getType())->getElementType();
-    LocalDeclMap[VD] = CreateTempAlloca(ValTy, Ptr->getName() + ".filt");
-  }
+  auto AI = Fn->arg_begin();
+  ++AI;
+  EmitCapturedLocals(ParentCGF, FilterExpr, &*AI);
 
   // Emit the original filter expression, convert to i32, and return.
   llvm::Value *R = EmitScalarExpr(FilterExpr);
@@ -1410,17 +1510,6 @@ CodeGenFunction::GenerateSEHFilterFuncti
 
   FinishFunction(FilterExpr->getLocEnd());
 
-  for (const auto &DeclPtrs : ParentCGF.LocalDeclMap) {
-    const Decl *VD = DeclPtrs.first;
-    auto *Alloca = cast<llvm::AllocaInst>(LocalDeclMap[VD]);
-    if (Alloca->hasNUses(0)) {
-      Alloca->eraseFromParent();
-      continue;
-    }
-    ErrorUnsupported(FilterExpr,
-                     "SEH filter expression local variable capture");
-  }
-
   return Fn;
 }
 

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.cpp?rev=234447&r1=234446&r2=234447&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.cpp (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.cpp Wed Apr  8 17:23:48 2015
@@ -279,6 +279,20 @@ void CodeGenFunction::FinishFunction(Sou
     Builder.ClearInsertionPoint();
   }
 
+  // If some of our locals escaped, insert a call to llvm.frameescape in the
+  // entry block.
+  if (!EscapedLocals.empty()) {
+    // Invert the map from local to index into a simple vector. There should be
+    // no holes.
+    SmallVector<llvm::Value *, 4> EscapeArgs;
+    EscapeArgs.resize(EscapedLocals.size());
+    for (auto &Pair : EscapedLocals)
+      EscapeArgs[Pair.second] = Pair.first;
+    llvm::Function *FrameEscapeFn = llvm::Intrinsic::getDeclaration(
+        &CGM.getModule(), llvm::Intrinsic::frameescape);
+    CGBuilderTy(AllocaInsertPt).CreateCall(FrameEscapeFn, EscapeArgs);
+  }
+
   // Remove the AllocaInsertPt instruction, which is just a convenience for us.
   llvm::Instruction *Ptr = AllocaInsertPt;
   AllocaInsertPt = nullptr;

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=234447&r1=234446&r2=234447&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Wed Apr  8 17:23:48 2015
@@ -877,6 +877,10 @@ private:
   typedef llvm::DenseMap<const Decl*, llvm::Value*> DeclMapTy;
   DeclMapTy LocalDeclMap;
 
+  /// Track escaped local variables with auto storage. Used during SEH
+  /// outlining to produce a call to llvm.frameescape.
+  llvm::DenseMap<llvm::AllocaInst *, int> EscapedLocals;
+
   /// LabelMap - This keeps track of the LLVM basic block for each C label.
   llvm::DenseMap<const LabelDecl*, JumpDest> LabelMap;
 
@@ -2007,6 +2011,12 @@ public:
   llvm::Value *EmitSEHExceptionInfo();
   llvm::Value *EmitSEHAbnormalTermination();
 
+  /// Scan the outlined statement for captures from the parent function. For
+  /// each capture, mark the capture as escaped and emit a call to
+  /// llvm.framerecover. Insert the framerecover result into the LocalDeclMap.
+  void EmitCapturedLocals(CodeGenFunction &ParentCGF, const Stmt *OutlinedStmt,
+                          llvm::Value *ParentFP);
+
   void EmitCXXForRangeStmt(const CXXForRangeStmt &S,
                            ArrayRef<const Attr *> Attrs = None);
 

Added: cfe/trunk/test/CodeGenCXX/exceptions-seh-filter-captures.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/exceptions-seh-filter-captures.cpp?rev=234447&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/exceptions-seh-filter-captures.cpp (added)
+++ cfe/trunk/test/CodeGenCXX/exceptions-seh-filter-captures.cpp Wed Apr  8 17:23:48 2015
@@ -0,0 +1,80 @@
+// RUN: %clang_cc1 -std=c++11 -fblocks -fms-extensions %s -triple=x86_64-windows-msvc -emit-llvm \
+// RUN:         -o - -mconstructor-aliases -fcxx-exceptions -fexceptions | \
+// RUN:         FileCheck %s --check-prefix=CHECK --check-prefix=CXXEH
+
+extern "C" int basic_filter(int v, ...);
+extern "C" void might_crash();
+
+extern "C" void test_freefunc(int p1) {
+  int l1 = 13;
+  static int s1 = 42;
+  __try {
+    might_crash();
+  } __except(basic_filter(p1, l1, s1)) {
+  }
+}
+
+// CHECK-LABEL: define void @test_freefunc(i32 %p1)
+// CHECK: @llvm.frameescape(i32* %[[p1_ptr:[^, ]*]], i32* %[[l1_ptr:[^, ]*]])
+// CHECK: store i32 %p1, i32* %[[p1_ptr]], align 4
+// CHECK: store i32 13, i32* %[[l1_ptr]], align 4
+// CHECK: invoke void @might_crash()
+
+// CHECK-LABEL: define internal i32 @"\01?filt$0 at 0@test_freefunc@@"(i8* %exception_pointers, i8* %frame_pointer)
+// CHECK: %[[p1_i8:[^ ]*]] = call i8* @llvm.framerecover(i8* bitcast (void (i32)* @test_freefunc to i8*), i8* %frame_pointer, i32 0)
+// CHECK: %[[p1_ptr:[^ ]*]] = bitcast i8* %[[p1_i8]] to i32*
+// CHECK: %[[l1_i8:[^ ]*]] = call i8* @llvm.framerecover(i8* bitcast (void (i32)* @test_freefunc to i8*), i8* %frame_pointer, i32 1)
+// CHECK: %[[l1_ptr:[^ ]*]] = bitcast i8* %[[l1_i8]] to i32*
+// CHECK: %[[s1:[^ ]*]] = load i32, i32* @"\01?s1@?1??test_freefunc@@9 at 4HA", align 4
+// CHECK: %[[l1:[^ ]*]] = load i32, i32* %[[l1_ptr]]
+// CHECK: %[[p1:[^ ]*]] = load i32, i32* %[[p1_ptr]]
+// CHECK: call i32 (i32, ...)* @basic_filter(i32 %[[p1]], i32 %[[l1]], i32 %[[s1]])
+
+struct S {
+  int m1;
+  void test_method(void);
+};
+
+void S::test_method() {
+  int l1 = 13;
+  __try {
+    might_crash();
+  } __except(basic_filter(l1)) {
+    // FIXME: Test capturing 'this' and 'm1'.
+  }
+}
+
+// CHECK-LABEL: define void @"\01?test_method at S@@QEAAXXZ"(%struct.S* %this)
+// CHECK: @llvm.frameescape(i32* %[[l1_addr:[^, ]*]])
+// CHECK: store i32 13, i32* %[[l1_addr]], align 4
+// CHECK: invoke void @might_crash()
+
+// CHECK-LABEL: define internal i32 @"\01?filt$0 at 0@test_method at S@@"(i8* %exception_pointers, i8* %frame_pointer)
+// CHECK: %[[l1_i8:[^ ]*]] = call i8* @llvm.framerecover(i8* bitcast (void (%struct.S*)* @"\01?test_method at S@@QEAAXXZ" to i8*), i8* %frame_pointer, i32 0)
+// CHECK: %[[l1_ptr:[^ ]*]] = bitcast i8* %[[l1_i8]] to i32*
+// CHECK: %[[l1:[^ ]*]] = load i32, i32* %[[l1_ptr]]
+// CHECK: call i32 (i32, ...)* @basic_filter(i32 %[[l1]])
+
+void test_lambda() {
+  int l1 = 13;
+  auto lambda = [&]() {
+    int l2 = 42;
+    __try {
+      might_crash();
+    } __except(basic_filter(l2)) {
+      // FIXME: Test 'l1' when we can capture the lambda's 'this' decl.
+    }
+  };
+  lambda();
+}
+
+// CHECK-LABEL: define internal void @"\01??R<lambda_0>@?test_lambda@@YAXXZ at QEBAXXZ"(%class.anon* %this)
+// CHECK: @llvm.frameescape(i32* %[[l2_addr:[^, ]*]])
+// CHECK: store i32 42, i32* %[[l2_addr]], align 4
+// CHECK: invoke void @might_crash()
+
+// CHECK-LABEL: define internal i32 @"\01?filt$0 at 0@?R<lambda_0>@?test_lambda@@YAXXZ@"(i8* %exception_pointers, i8* %frame_pointer)
+// CHECK: %[[l2_i8:[^ ]*]] = call i8* @llvm.framerecover(i8* bitcast (void (%class.anon*)* @"\01??R<lambda_0>@?test_lambda@@YAXXZ at QEBAXXZ" to i8*), i8* %frame_pointer, i32 0)
+// CHECK: %[[l2_ptr:[^ ]*]] = bitcast i8* %[[l2_i8]] to i32*
+// CHECK: %[[l2:[^ ]*]] = load i32, i32* %[[l2_ptr]]
+// CHECK: call i32 (i32, ...)* @basic_filter(i32 %[[l2]])





More information about the cfe-commits mailing list