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(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 { + CodeGenFunction &ParentCGF; + const VarDecl *ParentThis; + SmallVector 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::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(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(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(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(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(ParentVar->stripPointerCasts()); + assert(ParentRecover->getIntrinsicID() == llvm::Intrinsic::framerecover && + "expected alloca or framerecover in parent LocalDeclMap"); + RecoverCall = cast(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(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(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.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 DeclMapTy; DeclMapTy LocalDeclMap; + /// Track escaped local variables with auto storage. Used during SEH + /// outlining to produce a call to llvm.frameescape. + llvm::DenseMap EscapedLocals; + /// LabelMap - This keeps track of the LLVM basic block for each C label. llvm::DenseMap LabelMap; @@ -1072,8 +1079,7 @@ } bool currentFunctionUsesSEHTry() const { - const auto *FD = dyn_cast_or_null(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 Attrs = None); 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 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()) Fn->addFnAttr(llvm::Attribute::NoInline); + + // Track if the current function uses SEH try. + UsesSEHTry = FD->usesSEHTry(); } if (getLangOpts().OpenCL) { 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@?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.*}} }