Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -5574,6 +5574,8 @@ "cannot use SEH '__try' in blocks, captured regions, or Obj-C method decls">; def err_mixing_cxx_try_seh_try : Error< "cannot use C++ 'try' in the same function as SEH '__try'">; +def err_seh_try_unsupported : Error< + "SEH '__try' is not supported on this target">; def note_conflicting_try_here : Note< "conflicting %0 here">; def warn_jump_out_of_seh_finally : Warning< Index: include/clang/Basic/TargetInfo.h =================================================================== --- include/clang/Basic/TargetInfo.h +++ include/clang/Basic/TargetInfo.h @@ -804,6 +804,11 @@ return TLSSupported; } + /// \brief Whether the target supports SEH __try. + bool isSEHTrySupported() const { + return getTriple().isOSWindows(); + } + /// \brief Return true if {|} are normal characters in the asm string. /// /// If this returns false (the default), then {abc|xyz} is syntax Index: lib/CodeGen/CGException.cpp =================================================================== --- lib/CodeGen/CGException.cpp +++ lib/CodeGen/CGException.cpp @@ -20,6 +20,7 @@ #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" #include "clang/AST/StmtVisitor.h" +#include "clang/Basic/TargetBuiltins.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/IntrinsicInst.h" @@ -1279,14 +1280,6 @@ } void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) { - // FIXME: Implement SEH on other architectures. - const llvm::Triple &T = CGM.getTarget().getTriple(); - if (T.getArch() != llvm::Triple::x86_64 || - !T.isKnownWindowsMSVCEnvironment()) { - ErrorUnsupported(&S, "__try statement"); - return; - } - EnterSEHTryStmt(S); { JumpDest TryExit = getJumpDestInCurrentScope("__try.__leave"); @@ -1311,25 +1304,39 @@ void Emit(CodeGenFunction &CGF, Flags F) override { ASTContext &Context = CGF.getContext(); - QualType ArgTys[2] = {Context.UnsignedCharTy, Context.VoidPtrTy}; - FunctionProtoType::ExtProtoInfo EPI; - const auto *FTP = cast( - Context.getFunctionType(Context.VoidTy, ArgTys, EPI)); + CodeGenModule &CGM = CGF.CGM; + // In 64-bit, we call the child function with arguments. In 32-bit, we store + // zero in the parent frame and use framerecover to check the value. + const CGFunctionInfo *FnInfo; CallArgList Args; - llvm::Value *IsForEH = - llvm::ConstantInt::get(CGF.ConvertType(ArgTys[0]), F.isForEHCleanup()); - Args.add(RValue::get(IsForEH), ArgTys[0]); + if (CGF.getTarget().getTriple().getArch() == llvm::Triple::x86_64) { + // Compute the two argument values. + QualType ArgTys[2] = {Context.UnsignedCharTy, Context.VoidPtrTy}; + llvm::Value *FrameAddr = CGM.getIntrinsic(llvm::Intrinsic::frameaddress); + llvm::Value *FP = + CGF.Builder.CreateCall(FrameAddr, {CGF.Builder.getInt32(0)}); + llvm::Value *IsForEH = + llvm::ConstantInt::get(CGF.ConvertType(ArgTys[0]), F.isForEHCleanup()); + Args.add(RValue::get(IsForEH), ArgTys[0]); + Args.add(RValue::get(FP), ArgTys[1]); + + // Arrange a two-arg function info and type. + FunctionProtoType::ExtProtoInfo EPI; + const auto *FPT = cast( + Context.getFunctionType(Context.VoidTy, ArgTys, EPI)); + FnInfo = &CGM.getTypes().arrangeFreeFunctionCall(Args, FPT, + /*chainCall=*/false); + } else { + // Emit the zero store if this is normal control flow. There are no + // explicit arguments. + if (F.isForNormalCleanup() && CGF.ChildAbnormalTerminationSlot) + CGF.Builder.CreateStore(CGF.Builder.getInt32(0), + CGF.ChildAbnormalTerminationSlot); + FnInfo = &CGM.getTypes().arrangeNullaryFunction(); + } - CodeGenModule &CGM = CGF.CGM; - llvm::Value *Zero = llvm::ConstantInt::get(CGM.Int32Ty, 0); - llvm::Value *FrameAddr = CGM.getIntrinsic(llvm::Intrinsic::frameaddress); - llvm::Value *FP = CGF.Builder.CreateCall(FrameAddr, Zero); - Args.add(RValue::get(FP), ArgTys[1]); - - const CGFunctionInfo &FnInfo = - CGM.getTypes().arrangeFreeFunctionCall(Args, FTP, /*chainCall=*/false); - CGF.EmitCall(FnInfo, OutlinedFinally, ReturnValueSlot(), Args); + CGF.EmitCall(*FnInfo, OutlinedFinally, ReturnValueSlot(), Args); } }; } @@ -1340,9 +1347,16 @@ CodeGenFunction &ParentCGF; const VarDecl *ParentThis; SmallVector Captures; + llvm::Value *AbnormalTermination = nullptr; + llvm::Value *SEHCodeSlot = nullptr; CaptureFinder(CodeGenFunction &ParentCGF, const VarDecl *ParentThis) : ParentCGF(ParentCGF), ParentThis(ParentThis) {} + // Return true if we need to do any capturing work. + bool foundCaptures() { + return !Captures.empty() || AbnormalTermination || SEHCodeSlot; + } + void Visit(const Stmt *S) { // See if this is a capture, then recurse. ConstStmtVisitor::Visit(S); @@ -1366,25 +1380,112 @@ void VisitCXXThisExpr(const CXXThisExpr *E) { Captures.push_back(ParentThis); } + + void VisitCallExpr(const CallExpr *E) { + // We only need to add parent frame allocations for these builtins in x86. + if (ParentCGF.getTarget().getTriple().getArch() != llvm::Triple::x86) + return; + + unsigned ID = E->getBuiltinCallee(); + switch (ID) { + case Builtin::BI__abnormal_termination: + case Builtin::BI_abnormal_termination: + // This is the simple case where we are the outermost finally. All we + // have to do here is make sure we escape this and recover it in the + // outlined handler. + if (!AbnormalTermination) + AbnormalTermination = ParentCGF.CreateMemTemp( + ParentCGF.getContext().IntTy, "abnormal_termination"); + break; + case Builtin::BI__exception_code: + case Builtin::BI_exception_code: + // This is the simple case where we are the outermost finally. All we + // have to do here is make sure we escape this and recover it in the + // outlined handler. + if (!SEHCodeSlot) + SEHCodeSlot = ParentCGF.SEHCodeSlotStack.back(); + break; + } + } }; } +llvm::Value *CodeGenFunction::recoverAddrOfEscapedLocal( + CodeGenFunction &ParentCGF, llvm::Value *ParentVar, llvm::Value *ParentFP) { + 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, ParentCGF.EscapedLocals.size())); + int FrameEscapeIdx = InsertPair.first->second; + // call i8* @llvm.framerecover(i8* bitcast(@parentFn), i8* %fp, i32 N) + llvm::Function *FrameRecoverFn = llvm::Intrinsic::getDeclaration( + &CGM.getModule(), llvm::Intrinsic::framerecover); + llvm::Constant *ParentI8Fn = + llvm::ConstantExpr::getBitCast(ParentCGF.CurFn, Int8PtrTy); + RecoverCall = Builder.CreateCall( + 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()); + return ChildVar; +} + void CodeGenFunction::EmitCapturedLocals(CodeGenFunction &ParentCGF, const Stmt *OutlinedStmt, - llvm::Value *ParentFP) { + bool IsFilter) { // 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()) + // We can exit early on x86_64 when there are no captures. + if (!Finder.foundCaptures() && + CGM.getTarget().getTriple().getArch() == llvm::Triple::x86_64) 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); + llvm::Value *EntryEBP = nullptr; + llvm::Value *ParentFP; + if (CGM.getTarget().getTriple().getArch() == llvm::Triple::x86_64) { + // On x64, the parent FP is passed as the second argument. + auto AI = CurFn->arg_begin(); + ++AI; + ParentFP = AI; + } else { + // The end of the EH registration is passed in as the EBP physical register. + // We can recover that with llvm.frameaddress(1), and adjust that to + // recover the parent's true frame pointer. + CGBuilderTy Builder(AllocaInsertPt); + EntryEBP = Builder.CreateCall( + CGM.getIntrinsic(llvm::Intrinsic::frameaddress), {Builder.getInt32(1)}); + llvm::Function *RecoverFPIntrin = + CGM.getIntrinsic(llvm::Intrinsic::x86_seh_recoverfp); + llvm::Constant *ParentI8Fn = + llvm::ConstantExpr::getBitCast(ParentCGF.CurFn, Int8PtrTy); + ParentFP = Builder.CreateCall(RecoverFPIntrin, {ParentI8Fn, EntryEBP}); + + // Inlining will break llvm.frameaddress(1), so disable it. + // FIXME: We could teach the inliner about the special meaning of + // frameaddress, framerecover, and frameescape to remove this limitation. + CurFn->addFnAttr(llvm::Attribute::NoInline); + } // Create llvm.framerecover calls for all captures. for (const VarDecl *VD : Finder.Captures) { @@ -1407,49 +1508,70 @@ continue; llvm::Value *ParentVar = I->second; - 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.CreateCall( - FrameRecoverFn, {ParentI8Fn, ParentFP, - llvm::ConstantInt::get(Int32Ty, FrameEscapeIdx)}); + LocalDeclMap[VD] = + recoverAddrOfEscapedLocal(ParentCGF, ParentVar, ParentFP); + } - } 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); - } + // The __abnormal_termination and __exception_code values are just more + // captures. + if (Finder.AbnormalTermination) { + AbnormalTerminationSlot = recoverAddrOfEscapedLocal( + ParentCGF, Finder.AbnormalTermination, ParentFP); + // Save the slot on the parent so it can store 1 and 0 to it. + ParentCGF.ChildAbnormalTerminationSlot = Finder.AbnormalTermination; + } - // 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; + if (Finder.SEHCodeSlot) { + SEHCodeSlotStack.push_back( + recoverAddrOfEscapedLocal(ParentCGF, Finder.SEHCodeSlot, ParentFP)); } + + if (IsFilter) + EmitSEHExceptionCodeSave(ParentCGF, ParentFP, EntryEBP); } /// Arrange a function prototype that can be called by Windows exception /// handling personalities. On Win64, the prototype looks like: /// RetTy func(void *EHPtrs, void *ParentFP); void CodeGenFunction::startOutlinedSEHHelper(CodeGenFunction &ParentCGF, - StringRef Name, QualType RetTy, - FunctionArgList &Args, + bool IsFilter, const Stmt *OutlinedStmt) { + SourceLocation StartLoc = OutlinedStmt->getLocStart(); + + // Get the mangled function name. + SmallString<128> Name; + { + llvm::raw_svector_ostream OS(Name); + const Decl *ParentCodeDecl = ParentCGF.CurCodeDecl; + const NamedDecl *Parent = dyn_cast_or_null(ParentCodeDecl); + assert(Parent && "FIXME: handle unnamed decls (lambdas, blocks) with SEH"); + MangleContext &Mangler = CGM.getCXXABI().getMangleContext(); + if (IsFilter) + Mangler.mangleSEHFilterExpression(Parent, OS); + else + Mangler.mangleSEHFinallyBlock(Parent, OS); + } + + FunctionArgList Args; + if (CGM.getTarget().getTriple().getArch() == llvm::Triple::x86_64) { + if (IsFilter) { + Args.push_back(ImplicitParamDecl::Create( + getContext(), nullptr, StartLoc, + &getContext().Idents.get("exception_pointers"), + getContext().VoidPtrTy)); + } else { + Args.push_back(ImplicitParamDecl::Create( + getContext(), nullptr, StartLoc, + &getContext().Idents.get("abnormal_termination"), + getContext().UnsignedCharTy)); + } + Args.push_back(ImplicitParamDecl::Create( + getContext(), nullptr, StartLoc, + &getContext().Idents.get("frame_pointer"), getContext().VoidPtrTy)); + } + + QualType RetTy = IsFilter ? getContext().LongTy : getContext().VoidTy; + llvm::Function *ParentFn = ParentCGF.CurFn; const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeFreeFunctionDeclaration( RetTy, Args, FunctionType::ExtInfo(), /*isVariadic=*/false); @@ -1474,10 +1596,7 @@ OutlinedStmt->getLocStart(), OutlinedStmt->getLocStart()); CGM.SetLLVMFunctionAttributes(nullptr, FnInfo, CurFn); - - auto AI = Fn->arg_begin(); - ++AI; - EmitCapturedLocals(ParentCGF, OutlinedStmt, &*AI); + EmitCapturedLocals(ParentCGF, OutlinedStmt, IsFilter); } /// Create a stub filter function that will ultimately hold the code of the @@ -1487,37 +1606,7 @@ CodeGenFunction::GenerateSEHFilterFunction(CodeGenFunction &ParentCGF, const SEHExceptStmt &Except) { const Expr *FilterExpr = Except.getFilterExpr(); - SourceLocation StartLoc = FilterExpr->getLocStart(); - - SEHPointersDecl = ImplicitParamDecl::Create( - getContext(), nullptr, StartLoc, - &getContext().Idents.get("exception_pointers"), getContext().VoidPtrTy); - FunctionArgList Args; - Args.push_back(SEHPointersDecl); - Args.push_back(ImplicitParamDecl::Create( - getContext(), nullptr, StartLoc, - &getContext().Idents.get("frame_pointer"), getContext().VoidPtrTy)); - - // Get the mangled function name. - SmallString<128> Name; - { - llvm::raw_svector_ostream OS(Name); - const Decl *ParentCodeDecl = ParentCGF.CurCodeDecl; - const NamedDecl *Parent = dyn_cast_or_null(ParentCodeDecl); - assert(Parent && "FIXME: handle unnamed decls (lambdas, blocks) with SEH"); - CGM.getCXXABI().getMangleContext().mangleSEHFilterExpression(Parent, OS); - } - - startOutlinedSEHHelper(ParentCGF, Name, getContext().LongTy, Args, - FilterExpr); - - // Mark finally block calls as nounwind and noinline to make LLVM's job a - // little easier. - // FIXME: Remove these restrictions in the future. - CurFn->addFnAttr(llvm::Attribute::NoUnwind); - CurFn->addFnAttr(llvm::Attribute::NoInline); - - EmitSEHExceptionCodeSave(); + startOutlinedSEHHelper(ParentCGF, true, FilterExpr); // Emit the original filter expression, convert to i32, and return. llvm::Value *R = EmitScalarExpr(FilterExpr); @@ -1534,29 +1623,13 @@ CodeGenFunction::GenerateSEHFinallyFunction(CodeGenFunction &ParentCGF, const SEHFinallyStmt &Finally) { const Stmt *FinallyBlock = Finally.getBlock(); - SourceLocation StartLoc = FinallyBlock->getLocStart(); + startOutlinedSEHHelper(ParentCGF, false, FinallyBlock); - FunctionArgList Args; - Args.push_back(ImplicitParamDecl::Create( - getContext(), nullptr, StartLoc, - &getContext().Idents.get("abnormal_termination"), - getContext().UnsignedCharTy)); - Args.push_back(ImplicitParamDecl::Create( - getContext(), nullptr, StartLoc, - &getContext().Idents.get("frame_pointer"), getContext().VoidPtrTy)); - - // Get the mangled function name. - SmallString<128> Name; - { - llvm::raw_svector_ostream OS(Name); - const Decl *ParentCodeDecl = ParentCGF.CurCodeDecl; - const NamedDecl *Parent = dyn_cast_or_null(ParentCodeDecl); - assert(Parent && "FIXME: handle unnamed decls (lambdas, blocks) with SEH"); - CGM.getCXXABI().getMangleContext().mangleSEHFinallyBlock(Parent, OS); - } - - startOutlinedSEHHelper(ParentCGF, Name, getContext().VoidTy, Args, - FinallyBlock); + // Mark finally block calls as nounwind and noinline to make LLVM's job a + // little easier. + // FIXME: Remove these restrictions in the future. + CurFn->addFnAttr(llvm::Attribute::NoUnwind); + CurFn->addFnAttr(llvm::Attribute::NoInline); // Emit the original filter expression, convert to i32, and return. EmitStmt(FinallyBlock); @@ -1566,47 +1639,63 @@ return CurFn; } -void CodeGenFunction::EmitSEHExceptionCodeSave() { +void CodeGenFunction::EmitSEHExceptionCodeSave(CodeGenFunction &ParentCGF, + llvm::Value *ParentFP, + llvm::Value *EntryEBP) { + // Get the pointer to the EXCEPTION_POINTERS struct. This is returned by the + // __exception_info intrinsic. + if (CGM.getTarget().getTriple().getArch() == llvm::Triple::x86_64) { + // On Win64, the info is passed as the first parameter to the filter. + auto AI = CurFn->arg_begin(); + SEHInfo = AI; + SEHCodeSlotStack.push_back( + CreateMemTemp(getContext().IntTy, "__exception_code")); + } else { + // On Win32, the EBP on entry to the filter points to the end of an + // exception registration object. It contains 6 32-bit fields, and the info + // pointer is stored in the second field. So, GEP 20 bytes backwards and + // load the pointer. + SEHInfo = Builder.CreateConstInBoundsGEP1_32(Int8Ty, EntryEBP, -20); + SEHInfo = Builder.CreateBitCast(SEHInfo, Int8PtrTy->getPointerTo()); + SEHInfo = Builder.CreateLoad(Int8PtrTy, SEHInfo); + SEHCodeSlotStack.push_back(recoverAddrOfEscapedLocal( + ParentCGF, ParentCGF.SEHCodeSlotStack.back(), ParentFP)); + } + // Save the exception code in the exception slot to unify exception access in // the filter function and the landing pad. // struct EXCEPTION_POINTERS { // EXCEPTION_RECORD *ExceptionRecord; // CONTEXT *ContextRecord; // }; - // void *exn.slot = - // (void *)(uintptr_t)exception_pointers->ExceptionRecord->ExceptionCode; - llvm::Value *Ptrs = Builder.CreateLoad(GetAddrOfLocalVar(SEHPointersDecl)); + // int exceptioncode = exception_pointers->ExceptionRecord->ExceptionCode; llvm::Type *RecordTy = CGM.Int32Ty->getPointerTo(); llvm::Type *PtrsTy = llvm::StructType::get(RecordTy, CGM.VoidPtrTy, nullptr); - Ptrs = Builder.CreateBitCast(Ptrs, PtrsTy->getPointerTo()); + llvm::Value *Ptrs = Builder.CreateBitCast(SEHInfo, PtrsTy->getPointerTo()); llvm::Value *Rec = Builder.CreateStructGEP(PtrsTy, Ptrs, 0); Rec = Builder.CreateLoad(Rec); 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()); + assert(!SEHCodeSlotStack.empty() && "emitting EH code outside of __except"); + Builder.CreateStore(Code, SEHCodeSlotStack.back()); } llvm::Value *CodeGenFunction::EmitSEHExceptionInfo() { // Sema should diagnose calling this builtin outside of a filter context, but // don't crash if we screw up. - if (!SEHPointersDecl) + if (!SEHInfo) return llvm::UndefValue::get(Int8PtrTy); - return Builder.CreateLoad(GetAddrOfLocalVar(SEHPointersDecl)); + assert(SEHInfo->getType() == Int8PtrTy); + return SEHInfo; } llvm::Value *CodeGenFunction::EmitSEHExceptionCode() { - // 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); + assert(!SEHCodeSlotStack.empty() && "emitting EH code outside of __except"); + return Builder.CreateLoad(Int32Ty, SEHCodeSlotStack.back()); } llvm::Value *CodeGenFunction::EmitSEHAbnormalTermination() { + if (CGM.getTarget().getTriple().getArch() != llvm::Triple::x86_64) + return Builder.CreateLoad(AbnormalTerminationSlot); // Abnormal termination is just the first parameter to the outlined finally // helper. auto AI = CurFn->arg_begin(); @@ -1616,9 +1705,15 @@ void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S) { CodeGenFunction HelperCGF(CGM, /*suppressNewContext=*/true); if (const SEHFinallyStmt *Finally = S.getFinallyHandler()) { - // Push a cleanup for __finally blocks. + // Outline the finally block. llvm::Function *FinallyFunc = HelperCGF.GenerateSEHFinallyFunction(*this, *Finally); + + // Store 1 to indicate abnormal termination if an exception is thrown. + if (ChildAbnormalTerminationSlot) + Builder.CreateStore(Builder.getInt32(1), ChildAbnormalTerminationSlot); + + // Push a cleanup for __finally blocks. EHStack.pushCleanup(NormalAndEHCleanup, FinallyFunc); return; } @@ -1627,12 +1722,16 @@ const SEHExceptStmt *Except = S.getExceptHandler(); assert(Except); EHCatchScope *CatchScope = EHStack.pushCatch(1); + SEHCodeSlotStack.push_back( + CreateMemTemp(getContext().IntTy, "__exception_code")); - // If the filter is known to evaluate to 1, then we can use the clause "catch - // i8* null". + // If the filter is known to evaluate to 1, then we can use the clause + // "catch i8* null". We can't do this on x86 because the filter has to save + // the exception code. llvm::Constant *C = CGM.EmitConstantExpr(Except->getFilterExpr(), getContext().IntTy, this); - if (C && C->isOneValue()) { + if (CGM.getTarget().getTriple().getArch() == llvm::Triple::x86_64 && C && + C->isOneValue()) { CatchScope->setCatchAllHandler(0, createBasicBlock("__except")); return; } @@ -1650,6 +1749,7 @@ // Just pop the cleanup if it's a __finally block. if (S.getFinallyHandler()) { PopCleanupBlock(); + ChildAbnormalTerminationSlot = nullptr; return; } @@ -1664,6 +1764,7 @@ if (!CatchScope.hasEHBranches()) { CatchScope.clearHandlerBlocks(); EHStack.popCatch(); + SEHCodeSlotStack.pop_back(); return; } @@ -1683,9 +1784,20 @@ EmitBlockAfterUses(ExceptBB); + // On Win64, the exception pointer is the exception code. Copy it to the slot. + if (CGM.getTarget().getTriple().getArch() == llvm::Triple::x86_64) { + llvm::Value *Code = + Builder.CreatePtrToInt(getExceptionFromSlot(), IntPtrTy); + Code = Builder.CreateTrunc(Code, Int32Ty); + Builder.CreateStore(Code, SEHCodeSlotStack.back()); + } + // Emit the __except body. EmitStmt(Except->getBlock()); + // End the lifetime of the exception code. + SEHCodeSlotStack.pop_back(); + if (HaveInsertPoint()) Builder.CreateBr(ContBB); Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -324,11 +324,21 @@ /// write the current selector value into this alloca. llvm::AllocaInst *EHSelectorSlot; - llvm::AllocaInst *AbnormalTerminationSlot; + /// Entering and leaving an SEH __try / __finally scope causes stores to this + /// slot. + llvm::Value *ChildAbnormalTerminationSlot = nullptr; - /// The implicit parameter to SEH filter functions of type - /// 'EXCEPTION_POINTERS*'. - ImplicitParamDecl *SEHPointersDecl; + /// The SEH __abnormal_termination() intrinsic lowers down to loads from this + /// slot from a parent function. + llvm::Value *AbnormalTerminationSlot = nullptr; + + /// A stack of exception code slots. Entering an __except block pushes a slot + /// on the stack and leaving pops one. The __exception_code() intrinsic loads + /// a value from the top of the stack. + SmallVector SEHCodeSlotStack; + + /// Value returned by __exception_info intrinsic. + llvm::Value *SEHInfo = nullptr; /// Emits a landing pad for the current EH stack. llvm::BasicBlock *EmitLandingPad(); @@ -2045,8 +2055,7 @@ void EnterSEHTryStmt(const SEHTryStmt &S); void ExitSEHTryStmt(const SEHTryStmt &S); - void startOutlinedSEHHelper(CodeGenFunction &ParentCGF, StringRef Name, - QualType RetTy, FunctionArgList &Args, + void startOutlinedSEHHelper(CodeGenFunction &ParentCGF, bool IsFilter, const Stmt *OutlinedStmt); llvm::Function *GenerateSEHFilterFunction(CodeGenFunction &ParentCGF, @@ -2055,7 +2064,9 @@ llvm::Function *GenerateSEHFinallyFunction(CodeGenFunction &ParentCGF, const SEHFinallyStmt &Finally); - void EmitSEHExceptionCodeSave(); + void EmitSEHExceptionCodeSave(CodeGenFunction &ParentCGF, + llvm::Value *ParentFP, + llvm::Value *EntryEBP); llvm::Value *EmitSEHExceptionCode(); llvm::Value *EmitSEHExceptionInfo(); llvm::Value *EmitSEHAbnormalTermination(); @@ -2064,7 +2075,16 @@ /// 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); + bool IsFilter); + + /// Recovers the address of a local in a parent function. ParentVar is the + /// address of the variable used in the immediate parent function. It can + /// either be an alloca or a call to llvm.framerecover if there are nested + /// outlined functions. ParentFP is the frame pointer of the outermost parent + /// frame. + llvm::Value *recoverAddrOfEscapedLocal(CodeGenFunction &ParentCGF, + llvm::Value *ParentVar, + llvm::Value *ParentFP); void EmitCXXForRangeStmt(const CXXForRangeStmt &S, ArrayRef Attrs = None); Index: lib/CodeGen/CodeGenFunction.cpp =================================================================== --- lib/CodeGen/CodeGenFunction.cpp +++ lib/CodeGen/CodeGenFunction.cpp @@ -45,12 +45,12 @@ LambdaThisCaptureField(nullptr), NormalCleanupDest(nullptr), NextCleanupDestIndex(1), FirstBlockInfo(nullptr), EHResumeBlock(nullptr), ExceptionSlot(nullptr), EHSelectorSlot(nullptr), - AbnormalTerminationSlot(nullptr), SEHPointersDecl(nullptr), - DebugInfo(CGM.getModuleDebugInfo()), DisableDebugInfo(false), - DidCallStackSave(false), IndirectBranch(nullptr), PGO(cgm), - SwitchInsn(nullptr), SwitchWeights(nullptr), CaseRangeBlock(nullptr), - UnreachableBlock(nullptr), NumReturnExprs(0), NumSimpleReturnExprs(0), - CXXABIThisDecl(nullptr), CXXABIThisValue(nullptr), CXXThisValue(nullptr), + DebugInfo(CGM.getModuleDebugInfo()), + DisableDebugInfo(false), DidCallStackSave(false), IndirectBranch(nullptr), + PGO(cgm), SwitchInsn(nullptr), SwitchWeights(nullptr), + CaseRangeBlock(nullptr), UnreachableBlock(nullptr), NumReturnExprs(0), + NumSimpleReturnExprs(0), CXXABIThisDecl(nullptr), + CXXABIThisValue(nullptr), CXXThisValue(nullptr), CXXDefaultInitExprThis(nullptr), CXXStructorImplicitParamDecl(nullptr), CXXStructorImplicitParamValue(nullptr), OutermostConditional(nullptr), CurLexicalScope(nullptr), TerminateLandingPad(nullptr), Index: lib/Sema/SemaStmt.cpp =================================================================== --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -25,6 +25,7 @@ #include "clang/AST/StmtObjC.h" #include "clang/AST/TypeLoc.h" #include "clang/AST/TypeOrdering.h" +#include "clang/Basic/TargetInfo.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" @@ -3649,6 +3650,10 @@ else Diag(TryLoc, diag::err_seh_try_outside_functions); + // Reject __try on unsupported targets. + if (!Context.getTargetInfo().isSEHTrySupported()) + Diag(TryLoc, diag::err_seh_try_unsupported); + return SEHTryStmt::Create(Context, IsCXXTry, TryLoc, TryBlock, Handler); } Index: test/CodeGen/exceptions-seh-finally.c =================================================================== --- test/CodeGen/exceptions-seh-finally.c +++ test/CodeGen/exceptions-seh-finally.c @@ -1,4 +1,7 @@ -// RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fms-extensions -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fms-extensions -emit-llvm -o - \ +// RUN: | FileCheck %s --check-prefix=CHECK --check-prefix=X64 +// RUN: %clang_cc1 %s -triple i686-pc-win32 -fms-extensions -emit-llvm -o - \ +// RUN: | FileCheck %s --check-prefix=CHECK --check-prefix=X86 void abort(void) __attribute__((noreturn)); void might_crash(void); @@ -17,18 +20,20 @@ // CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]] // // CHECK: [[invoke_cont]] -// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) -// CHECK: call void @"\01?fin$0@0@basic_finally@@"(i8 0, i8* %[[fp]]) +// X64: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) +// X64: call void @"\01?fin$0@0@basic_finally@@"(i8 0, i8* %[[fp]]) +// X86: call void @"\01?fin$0@0@basic_finally@@"() // CHECK-NEXT: ret void // // CHECK: [[lpad]] // CHECK-NEXT: landingpad // CHECK-NEXT: cleanup -// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) -// CHECK: call void @"\01?fin$0@0@basic_finally@@"(i8 1, i8* %[[fp]]) +// X64: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) +// X64: call void @"\01?fin$0@0@basic_finally@@"(i8 1, i8* %[[fp]]) +// X86: call void @"\01?fin$0@0@basic_finally@@"() // CHECK: resume { i8*, i32 } -// CHECK: define internal void @"\01?fin$0@0@basic_finally@@"(i8 %abnormal_termination, i8* %frame_pointer) +// CHECK: define internal void @"\01?fin$0@0@basic_finally@@"({{.*}}) // CHECK: call void @cleanup() // Mostly check that we don't double emit 'r' which would crash. @@ -57,11 +62,12 @@ // CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]] // // CHECK: [[invoke_cont]] -// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) -// CHECK: call void @"\01?fin$0@0@label_in_finally@@"(i8 0, i8* %[[fp]]) +// X64: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) +// X64: call void @"\01?fin$0@0@label_in_finally@@"(i8 0, i8* %[[fp]]) +// X86: call void @"\01?fin$0@0@label_in_finally@@"() // CHECK: ret void -// CHECK: define internal void @"\01?fin$0@0@label_in_finally@@"(i8 %abnormal_termination, i8* %frame_pointer) +// CHECK: define internal void @"\01?fin$0@0@label_in_finally@@"({{.*}}) // CHECK: br label %[[l:[^ ]*]] // // CHECK: [[l]] @@ -80,23 +86,33 @@ } // CHECK-LABEL: define void @use_abnormal_termination() +// X86: call void (...) @llvm.frameescape(i32* %[[abnormal_termination:[^ ),]*]]) +// X86: store i32 1, i32* %[[abnormal_termination]] // CHECK: invoke void @might_crash() // CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]] // // CHECK: [[invoke_cont]] -// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) -// CHECK: call void @"\01?fin$0@0@use_abnormal_termination@@"(i8 0, i8* %[[fp]]) +// X64: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) +// X64: call void @"\01?fin$0@0@use_abnormal_termination@@"(i8 0, i8* %[[fp]]) +// X86: store i32 0, i32* %[[abnormal_termination]] +// X86: call void @"\01?fin$0@0@use_abnormal_termination@@"() // CHECK: ret void // // CHECK: [[lpad]] // CHECK-NEXT: landingpad // CHECK-NEXT: cleanup -// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) -// CHECK: call void @"\01?fin$0@0@use_abnormal_termination@@"(i8 1, i8* %[[fp]]) +// X64: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) +// X64: call void @"\01?fin$0@0@use_abnormal_termination@@"(i8 1, i8* %[[fp]]) +// X86: call void @"\01?fin$0@0@use_abnormal_termination@@"() // CHECK: resume { i8*, i32 } -// CHECK: define internal void @"\01?fin$0@0@use_abnormal_termination@@"(i8 %abnormal_termination, i8* %frame_pointer) -// CHECK: %[[abnormal_zext:[^ ]*]] = zext i8 %abnormal_termination to i32 +// X64: define internal void @"\01?fin$0@0@use_abnormal_termination@@"(i8 %[[abnormal:abnormal_termination]], i8* %frame_pointer) +// X64: %[[abnormal_zext:[^ ]*]] = zext i8 %[[abnormal]] to i32 +// X86: define internal void @"\01?fin$0@0@use_abnormal_termination@@"() +// X86: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 1) +// X86: %[[abnormal_i8:[^ ]*]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @use_abnormal_termination to i8*), i8* %[[fp]], i32 0) +// X86: %[[abnormal:[^ ]*]] = bitcast i8* %[[abnormal_i8]] to i32* +// X86: %[[abnormal_zext:[^ ]*]] = load i32, i32* %[[abnormal]] // CHECK: store i32 %[[abnormal_zext]], i32* @crashed // CHECK-NEXT: ret void @@ -109,11 +125,10 @@ } // CHECK-LABEL: define void @noreturn_noop_finally() -// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) -// CHECK: call void @"\01?fin$0@0@noreturn_noop_finally@@"(i8 0, i8* %[[fp]]) +// CHECK: call void @"\01?fin$0@0@noreturn_noop_finally@@"({{.*}}) // CHECK: ret void -// CHECK: define internal void @"\01?fin$0@0@noreturn_noop_finally@@"(i8 %abnormal_termination, i8* %frame_pointer) +// CHECK: define internal void @"\01?fin$0@0@noreturn_noop_finally@@"({{.*}}) // CHECK: call void @abort() // CHECK: unreachable @@ -130,18 +145,16 @@ // CHECK: to label %[[cont:[^ ]*]] unwind label %[[lpad:[^ ]*]] // // CHECK: [[cont]] -// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) -// CHECK: call void @"\01?fin$0@0@noreturn_finally@@"(i8 0, i8* %[[fp]]) +// CHECK: call void @"\01?fin$0@0@noreturn_finally@@"({{.*}}) // CHECK: ret void // // CHECK: [[lpad]] // CHECK: landingpad // CHECK-NEXT: cleanup -// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) -// CHECK: call void @"\01?fin$0@0@noreturn_finally@@"(i8 1, i8* %[[fp]]) +// CHECK: call void @"\01?fin$0@0@noreturn_finally@@"({{.*}}) // CHECK: resume { i8*, i32 } -// CHECK: define internal void @"\01?fin$0@0@noreturn_finally@@"(i8 %abnormal_termination, i8* %frame_pointer) +// CHECK: define internal void @"\01?fin$0@0@noreturn_finally@@"({{.*}}) // CHECK: call void @abort() // CHECK: unreachable @@ -152,11 +165,10 @@ } } // CHECK-LABEL: define i32 @finally_with_return() -// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) -// CHECK-NEXT: call void @"\01?fin$0@0@finally_with_return@@"(i8 0, i8* %[[fp]]) +// CHECK: call void @"\01?fin$0@0@finally_with_return@@"({{.*}}) // CHECK-NEXT: ret i32 42 -// CHECK: define internal void @"\01?fin$0@0@finally_with_return@@"(i8 %abnormal_termination, i8* %frame_pointer) +// CHECK: define internal void @"\01?fin$0@0@finally_with_return@@"({{.*}}) // CHECK-NOT: br i1 // CHECK-NOT: br label // CHECK: ret void @@ -174,25 +186,22 @@ } // CHECK-LABEL: define i32 @nested___finally___finally -// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) -// CHECK: invoke void @"\01?fin$1@0@nested___finally___finally@@"(i8 0, i8* %[[fp]]) +// CHECK: invoke void @"\01?fin$1@0@nested___finally___finally@@"({{.*}}) // CHECK: to label %[[outercont:[^ ]*]] unwind label %[[lpad:[^ ]*]] // // CHECK: [[outercont]] -// CHECK-NEXT: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) -// CHECK-NEXT: call void @"\01?fin$0@0@nested___finally___finally@@"(i8 0, i8* %[[fp]]) +// CHECK: call void @"\01?fin$0@0@nested___finally___finally@@"({{.*}}) // CHECK-NEXT: ret i32 0 // // CHECK: [[lpad]] // CHECK-NEXT: landingpad // CHECK-NEXT: cleanup -// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) -// CHECK-NEXT: call void @"\01?fin$0@0@nested___finally___finally@@"(i8 1, i8* %[[fp]]) +// CHECK: call void @"\01?fin$0@0@nested___finally___finally@@"({{.*}}) -// CHECK-LABEL: define internal void @"\01?fin$0@0@nested___finally___finally@@"(i8 %abnormal_termination, i8* %frame_pointer) +// CHECK-LABEL: define internal void @"\01?fin$0@0@nested___finally___finally@@"({{.*}}) // CHECK: ret void -// CHECK-LABEL: define internal void @"\01?fin$1@0@nested___finally___finally@@"(i8 %abnormal_termination, i8* %frame_pointer) +// CHECK-LABEL: define internal void @"\01?fin$1@0@nested___finally___finally@@"({{.*}}) // CHECK: unreachable int nested___finally___finally_with_eh_edge() { @@ -212,31 +221,27 @@ // CHECK-NEXT: to label %[[invokecont:[^ ]*]] unwind label %[[lpad1:[^ ]*]] // // [[invokecont]] -// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) -// CHECK: invoke void @"\01?fin$1@0@nested___finally___finally_with_eh_edge@@"(i8 0, i8* %[[fp]]) +// CHECK: invoke void @"\01?fin$1@0@nested___finally___finally_with_eh_edge@@"({{.*}}) // CHECK: to label %[[outercont:[^ ]*]] unwind label %[[lpad2:[^ ]*]] // // CHECK: [[outercont]] -// CHECK-NEXT: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) -// CHECK-NEXT: call void @"\01?fin$0@0@nested___finally___finally_with_eh_edge@@"(i8 0, i8* %[[fp]]) +// CHECK: call void @"\01?fin$0@0@nested___finally___finally_with_eh_edge@@"({{.*}}) // CHECK-NEXT: ret i32 912 // // CHECK: [[lpad1]] // CHECK-NEXT: landingpad // CHECK-NEXT: cleanup -// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) -// CHECK: invoke void @"\01?fin$1@0@nested___finally___finally_with_eh_edge@@"(i8 1, i8* %[[fp]]) +// CHECK: invoke void @"\01?fin$1@0@nested___finally___finally_with_eh_edge@@"({{.*}}) // CHECK: to label %[[outercont:[^ ]*]] unwind label %[[lpad2]] // // CHECK: [[lpad2]] // CHECK-NEXT: landingpad // CHECK-NEXT: cleanup -// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) -// CHECK: call void @"\01?fin$0@0@nested___finally___finally_with_eh_edge@@"(i8 1, i8* %[[fp]]) +// CHECK: call void @"\01?fin$0@0@nested___finally___finally_with_eh_edge@@"({{.*}}) // CHECK: resume -// CHECK-LABEL: define internal void @"\01?fin$0@0@nested___finally___finally_with_eh_edge@@"(i8 %abnormal_termination, i8* %frame_pointer) +// CHECK-LABEL: define internal void @"\01?fin$0@0@nested___finally___finally_with_eh_edge@@"({{.*}}) // CHECK: ret void -// CHECK-LABEL: define internal void @"\01?fin$1@0@nested___finally___finally_with_eh_edge@@"(i8 %abnormal_termination, i8* %frame_pointer) +// CHECK-LABEL: define internal void @"\01?fin$1@0@nested___finally___finally_with_eh_edge@@"({{.*}}) // CHECK: unreachable Index: test/CodeGen/exceptions-seh-leave.c =================================================================== --- test/CodeGen/exceptions-seh-leave.c +++ test/CodeGen/exceptions-seh-leave.c @@ -205,7 +205,7 @@ // CHECK: br label %[[except:[^ ]*]] // CHECK: [[except]] -// CHECK-NEXT: invoke void @g() +// CHECK: invoke void @g() // CHECK-NEXT: to label %[[g2_cont:.*]] unwind label %[[g2_lpad:.*]] // CHECK: [[g2_cont]] @@ -216,7 +216,7 @@ // CHECK: br label %[[outerexcept:[^ ]*]] // CHECK: [[outerexcept]] -// CHECK-NEXT: br label %[[trycont4:[^ ]*]] +// CHECK: br label %[[trycont4:[^ ]*]] // CHECK: [[trycont4]] // CHECK-NEXT: ret i32 1 @@ -258,11 +258,11 @@ // CHECK: br label %[[except:[^ ]*]] // CHECK: [[except]] -// CHECK-NEXT: invoke void @g() +// CHECK: invoke void @g() // CHECK-NEXT: to label %[[g2_cont:.*]] unwind label %[[g2_lpad:.*]] // CHECK: [[g2_cont]] -// CHECK-NEXT: br label %[[tryleave:[^ ]*]] +// CHECK: br label %[[tryleave:[^ ]*]] // CHECK-NOT: 23 // CHECK: [[g2_lpad]] Index: test/CodeGen/exceptions-seh.c =================================================================== --- test/CodeGen/exceptions-seh.c +++ test/CodeGen/exceptions-seh.c @@ -1,4 +1,7 @@ -// RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fms-extensions -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fms-extensions -emit-llvm -o - \ +// RUN: | FileCheck %s --check-prefix=CHECK --check-prefix=X64 +// RUN: %clang_cc1 %s -triple i686-pc-win32 -fms-extensions -emit-llvm -o - \ +// RUN: | FileCheck %s --check-prefix=CHECK --check-prefix=X86 void try_body(int numerator, int denominator, int *myres) { *myres = numerator / denominator; @@ -19,20 +22,45 @@ *res = myres; return success; } -// CHECK-LABEL: define i32 @safe_div(i32 %numerator, i32 %denominator, i32* %res) {{.*}} personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) -// CHECK: invoke void @try_body(i32 %{{.*}}, i32 %{{.*}}, i32* %{{.*}}) #[[NOINLINE:[0-9]+]] -// CHECK: to label %{{.*}} unwind label %[[lpad:[^ ]*]] -// -// CHECK: [[lpad]] -// CHECK: landingpad { i8*, i32 } -// CHECK-NEXT: catch i8* null -// CHECK-NOT: br i1 -// CHECK: br label %[[except:[^ ]*]] -// CHECK: [[except]] -// CHECK-NEXT: store i32 -42, i32* %[[success:[^ ]*]] -// -// CHECK: %[[res:[^ ]*]] = load i32, i32* %[[success]] -// CHECK: ret i32 %[[res]] + +// X64-LABEL: define i32 @safe_div(i32 %numerator, i32 %denominator, i32* %res) {{.*}} personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) +// X64: invoke void @try_body(i32 %{{.*}}, i32 %{{.*}}, i32* %{{.*}}) #[[NOINLINE:[0-9]+]] +// X64: to label %{{.*}} unwind label %[[lpad:[^ ]*]] +// +// X64: [[lpad]] +// X64: landingpad { i8*, i32 } +// X64-NEXT: catch i8* null +// X64-NOT: br i1 +// X64: br label %[[except:[^ ]*]] +// X64: [[except]] +// X64: store i32 -42, i32* %[[success:[^ ]*]] +// +// X64: %[[res:[^ ]*]] = load i32, i32* %[[success]] +// X64: ret i32 %[[res]] + +// X86-LABEL: define i32 @safe_div(i32 %numerator, i32 %denominator, i32* %res) {{.*}} personality i8* bitcast (i32 (...)* @_except_handler3 to i8*) +// X86: invoke void @try_body(i32 %{{.*}}, i32 %{{.*}}, i32* %{{.*}}) #[[NOINLINE:[0-9]+]] +// X86: to label %{{.*}} unwind label %[[lpad:[^ ]*]] +// +// X86: [[lpad]] +// X86: landingpad { i8*, i32 } +// X86-NEXT: catch i8* bitcast (i32 ()* @"\01?filt$0@0@safe_div@@" to i8*) +// X86-NOT: br i1 +// X86: br label %[[except:[^ ]*]] +// X86: [[except]] +// X86: store i32 -42, i32* %[[success:[^ ]*]] +// +// X86: %[[res:[^ ]*]] = load i32, i32* %[[success]] +// X86: ret i32 %[[res]] + +// X86-LABEL: define internal i32 @"\01?filt$0@0@safe_div@@"() +// X86: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 1) +// X86: call i8* @llvm.framerecover(i8* bitcast (i32 (i32, i32, i32*)* @safe_div to i8*), i8* %[[fp]], i32 0) +// X86: call i8* @llvm.x86.seh.exceptioninfo(i8* bitcast (i32 (i32, i32, i32*)* @safe_div to i8*), i8* %[[fp]]) +// X86: load i32*, i32** +// X86: load i32, i32* +// X86: store i32 %{{.*}}, i32* +// X86: ret i32 1 void j(void); @@ -47,19 +75,25 @@ } // CHECK-LABEL: define i32 @filter_expr_capture() {{.*}} personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) -// CHECK: call void (...) @llvm.frameescape(i32* %[[r:[^ ,]*]]) +// X64: call void (...) @llvm.frameescape(i32* %[[r:[^ ,]*]]) +// X86: call void (...) @llvm.frameescape(i32* %[[r:[^ ,]*]], i32* %[[code:[^ ,]*]]) // CHECK: store i32 42, i32* %[[r]] // CHECK: invoke void @j() #[[NOINLINE]] // // CHECK: landingpad -// CHECK-NEXT: catch i8* bitcast (i32 (i8*, i8*)* @"\01?filt$0@0@filter_expr_capture@@" to i8*) +// CHECK-NEXT: catch i8* bitcast (i32 ({{.*}})* @"\01?filt$0@0@filter_expr_capture@@" to i8*) // CHECK: store i32 13, i32* %[[r]] // // CHECK: %[[rv:[^ ]*]] = load i32, i32* %[[r]] // CHECK: ret i32 %[[rv]] -// CHECK-LABEL: define internal i32 @"\01?filt$0@0@filter_expr_capture@@"(i8* %exception_pointers, i8* %frame_pointer) -// CHECK: call i8* @llvm.framerecover(i8* bitcast (i32 ()* @filter_expr_capture to i8*), i8* %frame_pointer, i32 0) +// X64-LABEL: define internal i32 @"\01?filt$0@0@filter_expr_capture@@"(i8* %exception_pointers, i8* %frame_pointer) +// X64: call i8* @llvm.framerecover(i8* bitcast (i32 ()* @filter_expr_capture to i8*), i8* %frame_pointer, i32 0) +// +// X86-LABEL: define internal i32 @"\01?filt$0@0@filter_expr_capture@@"() +// X86: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 1) +// X86: call i8* @llvm.framerecover(i8* bitcast (i32 ()* @filter_expr_capture to i8*), i8* %[[fp]], i32 0) +// // CHECK: store i32 -1, i32* %{{.*}} // CHECK: ret i32 -1 @@ -94,12 +128,12 @@ // CHECK: store i32 %{{.*}}, i32* %[[sel_slot:[^ ]*]] // // CHECK: load i32, i32* %[[sel_slot]] -// CHECK: call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @"\01?filt$1@0@nested_try@@" to i8*)) +// CHECK: call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ({{.*}})* @"\01?filt$1@0@nested_try@@" to i8*)) // CHECK: icmp eq i32 // CHECK: br i1 // // CHECK: load i32, i32* %[[sel_slot]] -// CHECK: call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @"\01?filt$0@0@nested_try@@" to i8*)) +// CHECK: call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ({{.*}})* @"\01?filt$0@0@nested_try@@" to i8*)) // CHECK: icmp eq i32 // CHECK: br i1 // @@ -115,6 +149,18 @@ // // CHECK: [[inner_try_cont]] // CHECK: br label %[[outer_try_cont]] +// +// CHECK-LABEL: define internal i32 @"\01?filt$0@0@nested_try@@"({{.*}}) +// X86: call i8* @llvm.x86.seh.exceptioninfo({{.*}}) +// CHECK: load i32*, i32** +// CHECK: load i32, i32* +// CHECK: icmp eq i32 %{{.*}}, 456 +// +// CHECK-LABEL: define internal i32 @"\01?filt$1@0@nested_try@@"({{.*}}) +// X86: call i8* @llvm.x86.seh.exceptioninfo({{.*}}) +// CHECK: load i32*, i32** +// CHECK: load i32, i32* +// CHECK: icmp eq i32 %{{.*}}, 123 static unsigned g = 0; void basic_finally(void) { @@ -134,18 +180,20 @@ // CHECK: to label %[[cont:[^ ]*]] unwind label %[[lpad:[^ ]*]] // // CHECK: [[cont]] -// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) -// CHECK: call void @"\01?fin$0@0@basic_finally@@"(i8 0, i8* %[[fp]]) +// X64: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) +// X64: call void @"\01?fin$0@0@basic_finally@@"(i8 0, i8* %[[fp]]) +// X86: call void @"\01?fin$0@0@basic_finally@@"() // CHECK: ret void // // CHECK: [[lpad]] // CHECK: landingpad { i8*, i32 } // CHECK-NEXT: cleanup -// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) -// CHECK: call void @"\01?fin$0@0@basic_finally@@"(i8 1, i8* %[[fp]]) +// X64: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0) +// X64: call void @"\01?fin$0@0@basic_finally@@"(i8 1, i8* %[[fp]]) +// X86: call void @"\01?fin$0@0@basic_finally@@"() // CHECK: resume -// CHECK: define internal void @"\01?fin$0@0@basic_finally@@"(i8 %abnormal_termination, i8* %frame_pointer) +// CHECK: define internal void @"\01?fin$0@0@basic_finally@@"({{.*}}) // CHECK: load i32, i32* @g, align 4 // CHECK: add i32 %{{.*}}, -1 // CHECK: store i32 %{{.*}}, i32* @g, align 4