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. llvm-svn: 234447
This commit is contained in:
parent
ed6b5fc4b0
commit
31a1bb0c14
|
@ -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::Cleanup {
|
|||
};
|
||||
}
|
||||
|
||||
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::GenerateSEHFilterFunction(CodeGenFunction &ParentCGF,
|
|||
|
||||
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::GenerateSEHFilterFunction(CodeGenFunction &ParentCGF,
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -279,6 +279,20 @@ void CodeGenFunction::FinishFunction(SourceLocation EndLoc) {
|
|||
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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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@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]])
|
Loading…
Reference in New Issue