Hi majnemer,
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.
http://reviews.llvm.org/D8825
Files:
lib/CodeGen/CGException.cpp
lib/CodeGen/CodeGenFunction.cpp
lib/CodeGen/CodeGenFunction.h
test/CodeGen/exceptions-seh-finally.c
test/CodeGen/exceptions-seh.c
test/CodeGenCXX/exceptions-seh.cpp
EMAIL PREFERENCES
http://reviews.llvm.org/settings/panel/emailpreferences/
Index: lib/CodeGen/CGException.cpp
===================================================================
--- lib/CodeGen/CGException.cpp
+++ lib/CodeGen/CGException.cpp
@@ -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;
@@ -84,10 +86,15 @@
// This function must have prototype void(void*).
const char *CatchallRethrowFn;
- static const EHPersonality &get(CodeGenModule &CGM,
- const FunctionDecl *FD);
+ static const EHPersonality &get(CodeGenModule &CGM, bool UsingSEH = false);
static const EHPersonality &get(CodeGenFunction &CGF) {
- return get(CGF.CGM, dyn_cast_or_null<FunctionDecl>(CGF.CurCodeDecl));
+ return get(CGF.CGM, CGF.currentFunctionUsesSEHTry());
+ }
+
+ llvm::Type *getExceptionValueType(CodeGenModule &CGM) const {
+ if (this == &MSVC_except_handler || this == &MSVC_C_specific_handler)
+ return CGM.Int32Ty;
+ return CGM.Int8PtrTy;
}
static const EHPersonality GNU_C;
@@ -209,8 +216,7 @@
return EHPersonality::MSVC_C_specific_handler;
}
-const EHPersonality &EHPersonality::get(CodeGenModule &CGM,
- const FunctionDecl *FD) {
+const EHPersonality &EHPersonality::get(CodeGenModule &CGM, bool UsingSEH) {
const llvm::Triple &T = CGM.getTarget().getTriple();
const LangOptions &L = CGM.getLangOpts();
@@ -220,7 +226,7 @@
if (T.isWindowsMSVCEnvironment() && !L.ObjC1) {
if (L.SjLjExceptions)
return EHPersonality::GNU_CPlusPlus_SJLJ;
- else if (FD && FD->usesSEHTry())
+ else if (UsingSEH)
return getSEHPersonalityMSVC(T);
else
return EHPersonality::MSVC_CxxFrameHandler3;
@@ -310,7 +316,7 @@
if (!LangOpts.ObjCRuntime.isNeXTFamily())
return;
- const EHPersonality &ObjCXX = EHPersonality::get(*this, /*FD=*/nullptr);
+ const EHPersonality &ObjCXX = EHPersonality::get(*this);
const EHPersonality &CXX =
getCXXPersonality(getTarget().getTriple(), LangOpts);
if (&ObjCXX == &CXX)
@@ -387,8 +393,10 @@
}
llvm::Value *CodeGenFunction::getExceptionSlot() {
- if (!ExceptionSlot)
- ExceptionSlot = CreateTempAlloca(Int8PtrTy, "exn.slot");
+ if (!ExceptionSlot) {
+ llvm::Type *Ty = EHPersonality::get(*this).getExceptionValueType(CGM);
+ ExceptionSlot = CreateTempAlloca(Ty, "exn.slot");
+ }
return ExceptionSlot;
}
@@ -702,9 +710,9 @@
// Create and configure the landing pad.
llvm::BasicBlock *lpad = createBasicBlock("lpad");
EmitBlock(lpad);
-
+ llvm::Type *EHValTy = EHPersonality::get(*this).getExceptionValueType(CGM);
llvm::LandingPadInst *LPadInst =
- Builder.CreateLandingPad(llvm::StructType::get(Int8PtrTy, Int32Ty, nullptr),
+ Builder.CreateLandingPad(llvm::StructType::get(EHValTy, Int32Ty, nullptr),
getOpaquePersonalityFn(CGM, personality), 0);
llvm::Value *LPadExn = Builder.CreateExtractValue(LPadInst, 0);
@@ -1196,8 +1204,9 @@
// Tell the backend that this is a landing pad.
const EHPersonality &Personality = EHPersonality::get(*this);
+ llvm::Type *EHValTy = EHPersonality::get(*this).getExceptionValueType(CGM);
llvm::LandingPadInst *LPadInst =
- Builder.CreateLandingPad(llvm::StructType::get(Int8PtrTy, Int32Ty, nullptr),
+ Builder.CreateLandingPad(llvm::StructType::get(EHValTy, Int32Ty, nullptr),
getOpaquePersonalityFn(CGM, Personality), 0);
LPadInst->addClause(getCatchAllValue(*this));
@@ -1339,6 +1348,110 @@
};
}
+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 (Stmt::const_child_range CI = S->children(); CI; ++CI)
+ Visit(*CI);
+ }
+
+ 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.
@@ -1387,20 +1500,16 @@
Fn->setLinkage(llvm::GlobalValue::InternalLinkage);
}
+ UsesSEHTry = ParentCGF.UsesSEHTry;
+
StartFunction(GlobalDecl(), RetTy, Fn, FnInfo, Args,
FilterExpr->getLocStart(), FilterExpr->getLocStart());
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 +1519,6 @@
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;
}
@@ -1431,19 +1529,13 @@
// EXCEPTION_RECORD *ExceptionRecord;
// CONTEXT *ContextRecord;
// };
- // void *exn.slot =
- // (void *)(uintptr_t)exception_pointers->ExceptionRecord->ExceptionCode;
+ // int exn.slot = exception_pointers->ExceptionRecord->ExceptionCode;
llvm::Value *Ptrs = Builder.CreateLoad(GetAddrOfLocalVar(SEHPointersDecl));
llvm::Type *RecordTy = CGM.Int32Ty->getPointerTo();
- llvm::Type *PtrsTy = llvm::StructType::get(RecordTy, CGM.VoidPtrTy, nullptr);
- Ptrs = Builder.CreateBitCast(Ptrs, PtrsTy->getPointerTo());
- llvm::Value *Rec = Builder.CreateStructGEP(Ptrs, 0);
- Rec = Builder.CreateLoad(Rec);
+ llvm::Type *PtrsTy = RecordTy->getPointerTo();
+ Ptrs = Builder.CreateBitCast(Ptrs, PtrsTy);
+ llvm::Value *Rec = Builder.CreateLoad(Ptrs);
llvm::Value *Code = Builder.CreateLoad(Rec);
- Code = Builder.CreateZExt(Code, CGM.IntPtrTy);
- // FIXME: Change landing pads to produce {i32, i32} and make the exception
- // slot an i32.
- Code = Builder.CreateIntToPtr(Code, CGM.VoidPtrTy);
Builder.CreateStore(Code, getExceptionSlot());
}
@@ -1459,9 +1551,7 @@
// If we're in a landing pad or filter function, the exception slot contains
// the code.
assert(ExceptionSlot);
- llvm::Value *Code =
- Builder.CreatePtrToInt(getExceptionFromSlot(), CGM.IntPtrTy);
- return Builder.CreateTrunc(Code, CGM.Int32Ty);
+ return getExceptionFromSlot();
}
llvm::Value *CodeGenFunction::EmitSEHAbnormalTermination() {
Index: lib/CodeGen/CodeGenFunction.cpp
===================================================================
--- lib/CodeGen/CodeGenFunction.cpp
+++ lib/CodeGen/CodeGenFunction.cpp
@@ -39,8 +39,8 @@
CGBuilderInserterTy(this)),
CurFn(nullptr), CapturedStmtInfo(nullptr),
SanOpts(CGM.getLangOpts().Sanitize), IsSanitizerScope(false),
- CurFuncIsThunk(false), AutoreleaseResult(false), SawAsmBlock(false),
- BlockInfo(nullptr), BlockPointer(nullptr),
+ CurFuncIsThunk(false), UsesSEHTry(false), AutoreleaseResult(false),
+ SawAsmBlock(false), BlockInfo(nullptr), BlockPointer(nullptr),
LambdaThisCaptureField(nullptr), NormalCleanupDest(nullptr),
NextCleanupDestIndex(1), FirstBlockInfo(nullptr), EHResumeBlock(nullptr),
ExceptionSlot(nullptr), EHSelectorSlot(nullptr),
@@ -279,6 +279,20 @@
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;
@@ -601,6 +615,9 @@
}
} else if (!FD->hasAttr<AlwaysInlineAttr>())
Fn->addFnAttr(llvm::Attribute::NoInline);
+
+ // Track if the current function uses SEH try.
+ UsesSEHTry = FD->usesSEHTry();
}
if (getLangOpts().OpenCL) {
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -256,6 +256,9 @@
/// should emit cleanups.
bool CurFuncIsThunk;
+ /// If the current function or it's parent uses SEH __try.
+ bool UsesSEHTry;
+
/// In ARC, whether we should autorelease the return value.
bool AutoreleaseResult;
@@ -876,6 +879,10 @@
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;
@@ -1072,8 +1079,7 @@
}
bool currentFunctionUsesSEHTry() const {
- const auto *FD = dyn_cast_or_null<FunctionDecl>(CurCodeDecl);
- return FD && FD->usesSEHTry();
+ return UsesSEHTry;
}
const TargetInfo &getTarget() const { return Target; }
@@ -2005,6 +2011,12 @@
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);
Index: test/CodeGen/exceptions-seh-finally.c
===================================================================
--- test/CodeGen/exceptions-seh-finally.c
+++ test/CodeGen/exceptions-seh-finally.c
@@ -39,7 +39,7 @@
// CHECK: br label %[[ehresume:[^ ]*]]
//
// CHECK: [[ehresume]]
-// CHECK: resume { i8*, i32 }
+// CHECK: resume { i32, i32 }
// Mostly check that we don't double emit 'r' which would crash.
void decl_in_finally(void) {
@@ -117,7 +117,7 @@
// CHECK: br label %[[ehresume:[^ ]*]]
//
// CHECK: [[ehresume]]
-// CHECK: resume { i8*, i32 }
+// CHECK: resume { i32, i32 }
void noreturn_noop_finally() {
__try {
Index: test/CodeGen/exceptions-seh.c
===================================================================
--- test/CodeGen/exceptions-seh.c
+++ test/CodeGen/exceptions-seh.c
@@ -25,7 +25,7 @@
// CHECK: to label %{{.*}} unwind label %[[lpad:[^ ]*]]
//
// CHECK: [[lpad]]
-// CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
+// CHECK: landingpad { i32, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
// CHECK-NEXT: catch i8* null
// CHECK-NOT: br i1
// CHECK: br label %[[except:[^ ]*]]
@@ -89,10 +89,12 @@
// CHECK: br label %[[inner_try_cont:[^ ]*]]
//
// CHECK: [[lpad]]
-// CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
+// CHECK: landingpad { i32, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
// CHECK: catch i8* bitcast (i32 (i8*, i8*)* @"\01?filt$1@0@nested_try@@" to i8*)
// CHECK: catch i8* bitcast (i32 (i8*, i8*)* @"\01?filt$0@0@nested_try@@" to i8*)
-// CHECK: store i8* %{{.*}}, i8** %[[ehptr_slot:[^ ]*]]
+// CHECK: extractvalue { i32, i32 } %{{.*}}, 0
+// CHECK: store i32 %{{.*}}, i32* %[[ehptr_slot:[^ ]*]]
+// CHECK: extractvalue { i32, i32 } %{{.*}}, 1
// CHECK: store i32 %{{.*}}, i32* %[[sel_slot:[^ ]*]]
//
// CHECK: load i32, i32* %[[sel_slot]]
@@ -150,7 +152,7 @@
// CHECK: ret void
//
// CHECK: [[lpad]]
-// CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
+// CHECK: landingpad { i32, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
// CHECK-NEXT: cleanup
// CHECK: br label %[[finally]]
//
Index: test/CodeGenCXX/exceptions-seh.cpp
===================================================================
--- test/CodeGenCXX/exceptions-seh.cpp
+++ test/CodeGenCXX/exceptions-seh.cpp
@@ -65,7 +65,7 @@
// CHECK: br label %[[ret:[^ ]*]]
//
// CHECK: [[lpad]]
-// CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
+// CHECK: landingpad { i32, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
// CHECK-NEXT: catch i8*
//
// CHECK: br label %[[ret]]
@@ -93,6 +93,6 @@
// CHECK-LABEL: define internal void @"\01??R<lambda_0>@?use_seh_in_lambda@@YAXXZ@QEBAXXZ"(%class.anon* %this)
// CHECK: invoke void @might_throw() #[[NOINLINE]]
-// CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
+// CHECK: landingpad { i32, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
// CHECK: attributes #[[NOINLINE]] = { {{.*noinline.*}} }
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits