From b2c611a18bb8ea8d6e48fd03d89b771c60304e13 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Tue, 9 Jun 2015 17:47:50 +0000 Subject: [PATCH] Re-land r236052, "[SEH] Add 32-bit lowering code for __try" This reverts r236167. LLVM should be ready for this now. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@239415 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 2 + include/clang/Basic/TargetInfo.h | 5 + lib/CodeGen/CGException.cpp | 234 +++++++++++++-------- lib/CodeGen/CodeGenFunction.cpp | 13 +- lib/CodeGen/CodeGenFunction.h | 20 +- lib/Sema/SemaStmt.cpp | 5 + test/CodeGen/exceptions-seh-finally.c | 93 ++++---- test/CodeGen/exceptions-seh.c | 59 ++++-- 8 files changed, 278 insertions(+), 153 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index be1911e7d5..3741e0ea7e 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -5559,6 +5559,8 @@ def err_seh_try_outside_functions : Error< "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< diff --git a/include/clang/Basic/TargetInfo.h b/include/clang/Basic/TargetInfo.h index 8406205c7f..f126bb5342 100644 --- a/include/clang/Basic/TargetInfo.h +++ b/include/clang/Basic/TargetInfo.h @@ -797,6 +797,11 @@ public: 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 diff --git a/lib/CodeGen/CGException.cpp b/lib/CodeGen/CGException.cpp index d9a3f0b252..71c548dead 100644 --- a/lib/CodeGen/CGException.cpp +++ b/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" @@ -1274,14 +1275,6 @@ llvm::BasicBlock *CodeGenFunction::getEHResumeBlock(bool isCleanup) { } 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"); @@ -1306,25 +1299,39 @@ struct PerformSEHFinally : EHScopeStack::Cleanup { 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); } }; } @@ -1335,6 +1342,7 @@ struct CaptureFinder : ConstStmtVisitor { CodeGenFunction &ParentCGF; const VarDecl *ParentThis; SmallVector Captures; + llvm::Value *AbnormalTermination = nullptr; CaptureFinder(CodeGenFunction &ParentCGF, const VarDecl *ParentThis) : ParentCGF(ParentCGF), ParentThis(ParentThis) {} @@ -1361,25 +1369,93 @@ struct CaptureFinder : ConstStmtVisitor { 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; + } + } }; } +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) { + const Stmt *OutlinedStmt) { // 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()) + if (Finder.Captures.empty() && !Finder.AbnormalTermination) 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); + // The parent FP is passed in as EBP on x86 and the second argument on x64. + llvm::Value *ParentFP; + if (CGM.getTarget().getTriple().getArch() == llvm::Triple::x86_64) { + auto AI = CurFn->arg_begin(); + ++AI; + ParentFP = AI; + } else { + CGBuilderTy Builder(AllocaInsertPt); + ParentFP = Builder.CreateCall( + CGM.getIntrinsic(llvm::Intrinsic::frameaddress), {Builder.getInt32(1)}); + + // 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) { @@ -1402,39 +1478,16 @@ void CodeGenFunction::EmitCapturedLocals(CodeGenFunction &ParentCGF, 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)}); - - } 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); - } + LocalDeclMap[VD] = + recoverAddrOfEscapedLocal(ParentCGF, ParentVar, ParentFP); + } - // 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; + // AbnormalTermination is just another capture, but it has no Decl. + 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; } } @@ -1469,10 +1522,7 @@ void CodeGenFunction::startOutlinedSEHHelper(CodeGenFunction &ParentCGF, OutlinedStmt->getLocStart(), OutlinedStmt->getLocStart()); CGM.SetLLVMFunctionAttributes(nullptr, FnInfo, CurFn); - - auto AI = Fn->arg_begin(); - ++AI; - EmitCapturedLocals(ParentCGF, OutlinedStmt, &*AI); + EmitCapturedLocals(ParentCGF, OutlinedStmt); } /// Create a stub filter function that will ultimately hold the code of the @@ -1484,14 +1534,16 @@ CodeGenFunction::GenerateSEHFilterFunction(CodeGenFunction &ParentCGF, 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)); + if (CGM.getTarget().getTriple().getArch() == llvm::Triple::x86_64) { + SEHPointersDecl = ImplicitParamDecl::Create( + getContext(), nullptr, StartLoc, + &getContext().Idents.get("exception_pointers"), getContext().VoidPtrTy); + 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; @@ -1532,13 +1584,15 @@ CodeGenFunction::GenerateSEHFinallyFunction(CodeGenFunction &ParentCGF, SourceLocation StartLoc = FinallyBlock->getLocStart(); 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)); + if (CGM.getTarget().getTriple().getArch() == llvm::Triple::x86_64) { + 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; @@ -1570,7 +1624,7 @@ void CodeGenFunction::EmitSEHExceptionCodeSave() { // }; // void *exn.slot = // (void *)(uintptr_t)exception_pointers->ExceptionRecord->ExceptionCode; - llvm::Value *Ptrs = Builder.CreateLoad(GetAddrOfLocalVar(SEHPointersDecl)); + llvm::Value *Ptrs = EmitSEHExceptionInfo(); llvm::Type *RecordTy = CGM.Int32Ty->getPointerTo(); llvm::Type *PtrsTy = llvm::StructType::get(RecordTy, CGM.VoidPtrTy, nullptr); Ptrs = Builder.CreateBitCast(Ptrs, PtrsTy->getPointerTo()); @@ -1585,6 +1639,9 @@ void CodeGenFunction::EmitSEHExceptionCodeSave() { } llvm::Value *CodeGenFunction::EmitSEHExceptionInfo() { + if (CGM.getTarget().getTriple().getArch() != llvm::Triple::x86_64) + return Builder.CreateCall( + CGM.getIntrinsic(llvm::Intrinsic::eh_exceptioninfo), {}); // Sema should diagnose calling this builtin outside of a filter context, but // don't crash if we screw up. if (!SEHPointersDecl) @@ -1602,6 +1659,8 @@ llvm::Value *CodeGenFunction::EmitSEHExceptionCode() { } 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(); @@ -1611,9 +1670,15 @@ llvm::Value *CodeGenFunction::EmitSEHAbnormalTermination() { 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; } @@ -1645,6 +1710,7 @@ void CodeGenFunction::ExitSEHTryStmt(const SEHTryStmt &S) { // Just pop the cleanup if it's a __finally block. if (S.getFinallyHandler()) { PopCleanupBlock(); + ChildAbnormalTerminationSlot = nullptr; return; } diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index 01da750056..663e6a1a13 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -45,12 +45,13 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext) 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), + ChildAbnormalTerminationSlot(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), CXXDefaultInitExprThis(nullptr), CXXStructorImplicitParamDecl(nullptr), CXXStructorImplicitParamValue(nullptr), OutermostConditional(nullptr), CurLexicalScope(nullptr), TerminateLandingPad(nullptr), diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 469022dc62..cb8bfe39c3 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -310,7 +310,13 @@ public: /// 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; + + /// The SEH __abnormal_termination() intrinsic lowers down to loads from this + /// slot from a parent function. + llvm::Value *AbnormalTerminationSlot; /// The implicit parameter to SEH filter functions of type /// 'EXCEPTION_POINTERS*'. @@ -2039,8 +2045,16 @@ public: /// 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 EmitCapturedLocals(CodeGenFunction &ParentCGF, const Stmt *OutlinedStmt); + + /// 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); diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index 5c72529caa..a7c8798368 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/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" @@ -3648,6 +3649,10 @@ StmtResult Sema::ActOnSEHTryBlock(bool IsCXXTry, SourceLocation TryLoc, 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); } diff --git a/test/CodeGen/exceptions-seh-finally.c b/test/CodeGen/exceptions-seh-finally.c index 345d514611..6366c35039 100644 --- a/test/CodeGen/exceptions-seh-finally.c +++ b/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 @@ void basic_finally(void) { // 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 @@ l: // 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 @@ void use_abnormal_termination(void) { } // 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 @@ void noreturn_noop_finally() { } // 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 @@ void noreturn_finally() { // 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 @@ int finally_with_return() { } } // 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 @@ int nested___finally___finally() { } // 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 @@ int nested___finally___finally_with_eh_edge() { // 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 diff --git a/test/CodeGen/exceptions-seh.c b/test/CodeGen/exceptions-seh.c index d8135e805c..40796bc075 100644 --- a/test/CodeGen/exceptions-seh.c +++ b/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; @@ -24,7 +27,8 @@ int safe_div(int numerator, int denominator, int *res) { // CHECK: to label %{{.*}} unwind label %[[lpad:[^ ]*]] // // CHECK: [[lpad]] -// CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) +// X64: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) +// X86: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @_except_handler3 to i8*) // CHECK-NEXT: catch i8* null // CHECK-NOT: br i1 // CHECK: br label %[[except:[^ ]*]] @@ -52,14 +56,19 @@ int filter_expr_capture(void) { // 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 @@ -87,19 +96,20 @@ int nested_try(void) { // CHECK: br label %[[inner_try_cont:[^ ]*]] // // CHECK: [[lpad]] -// CHECK: landingpad { i8*, 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*) +// X64: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) +// X86: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @_except_handler3 to i8*) +// CHECK: catch i8* bitcast (i32 ({{.*}})* @"\01?filt$1@0@nested_try@@" to i8*) +// CHECK: catch i8* bitcast (i32 ({{.*}})* @"\01?filt$0@0@nested_try@@" to i8*) // CHECK: store i8* %{{.*}}, i8** %[[ehptr_slot:[^ ]*]] // 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 +125,20 @@ int nested_try(void) { // // 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.eh.exceptioninfo() +// CHECK: load i32*, i32** +// CHECK: load i32, i32* +// CHECK: ptrtoint +// CHECK: icmp eq i32 %{{.*}}, 456 +// +// CHECK-LABEL: define internal i32 @"\01?filt$1@0@nested_try@@"({{.*}}) +// X86: call i8* @llvm.eh.exceptioninfo() +// CHECK: load i32*, i32** +// CHECK: load i32, i32* +// CHECK: ptrtoint +// CHECK: icmp eq i32 %{{.*}}, 123 static unsigned g = 0; void basic_finally(void) { @@ -134,18 +158,21 @@ void basic_finally(void) { // 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 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) +// X64: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) +// X86: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @_except_handler3 to i8*) // 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 -- 2.40.0