r234261 - [SEH] Implement filter capturing in CodeGen

Daniel Jasper djasper at google.com
Tue Apr 7 03:11:01 PDT 2015


Reverted in r234306 as the test fails:
http://lab.llvm.org:8080/green/job/clang-stage2-configure-Rlto_check/3182/

On Tue, Apr 7, 2015 at 1:51 AM, Reid Kleckner <reid at kleckner.net> wrote:

> Author: rnk
> Date: Mon Apr  6 18:51:44 2015
> New Revision: 234261
>
> URL: http://llvm.org/viewvc/llvm-project?rev=234261&view=rev
> Log:
> [SEH] Implement filter capturing in CodeGen
>
> While capturing filters aren't very common, we'd like to outline
> __finally blocks in the frontend to simplify -O0 EH preparation and
> reduce code size. Finally blocks are usually have captures, and this is
> the first step towards that.
>
> Currently we don't support capturing 'this' or VLAs.
>
> Reviewers: majnemer
>
> Differential Revision: http://reviews.llvm.org/D8825
>
> 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=234261&r1=234260&r2=234261&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/CodeGen/CGException.cpp (original)
> +++ cfe/trunk/lib/CodeGen/CGException.cpp Mon Apr  6 18:51:44 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=234261&r1=234260&r2=234261&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/CodeGen/CodeGenFunction.cpp (original)
> +++ cfe/trunk/lib/CodeGen/CodeGenFunction.cpp Mon Apr  6 18:51:44 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=234261&r1=234260&r2=234261&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
> +++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Mon Apr  6 18:51:44 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=234261&view=auto
>
> ==============================================================================
> --- cfe/trunk/test/CodeGenCXX/exceptions-seh-filter-captures.cpp (added)
> +++ cfe/trunk/test/CodeGenCXX/exceptions-seh-filter-captures.cpp Mon Apr
> 6 18:51:44 2015
> @@ -0,0 +1,81 @@
> +// 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]])
> +
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20150407/4f7e771d/attachment.html>


More information about the cfe-commits mailing list