<div dir="ltr">Reverted in r234306 as the test fails:<div><a href="http://lab.llvm.org:8080/green/job/clang-stage2-configure-Rlto_check/3182/">http://lab.llvm.org:8080/green/job/clang-stage2-configure-Rlto_check/3182/</a><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Apr 7, 2015 at 1:51 AM, Reid Kleckner <span dir="ltr"><<a href="mailto:reid@kleckner.net" target="_blank">reid@kleckner.net</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: rnk<br>
Date: Mon Apr  6 18:51:44 2015<br>
New Revision: 234261<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=234261&view=rev" target="_blank">http://llvm.org/viewvc/llvm-project?rev=234261&view=rev</a><br>
Log:<br>
[SEH] Implement filter capturing in CodeGen<br>
<br>
While capturing filters aren't very common, we'd like to outline<br>
__finally blocks in the frontend to simplify -O0 EH preparation and<br>
reduce code size. Finally blocks are usually have captures, and this is<br>
the first step towards that.<br>
<br>
Currently we don't support capturing 'this' or VLAs.<br>
<br>
Reviewers: majnemer<br>
<br>
Differential Revision: <a href="http://reviews.llvm.org/D8825" target="_blank">http://reviews.llvm.org/D8825</a><br>
<br>
Added:<br>
    cfe/trunk/test/CodeGenCXX/exceptions-seh-filter-captures.cpp<br>
Modified:<br>
    cfe/trunk/lib/CodeGen/CGException.cpp<br>
    cfe/trunk/lib/CodeGen/CodeGenFunction.cpp<br>
    cfe/trunk/lib/CodeGen/CodeGenFunction.h<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CGException.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGException.cpp?rev=234261&r1=234260&r2=234261&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGException.cpp?rev=234261&r1=234260&r2=234261&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CGException.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/CGException.cpp Mon Apr  6 18:51:44 2015<br>
@@ -19,8 +19,10 @@<br>
 #include "clang/AST/Mangle.h"<br>
 #include "clang/AST/StmtCXX.h"<br>
 #include "clang/AST/StmtObjC.h"<br>
+#include "clang/AST/StmtVisitor.h"<br>
 #include "llvm/IR/CallSite.h"<br>
 #include "llvm/IR/Intrinsics.h"<br>
+#include "llvm/IR/IntrinsicInst.h"<br>
<br>
 using namespace clang;<br>
 using namespace CodeGen;<br>
@@ -1339,6 +1341,110 @@ struct PerformSEHFinally : EHScopeStack:<br>
 };<br>
 }<br>
<br>
+namespace {<br>
+/// Find all local variable captures in the statement.<br>
+struct CaptureFinder : ConstStmtVisitor<CaptureFinder> {<br>
+  CodeGenFunction &ParentCGF;<br>
+  const VarDecl *ParentThis;<br>
+  SmallVector<const VarDecl *, 4> Captures;<br>
+  CaptureFinder(CodeGenFunction &ParentCGF, const VarDecl *ParentThis)<br>
+      : ParentCGF(ParentCGF), ParentThis(ParentThis) {}<br>
+<br>
+  void Visit(const Stmt *S) {<br>
+    // See if this is a capture, then recurse.<br>
+    ConstStmtVisitor<CaptureFinder>::Visit(S);<br>
+    for (const Stmt *Child : S->children())<br>
+      Visit(Child);<br>
+  }<br>
+<br>
+  void VisitDeclRefExpr(const DeclRefExpr *E) {<br>
+    // If this is already a capture, just make sure we capture 'this'.<br>
+    if (E->refersToEnclosingVariableOrCapture()) {<br>
+      Captures.push_back(ParentThis);<br>
+      return;<br>
+    }<br>
+<br>
+    const auto *D = dyn_cast<VarDecl>(E->getDecl());<br>
+    if (D && D->isLocalVarDeclOrParm() && D->hasLocalStorage())<br>
+      Captures.push_back(D);<br>
+  }<br>
+<br>
+  void VisitCXXThisExpr(const CXXThisExpr *E) {<br>
+    Captures.push_back(ParentThis);<br>
+  }<br>
+};<br>
+}<br>
+<br>
+void CodeGenFunction::EmitCapturedLocals(CodeGenFunction &ParentCGF,<br>
+                                         const Stmt *OutlinedStmt,<br>
+                                         llvm::Value *ParentFP) {<br>
+  // Find all captures in the Stmt.<br>
+  CaptureFinder Finder(ParentCGF, ParentCGF.CXXABIThisDecl);<br>
+  Finder.Visit(OutlinedStmt);<br>
+<br>
+  // Typically there are no captures and we can exit early.<br>
+  if (Finder.Captures.empty())<br>
+    return;<br>
+<br>
+  // Prepare the first two arguments to llvm.framerecover.<br>
+  llvm::Function *FrameRecoverFn = llvm::Intrinsic::getDeclaration(<br>
+      &CGM.getModule(), llvm::Intrinsic::framerecover);<br>
+  llvm::Constant *ParentI8Fn =<br>
+      llvm::ConstantExpr::getBitCast(ParentCGF.CurFn, Int8PtrTy);<br>
+<br>
+  // Create llvm.framerecover calls for all captures.<br>
+  for (const VarDecl *VD : Finder.Captures) {<br>
+    if (isa<ImplicitParamDecl>(VD)) {<br>
+      CGM.ErrorUnsupported(VD, "'this' captured by SEH");<br>
+      CXXThisValue = llvm::UndefValue::get(ConvertTypeForMem(VD->getType()));<br>
+      continue;<br>
+    }<br>
+    if (VD->getType()->isVariablyModifiedType()) {<br>
+      CGM.ErrorUnsupported(VD, "VLA captured by SEH");<br>
+      continue;<br>
+    }<br>
+<br>
+    assert((isa<ImplicitParamDecl>(VD) || VD->isLocalVarDeclOrParm()) &&<br>
+           "captured non-local variable");<br>
+<br>
+    llvm::Value *ParentVar = ParentCGF.LocalDeclMap[VD];<br>
+    assert(ParentVar && "capture was not a local decl");<br>
+    llvm::CallInst *RecoverCall = nullptr;<br>
+    CGBuilderTy Builder(AllocaInsertPt);<br>
+    if (auto *ParentAlloca = dyn_cast<llvm::AllocaInst>(ParentVar)) {<br>
+      // Mark the variable escaped if nobody else referenced it and compute the<br>
+      // frameescape index.<br>
+      auto InsertPair =<br>
+          ParentCGF.EscapedLocals.insert(std::make_pair(ParentAlloca, -1));<br>
+      if (InsertPair.second)<br>
+        InsertPair.first->second = ParentCGF.EscapedLocals.size() - 1;<br>
+      int FrameEscapeIdx = InsertPair.first->second;<br>
+      // call i8* @llvm.framerecover(i8* bitcast(@parentFn), i8* %fp, i32 N)<br>
+      RecoverCall =<br>
+          Builder.CreateCall3(FrameRecoverFn, ParentI8Fn, ParentFP,<br>
+                              llvm::ConstantInt::get(Int32Ty, FrameEscapeIdx));<br>
+<br>
+    } else {<br>
+      // If the parent didn't have an alloca, we're doing some nested outlining.<br>
+      // Just clone the existing framerecover call, but tweak the FP argument to<br>
+      // use our FP value. All other arguments are constants.<br>
+      auto *ParentRecover =<br>
+          cast<llvm::IntrinsicInst>(ParentVar->stripPointerCasts());<br>
+      assert(ParentRecover->getIntrinsicID() == llvm::Intrinsic::framerecover &&<br>
+             "expected alloca or framerecover in parent LocalDeclMap");<br>
+      RecoverCall = cast<llvm::CallInst>(ParentRecover->clone());<br>
+      RecoverCall->setArgOperand(1, ParentFP);<br>
+      RecoverCall->insertBefore(AllocaInsertPt);<br>
+    }<br>
+<br>
+    // Bitcast the variable, rename it, and insert it in the local decl map.<br>
+    llvm::Value *ChildVar =<br>
+        Builder.CreateBitCast(RecoverCall, ParentVar->getType());<br>
+    ChildVar->setName(ParentVar->getName());<br>
+    LocalDeclMap[VD] = ChildVar;<br>
+  }<br>
+}<br>
+<br>
 /// Create a stub filter function that will ultimately hold the code of the<br>
 /// filter expression. The EH preparation passes in LLVM will outline the code<br>
 /// from the main function body into this stub.<br>
@@ -1392,15 +1498,9 @@ CodeGenFunction::GenerateSEHFilterFuncti<br>
<br>
   EmitSEHExceptionCodeSave();<br>
<br>
-  // Insert dummy allocas for every local variable in scope. We'll initialize<br>
-  // them and prune the unused ones after we find out which ones were<br>
-  // referenced.<br>
-  for (const auto &DeclPtrs : ParentCGF.LocalDeclMap) {<br>
-    const Decl *VD = DeclPtrs.first;<br>
-    llvm::Value *Ptr = DeclPtrs.second;<br>
-    auto *ValTy = cast<llvm::PointerType>(Ptr->getType())->getElementType();<br>
-    LocalDeclMap[VD] = CreateTempAlloca(ValTy, Ptr->getName() + ".filt");<br>
-  }<br>
+  auto AI = Fn->arg_begin();<br>
+  ++AI;<br>
+  EmitCapturedLocals(ParentCGF, FilterExpr, &*AI);<br>
<br>
   // Emit the original filter expression, convert to i32, and return.<br>
   llvm::Value *R = EmitScalarExpr(FilterExpr);<br>
@@ -1410,17 +1510,6 @@ CodeGenFunction::GenerateSEHFilterFuncti<br>
<br>
   FinishFunction(FilterExpr->getLocEnd());<br>
<br>
-  for (const auto &DeclPtrs : ParentCGF.LocalDeclMap) {<br>
-    const Decl *VD = DeclPtrs.first;<br>
-    auto *Alloca = cast<llvm::AllocaInst>(LocalDeclMap[VD]);<br>
-    if (Alloca->hasNUses(0)) {<br>
-      Alloca->eraseFromParent();<br>
-      continue;<br>
-    }<br>
-    ErrorUnsupported(FilterExpr,<br>
-                     "SEH filter expression local variable capture");<br>
-  }<br>
-<br>
   return Fn;<br>
 }<br>
<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.cpp?rev=234261&r1=234260&r2=234261&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.cpp?rev=234261&r1=234260&r2=234261&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CodeGenFunction.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.cpp Mon Apr  6 18:51:44 2015<br>
@@ -279,6 +279,20 @@ void CodeGenFunction::FinishFunction(Sou<br>
     Builder.ClearInsertionPoint();<br>
   }<br>
<br>
+  // If some of our locals escaped, insert a call to llvm.frameescape in the<br>
+  // entry block.<br>
+  if (!EscapedLocals.empty()) {<br>
+    // Invert the map from local to index into a simple vector. There should be<br>
+    // no holes.<br>
+    SmallVector<llvm::Value *, 4> EscapeArgs;<br>
+    EscapeArgs.resize(EscapedLocals.size());<br>
+    for (auto &Pair : EscapedLocals)<br>
+      EscapeArgs[Pair.second] = Pair.first;<br>
+    llvm::Function *FrameEscapeFn = llvm::Intrinsic::getDeclaration(<br>
+        &CGM.getModule(), llvm::Intrinsic::frameescape);<br>
+    CGBuilderTy(AllocaInsertPt).CreateCall(FrameEscapeFn, EscapeArgs);<br>
+  }<br>
+<br>
   // Remove the AllocaInsertPt instruction, which is just a convenience for us.<br>
   llvm::Instruction *Ptr = AllocaInsertPt;<br>
   AllocaInsertPt = nullptr;<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=234261&r1=234260&r2=234261&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=234261&r1=234260&r2=234261&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)<br>
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Mon Apr  6 18:51:44 2015<br>
@@ -877,6 +877,10 @@ private:<br>
   typedef llvm::DenseMap<const Decl*, llvm::Value*> DeclMapTy;<br>
   DeclMapTy LocalDeclMap;<br>
<br>
+  /// Track escaped local variables with auto storage. Used during SEH<br>
+  /// outlining to produce a call to llvm.frameescape.<br>
+  llvm::DenseMap<llvm::AllocaInst *, int> EscapedLocals;<br>
+<br>
   /// LabelMap - This keeps track of the LLVM basic block for each C label.<br>
   llvm::DenseMap<const LabelDecl*, JumpDest> LabelMap;<br>
<br>
@@ -2007,6 +2011,12 @@ public:<br>
   llvm::Value *EmitSEHExceptionInfo();<br>
   llvm::Value *EmitSEHAbnormalTermination();<br>
<br>
+  /// Scan the outlined statement for captures from the parent function. For<br>
+  /// each capture, mark the capture as escaped and emit a call to<br>
+  /// llvm.framerecover. Insert the framerecover result into the LocalDeclMap.<br>
+  void EmitCapturedLocals(CodeGenFunction &ParentCGF, const Stmt *OutlinedStmt,<br>
+                          llvm::Value *ParentFP);<br>
+<br>
   void EmitCXXForRangeStmt(const CXXForRangeStmt &S,<br>
                            ArrayRef<const Attr *> Attrs = None);<br>
<br>
<br>
Added: cfe/trunk/test/CodeGenCXX/exceptions-seh-filter-captures.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/exceptions-seh-filter-captures.cpp?rev=234261&view=auto" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/exceptions-seh-filter-captures.cpp?rev=234261&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGenCXX/exceptions-seh-filter-captures.cpp (added)<br>
+++ cfe/trunk/test/CodeGenCXX/exceptions-seh-filter-captures.cpp Mon Apr  6 18:51:44 2015<br>
@@ -0,0 +1,81 @@<br>
+// RUN: %clang_cc1 -std=c++11 -fblocks -fms-extensions %s -triple=x86_64-windows-msvc -emit-llvm \<br>
+// RUN:         -o - -mconstructor-aliases -fcxx-exceptions -fexceptions | \<br>
+// RUN:         FileCheck %s --check-prefix=CHECK --check-prefix=CXXEH<br>
+<br>
+extern "C" int basic_filter(int v, ...);<br>
+extern "C" void might_crash();<br>
+<br>
+extern "C" void test_freefunc(int p1) {<br>
+  int l1 = 13;<br>
+  static int s1 = 42;<br>
+  __try {<br>
+    might_crash();<br>
+  } __except(basic_filter(p1, l1, s1)) {<br>
+  }<br>
+}<br>
+<br>
+// CHECK-LABEL: define void @test_freefunc(i32 %p1)<br>
+// CHECK: @llvm.frameescape(i32* %[[p1_ptr:[^, ]*]], i32* %[[l1_ptr:[^, ]*]])<br>
+// CHECK: store i32 %p1, i32* %[[p1_ptr]], align 4<br>
+// CHECK: store i32 13, i32* %[[l1_ptr]], align 4<br>
+// CHECK: invoke void @might_crash()<br>
+<br>
+// CHECK-LABEL: define internal i32 @"\01?filt$0@0@test_freefunc@@"(i8* %exception_pointers, i8* %frame_pointer)<br>
+// CHECK: %[[p1_i8:[^ ]*]] = call i8* @llvm.framerecover(i8* bitcast (void (i32)* @test_freefunc to i8*), i8* %frame_pointer, i32 0)<br>
+// CHECK: %[[p1_ptr:[^ ]*]] = bitcast i8* %[[p1_i8]] to i32*<br>
+// CHECK: %[[l1_i8:[^ ]*]] = call i8* @llvm.framerecover(i8* bitcast (void (i32)* @test_freefunc to i8*), i8* %frame_pointer, i32 1)<br>
+// CHECK: %[[l1_ptr:[^ ]*]] = bitcast i8* %[[l1_i8]] to i32*<br>
+// CHECK: %[[s1:[^ ]*]] = load i32, i32* @"\01?s1@?1??test_freefunc@@9@4HA", align 4<br>
+// CHECK: %[[l1:[^ ]*]] = load i32, i32* %[[l1_ptr]]<br>
+// CHECK: %[[p1:[^ ]*]] = load i32, i32* %[[p1_ptr]]<br>
+// CHECK: call i32 (i32, ...)* @basic_filter(i32 %[[p1]], i32 %[[l1]], i32 %[[s1]])<br>
+<br>
+struct S {<br>
+  int m1;<br>
+  void test_method(void);<br>
+};<br>
+<br>
+void S::test_method() {<br>
+  int l1 = 13;<br>
+  __try {<br>
+    might_crash();<br>
+  } __except(basic_filter(l1)) {<br>
+    // FIXME: Test capturing 'this' and 'm1'.<br>
+  }<br>
+}<br>
+<br>
+// CHECK-LABEL: define void @"\01?test_method@S@@QEAAXXZ"(%struct.S* %this)<br>
+// CHECK: @llvm.frameescape(i32* %[[l1_addr:[^, ]*]])<br>
+// CHECK: store i32 13, i32* %[[l1_addr]], align 4<br>
+// CHECK: invoke void @might_crash()<br>
+<br>
+// CHECK-LABEL: define internal i32 @"\01?filt$0@0@test_method@S@@"(i8* %exception_pointers, i8* %frame_pointer)<br>
+// CHECK: %[[l1_i8:[^ ]*]] = call i8* @llvm.framerecover(i8* bitcast (void (%struct.S*)* @"\01?test_method@S@@QEAAXXZ" to i8*), i8* %frame_pointer, i32 0)<br>
+// CHECK: %[[l1_ptr:[^ ]*]] = bitcast i8* %[[l1_i8]] to i32*<br>
+// CHECK: %[[l1:[^ ]]] = load i32, i32* %[[l1_ptr]]<br>
+// CHECK: call i32 (i32, ...)* @basic_filter(i32 %[[l1]])<br>
+<br>
+void test_lambda() {<br>
+  int l1 = 13;<br>
+  auto lambda = [&]() {<br>
+    int l2 = 42;<br>
+    __try {<br>
+      might_crash();<br>
+    } __except(basic_filter(l2)) {<br>
+      // FIXME: Test 'l1' when we can capture the lambda's 'this' decl.<br>
+    }<br>
+  };<br>
+  lambda();<br>
+}<br>
+<br>
+// CHECK-LABEL: define internal void @"\01??R<lambda_0>@?test_lambda@@YAXXZ@QEBAXXZ"(%class.anon* %this)<br>
+// CHECK: @llvm.frameescape(i32* %[[l2_addr:[^, ]*]])<br>
+// CHECK: store i32 42, i32* %[[l2_addr]], align 4<br>
+// CHECK: invoke void @might_crash()<br>
+<br>
+// CHECK-LABEL: define internal i32 @"\01?filt$0@0@?R<lambda_0>@?test_lambda@@YAXXZ@"(i8* %exception_pointers, i8* %frame_pointer)<br>
+// CHECK: %[[l2_i8:[^ ]*]] = call i8* @llvm.framerecover(i8* bitcast (void (%class.anon*)* @"\01??R<lambda_0>@?test_lambda@@YAXXZ@QEBAXXZ" to i8*), i8* %frame_pointer, i32 0)<br>
+// CHECK: %[[l2_ptr:[^ ]*]] = bitcast i8* %[[l2_i8]] to i32*<br>
+// CHECK: %[[l2:[^ ]]] = load i32, i32* %[[l2_ptr]]<br>
+// CHECK: call i32 (i32, ...)* @basic_filter(i32 %[[l2]])<br>
+<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@cs.uiuc.edu">cfe-commits@cs.uiuc.edu</a><br>
<a href="http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits" target="_blank">http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits</a><br>
</blockquote></div><br></div>