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 <[email protected]> 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@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@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@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@0@test_method@S@@"(i8* > %exception_pointers, i8* %frame_pointer) > +// CHECK: %[[l1_i8:[^ ]*]] = call i8* @llvm.framerecover(i8* bitcast > (void (%struct.S*)* @"\01?test_method@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@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@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@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 > [email protected] > http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits >
_______________________________________________ cfe-commits mailing list [email protected] http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
