diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 465f3f4e670c2..44a71b67e56bc 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -3982,63 +3982,41 @@ llvm::Value *CodeGenFunction::EmitCMSEClearRecord(llvm::Value *Src, return R; } -void CodeGenFunction::EmitFunctionEpilog( - const CGFunctionInfo &FI, bool EmitRetDbgLoc, SourceLocation EndLoc, - uint64_t RetKeyInstructionsSourceAtom) { - if (FI.isNoReturn()) { - // Noreturn functions don't return. - EmitUnreachable(EndLoc); - return; - } - - if (CurCodeDecl && CurCodeDecl->hasAttr()) { - // Naked functions don't have epilogues. - Builder.CreateUnreachable(); - return; - } - - // Functions with no result always return void. - if (!ReturnValue.isValid()) { - auto *I = Builder.CreateRetVoid(); - if (RetKeyInstructionsSourceAtom) - addInstToSpecificSourceAtom(I, nullptr, RetKeyInstructionsSourceAtom); - else - addInstToNewSourceAtom(I, nullptr); - return; - } - - llvm::DebugLoc RetDbgLoc; - llvm::Value *RV = nullptr; - QualType RetTy = FI.getReturnType(); +static void processFunctionReturnInfo(CodeGenFunction &CGF, + const CGFunctionInfo &FI, + bool EmitRetDbgLoc, SourceLocation EndLoc, + QualType RetTy, llvm::Value *&RV, + llvm::DebugLoc &RetDbgLoc) { const ABIArgInfo &RetAI = FI.getReturnInfo(); switch (RetAI.getKind()) { case ABIArgInfo::InAlloca: // Aggregates get evaluated directly into the destination. Sometimes we // need to return the sret value in a register, though. - assert(hasAggregateEvaluationKind(RetTy)); + assert(CodeGenFunction::hasAggregateEvaluationKind(RetTy)); if (RetAI.getInAllocaSRet()) { - llvm::Function::arg_iterator EI = CurFn->arg_end(); + llvm::Function::arg_iterator EI = CGF.CurFn->arg_end(); --EI; llvm::Value *ArgStruct = &*EI; - llvm::Value *SRet = Builder.CreateStructGEP( + llvm::Value *SRet = CGF.Builder.CreateStructGEP( FI.getArgStruct(), ArgStruct, RetAI.getInAllocaFieldIndex()); llvm::Type *Ty = cast(SRet)->getResultElementType(); - RV = Builder.CreateAlignedLoad(Ty, SRet, getPointerAlign(), "sret"); + RV = CGF.Builder.CreateAlignedLoad(Ty, SRet, CGF.getPointerAlign(), + "sret"); } break; case ABIArgInfo::Indirect: { - auto AI = CurFn->arg_begin(); + auto AI = CGF.CurFn->arg_begin(); if (RetAI.isSRetAfterThis()) ++AI; - switch (getEvaluationKind(RetTy)) { + switch (CodeGenFunction::getEvaluationKind(RetTy)) { case TEK_Complex: { - ComplexPairTy RT = - EmitLoadOfComplex(MakeAddrLValue(ReturnValue, RetTy), EndLoc); - EmitStoreOfComplex(RT, MakeNaturalAlignAddrLValue(&*AI, RetTy), - /*isInit*/ true); + CodeGenFunction::ComplexPairTy RT = CGF.EmitLoadOfComplex( + CGF.MakeAddrLValue(CGF.ReturnValue, RetTy), EndLoc); + CGF.EmitStoreOfComplex(RT, CGF.MakeNaturalAlignAddrLValue(&*AI, RetTy), + /*isInit*/ true); break; } case TEK_Aggregate: @@ -4048,13 +4026,14 @@ void CodeGenFunction::EmitFunctionEpilog( LValueBaseInfo BaseInfo; TBAAAccessInfo TBAAInfo; CharUnits Alignment = - CGM.getNaturalTypeAlignment(RetTy, &BaseInfo, &TBAAInfo); - Address ArgAddr(&*AI, ConvertType(RetTy), Alignment); - LValue ArgVal = - LValue::MakeAddr(ArgAddr, RetTy, getContext(), BaseInfo, TBAAInfo); - EmitStoreOfScalar( - EmitLoadOfScalar(MakeAddrLValue(ReturnValue, RetTy), EndLoc), ArgVal, - /*isInit*/ true); + CGF.CGM.getNaturalTypeAlignment(RetTy, &BaseInfo, &TBAAInfo); + Address ArgAddr(&*AI, CGF.ConvertType(RetTy), Alignment); + LValue ArgVal = LValue::MakeAddr(ArgAddr, RetTy, CGF.getContext(), + BaseInfo, TBAAInfo); + CGF.EmitStoreOfScalar( + CGF.EmitLoadOfScalar(CGF.MakeAddrLValue(CGF.ReturnValue, RetTy), + EndLoc), + ArgVal, /*isInit*/ true); break; } } @@ -4063,37 +4042,37 @@ void CodeGenFunction::EmitFunctionEpilog( case ABIArgInfo::Extend: case ABIArgInfo::Direct: - if (RetAI.getCoerceToType() == ConvertType(RetTy) && + if (RetAI.getCoerceToType() == CGF.ConvertType(RetTy) && RetAI.getDirectOffset() == 0) { // The internal return value temp always will have pointer-to-return-type // type, just do a load. // If there is a dominating store to ReturnValue, we can elide // the load, zap the store, and usually zap the alloca. - if (llvm::StoreInst *SI = findDominatingStoreToReturnValue(*this)) { + if (llvm::StoreInst *SI = findDominatingStoreToReturnValue(CGF)) { // Reuse the debug location from the store unless there is // cleanup code to be emitted between the store and return // instruction. - if (EmitRetDbgLoc && !AutoreleaseResult) + if (EmitRetDbgLoc && !CGF.AutoreleaseResult) RetDbgLoc = SI->getDebugLoc(); // Get the stored value and nuke the now-dead store. RV = SI->getValueOperand(); SI->eraseFromParent(); - // Otherwise, we have to do a simple load. + // Otherwise, we have to do a simple load. } else { - RV = Builder.CreateLoad(ReturnValue); + RV = CGF.Builder.CreateLoad(CGF.ReturnValue); } } else { // If the value is offset in memory, apply the offset now. - Address V = emitAddressAtOffset(*this, ReturnValue, RetAI); + Address V = emitAddressAtOffset(CGF, CGF.ReturnValue, RetAI); - RV = CreateCoercedLoad(V, RetAI.getCoerceToType(), *this); + RV = CreateCoercedLoad(V, RetAI.getCoerceToType(), CGF); } // In ARC, end functions that return a retainable type with a call // to objc_autoreleaseReturnValue. - if (AutoreleaseResult) { + if (CGF.AutoreleaseResult) { #ifndef NDEBUG // Type::isObjCRetainabletype has to be called on a QualType that hasn't // been stripped of the typedefs, so we cannot use RetTy here. Get the @@ -4101,19 +4080,19 @@ void CodeGenFunction::EmitFunctionEpilog( // CurCodeDecl or BlockInfo. QualType RT; - if (auto *FD = dyn_cast(CurCodeDecl)) + if (auto *FD = dyn_cast(CGF.CurCodeDecl)) RT = FD->getReturnType(); - else if (auto *MD = dyn_cast(CurCodeDecl)) + else if (auto *MD = dyn_cast(CGF.CurCodeDecl)) RT = MD->getReturnType(); - else if (isa(CurCodeDecl)) - RT = BlockInfo->BlockExpression->getFunctionType()->getReturnType(); + else if (isa(CGF.CurCodeDecl)) + RT = CGF.BlockInfo->BlockExpression->getFunctionType()->getReturnType(); else llvm_unreachable("Unexpected function/method type"); - assert(getLangOpts().ObjCAutoRefCount && !FI.isReturnsRetained() && + assert(CGF.getLangOpts().ObjCAutoRefCount && !FI.isReturnsRetained() && RT->isObjCRetainableType()); #endif - RV = emitAutoreleaseOfResult(*this, RV); + RV = emitAutoreleaseOfResult(CGF, RV); } break; @@ -4128,19 +4107,19 @@ void CodeGenFunction::EmitFunctionEpilog( // Load all of the coerced elements out into results. llvm::SmallVector results; - Address addr = ReturnValue.withElementType(coercionType); + Address addr = CGF.ReturnValue.withElementType(coercionType); unsigned unpaddedIndex = 0; for (unsigned i = 0, e = coercionType->getNumElements(); i != e; ++i) { auto coercedEltType = coercionType->getElementType(i); if (ABIArgInfo::isPaddingForCoerceAndExpand(coercedEltType)) continue; - auto eltAddr = Builder.CreateStructGEP(addr, i); + auto eltAddr = CGF.Builder.CreateStructGEP(addr, i); llvm::Value *elt = CreateCoercedLoad( eltAddr, unpaddedStruct ? unpaddedStruct->getElementType(unpaddedIndex++) : unpaddedCoercionType, - *this); + CGF); results.push_back(elt); } @@ -4148,27 +4127,58 @@ void CodeGenFunction::EmitFunctionEpilog( if (results.size() == 1) { RV = results[0]; - // Otherwise, we need to make a first-class aggregate. + // Otherwise, we need to make a first-class aggregate. } else { // Construct a return type that lacks padding elements. llvm::Type *returnType = RetAI.getUnpaddedCoerceAndExpandType(); RV = llvm::PoisonValue::get(returnType); for (unsigned i = 0, e = results.size(); i != e; ++i) { - RV = Builder.CreateInsertValue(RV, results[i], i); + RV = CGF.Builder.CreateInsertValue(RV, results[i], i); } } break; } case ABIArgInfo::TargetSpecific: { - Address V = emitAddressAtOffset(*this, ReturnValue, RetAI); - RV = CGM.getABIInfo().createCoercedLoad(V, RetAI, *this); + Address V = emitAddressAtOffset(CGF, CGF.ReturnValue, RetAI); + RV = CGF.CGM.getABIInfo().createCoercedLoad(V, RetAI, CGF); break; } case ABIArgInfo::Expand: case ABIArgInfo::IndirectAliased: llvm_unreachable("Invalid ABI kind for return argument"); } +} + +static bool isReturnReachable(CodeGenFunction::JumpDest &ReturnBlock) { + return !ReturnBlock.isValid() || !ReturnBlock.getBlock()->use_empty(); +} + +void CodeGenFunction::EmitFunctionEpilog( + const CGFunctionInfo &FI, bool EmitRetDbgLoc, SourceLocation EndLoc, + uint64_t RetKeyInstructionsSourceAtom) { + if (FI.isNoReturn()) { + // Noreturn functions don't return. + EmitUnreachable(EndLoc); + return; + } + + if (CurCodeDecl && CurCodeDecl->hasAttr()) { + // Naked functions don't have epilogues. + Builder.CreateUnreachable(); + return; + } + + llvm::Value *RV = nullptr; + llvm::DebugLoc RetDbgLoc; + QualType RetTy = FI.getReturnType(); + + if (ReturnValue.isValid()) + processFunctionReturnInfo(*this, FI, EmitRetDbgLoc, EndLoc, RetTy, RV, + RetDbgLoc); + + if (SehTryEndInvokeDest && isReturnReachable(ReturnBlock)) + EmitSehTryScopeEnd(SehTryEndInvokeDest); llvm::Instruction *Ret; if (RV) { @@ -4203,7 +4213,7 @@ void CodeGenFunction::EmitReturnValueCheck(llvm::Value *RV) { // If the return block isn't reachable, neither is this check, so don't emit // it. - if (ReturnBlock.isValid() && ReturnBlock.getBlock()->use_empty()) + if (!isReturnReachable(ReturnBlock)) return; ReturnsNonNullAttr *RetNNAttr = nullptr; diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp index 28ac9bf396356..8ef921029ccda 100644 --- a/clang/lib/CodeGen/CGCleanup.cpp +++ b/clang/lib/CodeGen/CGCleanup.cpp @@ -1329,8 +1329,10 @@ void CodeGenFunction::EmitCXXTemporary(const CXXTemporary *Temporary, // Need to set "funclet" in OperandBundle properly for noThrow // intrinsic (see CGCall.cpp) static void EmitSehScope(CodeGenFunction &CGF, - llvm::FunctionCallee &SehCppScope) { - llvm::BasicBlock *InvokeDest = CGF.getInvokeDest(); + llvm::FunctionCallee &SehCppScope, + llvm::BasicBlock *InvokeDest = nullptr) { + if (!InvokeDest) + InvokeDest = CGF.getInvokeDest(); assert(CGF.Builder.GetInsertBlock() && InvokeDest); llvm::BasicBlock *Cont = CGF.createBasicBlock("invoke.cont"); SmallVector BundleList = @@ -1373,11 +1375,11 @@ void CodeGenFunction::EmitSehTryScopeBegin() { } // Invoke a llvm.seh.try.end at the end of a SEH scope for -EHa -void CodeGenFunction::EmitSehTryScopeEnd() { +void CodeGenFunction::EmitSehTryScopeEnd(llvm::BasicBlock *InvokeDest) { assert(getLangOpts().EHAsynch); llvm::FunctionType *FTy = llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false); llvm::FunctionCallee SehCppScope = CGM.CreateRuntimeFunction(FTy, "llvm.seh.try.end"); - EmitSehScope(*this, SehCppScope); + EmitSehScope(*this, SehCppScope, InvokeDest); } diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index f86af4581c345..69c1e49f5832b 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -635,6 +635,37 @@ void CodeGenFunction::EmitCXXTryStmt(const CXXTryStmt &S) { ExitCXXTryStmt(S); } +static bool +RequiresSehTryEnd(const CompoundStmt *S, + llvm::SmallPtrSet &CheckedSehTryBlockStmts) { + if (!S || CheckedSehTryBlockStmts.contains(S)) + return false; + + llvm::SmallVector WorkList; + WorkList.push_back(S); + + while (!WorkList.empty()) { + auto *Next = WorkList.back(); + WorkList.pop_back(); + if (!Next) + continue; + + if (isa(Next)) + return true; + + if (auto *Try = dyn_cast(Next)) + CheckedSehTryBlockStmts.insert(Try->getTryBlock()); + + if (auto *Try = dyn_cast(Next)) + CheckedSehTryBlockStmts.insert(Try->getTryBlock()); + + auto Children = Next->children(); + WorkList.insert(WorkList.end(), Children.begin(), Children.end()); + } + + return false; +} + void CodeGenFunction::EnterCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) { unsigned NumHandlers = S.getNumHandlers(); EHCatchScope *CatchScope = EHStack.pushCatch(NumHandlers); @@ -666,8 +697,14 @@ void CodeGenFunction::EnterCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) { CatchScope->setHandler(I, CGM.getCXXABI().getCatchAllTypeInfo(), Handler); // Under async exceptions, catch(...) need to catch HW exception too // Mark scope with SehTryBegin as a SEH __try scope - if (getLangOpts().EHAsynch) + if (getLangOpts().EHAsynch) { EmitSehTryScopeBegin(); + auto *TryBlock = S.getTryBlock(); + + if (!SehTryEndInvokeDest && + RequiresSehTryEnd(TryBlock, CheckedSehTryBlockStmts)) + SehTryEndInvokeDest = getInvokeDest(); + } } } } @@ -1670,14 +1707,18 @@ void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) { SEHTryEpilogueStack.push_back(&TryExit); llvm::BasicBlock *TryBB = nullptr; + auto *TryBlock = S.getTryBlock(); // IsEHa: emit an invoke to _seh_try_begin() runtime for -EHa if (getLangOpts().EHAsynch) { EmitRuntimeCallOrInvoke(getSehTryBeginFn(CGM)); if (SEHTryEpilogueStack.size() == 1) // outermost only TryBB = Builder.GetInsertBlock(); + if (!SehTryEndInvokeDest && + RequiresSehTryEnd(TryBlock, CheckedSehTryBlockStmts)) + SehTryEndInvokeDest = getInvokeDest(); } - EmitStmt(S.getTryBlock()); + EmitStmt(TryBlock); // Volatilize all blocks in Try, till current insert point if (TryBB) { diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 8c4c1c8c2dc95..1920e8d562674 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -2129,6 +2129,12 @@ class CodeGenFunction : public CodeGenTypeCache { /// Terminate funclets keyed by parent funclet pad. llvm::MapVector TerminateFunclets; + /// Visited try block statements that do not need a scope end. + llvm::SmallPtrSet CheckedSehTryBlockStmts; + + /// Unwind destination for try scope end. + llvm::BasicBlock *SehTryEndInvokeDest = nullptr; + /// Largest vector width used in ths function. Will be used to create a /// function attribute. unsigned LargestVectorWidth = 0; @@ -3241,7 +3247,7 @@ class CodeGenFunction : public CodeGenTypeCache { void EmitSehCppScopeBegin(); void EmitSehCppScopeEnd(); void EmitSehTryScopeBegin(); - void EmitSehTryScopeEnd(); + void EmitSehTryScopeEnd(llvm::BasicBlock *InvokeDest = nullptr); bool EmitLifetimeStart(llvm::Value *Addr); void EmitLifetimeEnd(llvm::Value *Addr); diff --git a/clang/test/CodeGen/windows-seh-EHa-Inline1.cpp b/clang/test/CodeGen/windows-seh-EHa-Inline1.cpp new file mode 100644 index 0000000000000..26d8cc204274e --- /dev/null +++ b/clang/test/CodeGen/windows-seh-EHa-Inline1.cpp @@ -0,0 +1,62 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6 +// RUN: %clang_cc1 -O3 -triple x86_64-windows -fasync-exceptions -fcxx-exceptions -fexceptions -fms-extensions -x c++ -Wno-implicit-function-declaration -emit-llvm %s -o - | FileCheck %s +// Check that the try scope of ExitOnThrow is terminated upon inlining into main. +int AlwaysThrows(int); +[[noreturn]] void Exit(); + +int ExitOnThrow(int argc) noexcept +{ + try { + if (!argc) { throw -1; } + return argc; + } catch (...) { + } + + Exit(); + return 0; +} + +// CHECK-LABEL: define dso_local noundef i32 @main( +// CHECK-SAME: i32 noundef [[ARGC:%.*]], ptr noundef readnone captures(none) [[TMP0:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] personality ptr @__CxxFrameHandler3 { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP_I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[TMP_I]]) +// CHECK-NEXT: invoke void @llvm.seh.try.begin() +// CHECK-NEXT: to label %[[INVOKE_CONT_I:.*]] unwind label %[[CATCH_DISPATCH_I:.*]] +// CHECK: [[INVOKE_CONT_I]]: +// CHECK-NEXT: [[TOBOOL_NOT_I:%.*]] = icmp eq i32 [[ARGC]], 0 +// CHECK-NEXT: br i1 [[TOBOOL_NOT_I]], label %[[IF_THEN_I:.*]], label %[[IF_END_I:.*]] +// CHECK: [[IF_THEN_I]]: +// CHECK-NEXT: store i32 -1, ptr [[TMP_I]], align 4, !tbaa [[INT_TBAA7:![0-9]+]] +// CHECK-NEXT: invoke void @_CxxThrowException(ptr nonnull [[TMP_I]], ptr nonnull @_TI1H) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: to label %[[UNREACHABLE_I:.*]] unwind label %[[CATCH_DISPATCH_I]] +// CHECK: [[CATCH_DISPATCH_I]]: +// CHECK-NEXT: [[TMP1:%.*]] = catchswitch within none [label %[[CATCH_I:.*]]] unwind to caller +// CHECK: [[CATCH_I]]: +// CHECK-NEXT: [[TMP2:%.*]] = catchpad within [[TMP1]] [ptr null, i32 0, ptr null] +// CHECK-NEXT: catchret from [[TMP2]] to label %[[TRY_CONT_I:.*]] +// CHECK: [[TRY_CONT_I]]: +// CHECK-NEXT: call void @"?Exit@@YAXXZ"() #[[ATTR7:[0-9]+]] +// CHECK-NEXT: unreachable +// CHECK: [[IF_END_I]]: +// CHECK-NEXT: invoke void @llvm.seh.try.end() +// CHECK-NEXT: to label %"?ExitOnThrow@@YAHH@Z.exit" unwind label %[[CATCH_DISPATCH_I]] +// CHECK: [[UNREACHABLE_I]]: +// CHECK-NEXT: unreachable +// CHECK: "?ExitOnThrow@@YAHH@Z.exit": +// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[TMP_I]]) +// CHECK-NEXT: [[CALL1:%.*]] = tail call noundef i32 @"?AlwaysThrows@@YAHH@Z"(i32 noundef [[ARGC]]) +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[CALL1]], [[ARGC]] +// CHECK-NEXT: ret i32 [[ADD]] +// +int main(int argc, char**) +{ + auto data = ExitOnThrow(argc); + return data + AlwaysThrows(data); +} +//. +// CHECK: [[INT_TBAA7]] = !{[[META8:![0-9]+]], [[META8]], i64 0} +// CHECK: [[META8]] = !{!"int", [[META9:![0-9]+]], i64 0} +// CHECK: [[META9]] = !{!"omnipotent char", [[META10:![0-9]+]], i64 0} +// CHECK: [[META10]] = !{!"Simple C++ TBAA"} +//. diff --git a/clang/test/CodeGen/windows-seh-EHa-Inline2.cpp b/clang/test/CodeGen/windows-seh-EHa-Inline2.cpp new file mode 100644 index 0000000000000..f8539cb315dfb --- /dev/null +++ b/clang/test/CodeGen/windows-seh-EHa-Inline2.cpp @@ -0,0 +1,103 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6 +// RUN: %clang_cc1 -O3 -triple x86_64-windows -fasync-exceptions -fcxx-exceptions -fexceptions -fms-extensions -x c++ -Wno-implicit-function-declaration -emit-llvm %s -o - | FileCheck %s +// Check that only the outermost try scope containing a return statement is terminated upon inlining into main. +void DoSth(); +int AlwaysThrows(int); +[[noreturn]] void Exit(); + +int ExitOnThrow(int argc) noexcept +{ + try { + try { + DoSth(); + } catch(...) {} + } catch(...) {} + + try { + try { + if (!argc) { throw -1; } + return argc; + } catch (...) {} + } catch (...) {} + + Exit(); + return 0; +} + +// CHECK-LABEL: define dso_local noundef i32 @main( +// CHECK-SAME: i32 noundef [[ARGC:%.*]], ptr noundef readnone captures(none) [[TMP0:%.*]]) local_unnamed_addr #[[ATTR5:[0-9]+]] personality ptr @__CxxFrameHandler3 { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP_I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[TMP_I]]) +// CHECK-NEXT: invoke void @llvm.seh.try.begin() +// CHECK-NEXT: to label %[[INVOKE_CONT_I:.*]] unwind label %[[CATCH_DISPATCH4_I:.*]] +// CHECK: [[INVOKE_CONT_I]]: +// CHECK-NEXT: invoke void @llvm.seh.try.begin() +// CHECK-NEXT: to label %[[INVOKE_CONT1_I:.*]] unwind label %[[CATCH_DISPATCH_I:.*]] +// CHECK: [[INVOKE_CONT1_I]]: +// CHECK-NEXT: invoke void @"?DoSth@@YAXXZ"() +// CHECK-NEXT: to label %[[TRY_CONT7_I:.*]] unwind label %[[CATCH_DISPATCH_I]] +// CHECK: [[CATCH_DISPATCH_I]]: +// CHECK-NEXT: [[TMP1:%.*]] = catchswitch within none [label %[[CATCH_I:.*]]] unwind label %[[CATCH_DISPATCH4_I]] +// CHECK: [[CATCH_I]]: +// CHECK-NEXT: [[TMP2:%.*]] = catchpad within [[TMP1]] [ptr null, i32 0, ptr null] +// CHECK-NEXT: invoke void @llvm.seh.scope.end() [ "funclet"(token [[TMP2]]) ] +// CHECK-NEXT: to label %[[INVOKE_CONT3_I:.*]] unwind label %[[CATCH_DISPATCH4_I]] +// CHECK: [[CATCH_DISPATCH4_I]]: +// CHECK-NEXT: [[TMP3:%.*]] = catchswitch within none [label %[[CATCH5_I:.*]]] unwind to caller +// CHECK: [[CATCH5_I]]: +// CHECK-NEXT: [[TMP4:%.*]] = catchpad within [[TMP3]] [ptr null, i32 0, ptr null] +// CHECK-NEXT: catchret from [[TMP4]] to label %[[TRY_CONT7_I]] +// CHECK: [[TRY_CONT7_I]]: +// CHECK-NEXT: invoke void @llvm.seh.try.begin() +// CHECK-NEXT: to label %[[INVOKE_CONT8_I:.*]] unwind label %[[CATCH_DISPATCH15_I:.*]] +// CHECK: [[INVOKE_CONT8_I]]: +// CHECK-NEXT: invoke void @llvm.seh.try.begin() +// CHECK-NEXT: to label %[[INVOKE_CONT9_I:.*]] unwind label %[[CATCH_DISPATCH10_I:.*]] +// CHECK: [[INVOKE_CONT9_I]]: +// CHECK-NEXT: [[TOBOOL_NOT_I:%.*]] = icmp eq i32 [[ARGC]], 0 +// CHECK-NEXT: br i1 [[TOBOOL_NOT_I]], label %[[IF_THEN_I:.*]], label %[[IF_END_I:.*]] +// CHECK: [[IF_THEN_I]]: +// CHECK-NEXT: store i32 -1, ptr [[TMP_I]], align 4, !tbaa [[INT_TBAA7:![0-9]+]] +// CHECK-NEXT: invoke void @_CxxThrowException(ptr nonnull [[TMP_I]], ptr nonnull @_TI1H) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: to label %[[UNREACHABLE_I:.*]] unwind label %[[CATCH_DISPATCH10_I]] +// CHECK: [[CATCH_DISPATCH10_I]]: +// CHECK-NEXT: [[TMP5:%.*]] = catchswitch within none [label %[[CATCH11_I:.*]]] unwind label %[[CATCH_DISPATCH15_I]] +// CHECK: [[CATCH11_I]]: +// CHECK-NEXT: [[TMP6:%.*]] = catchpad within [[TMP5]] [ptr null, i32 0, ptr null] +// CHECK-NEXT: invoke void @llvm.seh.scope.end() [ "funclet"(token [[TMP6]]) ] +// CHECK-NEXT: to label %[[INVOKE_CONT12_I:.*]] unwind label %[[CATCH_DISPATCH15_I]] +// CHECK: [[CATCH_DISPATCH15_I]]: +// CHECK-NEXT: [[TMP7:%.*]] = catchswitch within none [label %[[CATCH16_I:.*]]] unwind to caller +// CHECK: [[CATCH16_I]]: +// CHECK-NEXT: [[TMP8:%.*]] = catchpad within [[TMP7]] [ptr null, i32 0, ptr null] +// CHECK-NEXT: catchret from [[TMP8]] to label %[[TRY_CONT18_I:.*]] +// CHECK: [[TRY_CONT18_I]]: +// CHECK-NEXT: call void @"?Exit@@YAXXZ"() #[[ATTR8:[0-9]+]] +// CHECK-NEXT: unreachable +// CHECK: [[INVOKE_CONT12_I]]: +// CHECK-NEXT: catchret from [[TMP6]] to label %[[TRY_CONT18_I]] +// CHECK: [[INVOKE_CONT3_I]]: +// CHECK-NEXT: catchret from [[TMP2]] to label %[[TRY_CONT7_I]] +// CHECK: [[IF_END_I]]: +// CHECK-NEXT: invoke void @llvm.seh.try.end() +// CHECK-NEXT: to label %"?ExitOnThrow@@YAHH@Z.exit" unwind label %[[CATCH_DISPATCH15_I]] +// CHECK: [[UNREACHABLE_I]]: +// CHECK-NEXT: unreachable +// CHECK: "?ExitOnThrow@@YAHH@Z.exit": +// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[TMP_I]]) +// CHECK-NEXT: [[CALL1:%.*]] = tail call noundef i32 @"?AlwaysThrows@@YAHH@Z"(i32 noundef [[ARGC]]) +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[CALL1]], [[ARGC]] +// CHECK-NEXT: ret i32 [[ADD]] +// +int main(int argc, char**) +{ + auto data = ExitOnThrow(argc); + return data + AlwaysThrows(data); +} +//. +// CHECK: [[INT_TBAA7]] = !{[[META8:![0-9]+]], [[META8]], i64 0} +// CHECK: [[META8]] = !{!"int", [[META9:![0-9]+]], i64 0} +// CHECK: [[META9]] = !{!"omnipotent char", [[META10:![0-9]+]], i64 0} +// CHECK: [[META10]] = !{!"Simple C++ TBAA"} +//. diff --git a/clang/test/CodeGen/windows-seh-EHa-Inline3.cpp b/clang/test/CodeGen/windows-seh-EHa-Inline3.cpp new file mode 100644 index 0000000000000..a32c07d625746 --- /dev/null +++ b/clang/test/CodeGen/windows-seh-EHa-Inline3.cpp @@ -0,0 +1,87 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6 +// RUN: %clang_cc1 -O3 -triple x86_64-windows -fasync-exceptions -fcxx-exceptions -fexceptions -fms-extensions -x c++ -Wno-implicit-function-declaration -emit-llvm %s -o - | FileCheck %s +// Check that the outermost __try scope containing a return statement is terminated upon inlining into main. +int AlwaysThrows(int); +[[noreturn]] void Exit(); +volatile int *p{nullptr}; + +int ExitOnThrow(int argc) noexcept +{ + __try { + __try { + if (!argc) { *p = 0; } + return argc; + } __except(1) {} + } __except(1) {} + + Exit(); + return 0; +} + +// CHECK-LABEL: define dso_local noundef i32 @main( +// CHECK-SAME: i32 noundef [[ARGC:%.*]], ptr noundef readnone captures(none) [[TMP0:%.*]]) local_unnamed_addr #[[ATTR4:[0-9]+]] personality ptr @__C_specific_handler { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARGC_ADDR_I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[__EXCEPTION_CODE1_I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[ARGC_ADDR_I]]) +// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[__EXCEPTION_CODE1_I]]) +// CHECK-NEXT: store i32 [[ARGC]], ptr [[ARGC_ADDR_I]], align 4, !tbaa [[INT_TBAA7:![0-9]+]] +// CHECK-NEXT: invoke void @llvm.seh.try.begin() +// CHECK-NEXT: to label %[[INVOKE_CONT_I:.*]] unwind label %[[CATCH_DISPATCH5_I:.*]] +// CHECK: [[INVOKE_CONT_I]]: +// CHECK-NEXT: invoke void @llvm.seh.try.begin() +// CHECK-NEXT: to label %[[INVOKE_CONT2_I:.*]] unwind label %[[CATCH_DISPATCH_I:.*]] +// CHECK: [[CATCH_DISPATCH_I]]: +// CHECK-NEXT: [[TMP1:%.*]] = catchswitch within none [label %[[__EXCEPT_I:.*]]] unwind label %[[CATCH_DISPATCH5_I]] +// CHECK: [[__EXCEPT_I]]: +// CHECK-NEXT: [[TMP2:%.*]] = catchpad within [[TMP1]] [ptr null] +// CHECK-NEXT: catchret from [[TMP2]] to label %[[__EXCEPT3_I:.*]] +// CHECK: [[__EXCEPT3_I]]: +// CHECK-NEXT: [[TMP3:%.*]] = tail call i32 @llvm.eh.exceptioncode(token [[TMP2]]) +// CHECK-NEXT: store volatile i32 [[TMP3]], ptr [[__EXCEPTION_CODE1_I]], align 4 +// CHECK-NEXT: invoke void @llvm.seh.try.end() +// CHECK-NEXT: to label %[[__TRY_CONT8_I:.*]] unwind label %[[CATCH_DISPATCH5_I]] +// CHECK: [[CATCH_DISPATCH5_I]]: +// CHECK-NEXT: [[TMP4:%.*]] = catchswitch within none [label %[[__EXCEPT6_I:.*]]] unwind to caller +// CHECK: [[__EXCEPT6_I]]: +// CHECK-NEXT: [[TMP5:%.*]] = catchpad within [[TMP4]] [ptr null] +// CHECK-NEXT: catchret from [[TMP5]] to label %[[__EXCEPT7_I:.*]] +// CHECK: [[__EXCEPT7_I]]: +// CHECK-NEXT: [[TMP6:%.*]] = tail call i32 @llvm.eh.exceptioncode(token [[TMP5]]) +// CHECK-NEXT: br label %[[__TRY_CONT8_I]] +// CHECK: [[__TRY_CONT8_I]]: +// CHECK-NEXT: tail call void @"?Exit@@YAXXZ"() #[[ATTR7:[0-9]+]] +// CHECK-NEXT: unreachable +// CHECK: [[INVOKE_CONT2_I]]: +// CHECK-NEXT: [[ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0__I:%.*]] = load volatile i32, ptr [[ARGC_ADDR_I]], align 4, !tbaa [[INT_TBAA7]] +// CHECK-NEXT: [[TOBOOL_NOT_I:%.*]] = icmp eq i32 [[ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0__I]], 0 +// CHECK-NEXT: br i1 [[TOBOOL_NOT_I]], label %[[IF_THEN_I:.*]], label %[[IF_END_I:.*]] +// CHECK: [[IF_THEN_I]]: +// CHECK-NEXT: [[TMP7:%.*]] = load volatile ptr, ptr @"?p@@3PECHEC", align 8, !tbaa [[INTPTR_TBAA11:![0-9]+]] +// CHECK-NEXT: store volatile i32 0, ptr [[TMP7]], align 4, !tbaa [[INT_TBAA7]] +// CHECK-NEXT: br label %[[IF_END_I]] +// CHECK: [[IF_END_I]]: +// CHECK-NEXT: [[ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0_9_I:%.*]] = load volatile i32, ptr [[ARGC_ADDR_I]], align 4, !tbaa [[INT_TBAA7]] +// CHECK-NEXT: invoke void @llvm.seh.try.end() +// CHECK-NEXT: to label %"?ExitOnThrow@@YAHH@Z.exit" unwind label %[[CATCH_DISPATCH5_I]] +// CHECK: "?ExitOnThrow@@YAHH@Z.exit": +// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[ARGC_ADDR_I]]) +// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[__EXCEPTION_CODE1_I]]) +// CHECK-NEXT: [[CALL1:%.*]] = tail call noundef i32 @"?AlwaysThrows@@YAHH@Z"(i32 noundef [[ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0_9_I]]) +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[CALL1]], [[ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0_9_I]] +// CHECK-NEXT: ret i32 [[ADD]] +// +int main(int argc, char**) +{ + auto data = ExitOnThrow(argc); + return data + AlwaysThrows(data); +} +//. +// CHECK: [[INT_TBAA7]] = !{[[META8:![0-9]+]], [[META8]], i64 0} +// CHECK: [[META8]] = !{!"int", [[META9:![0-9]+]], i64 0} +// CHECK: [[META9]] = !{!"omnipotent char", [[META10:![0-9]+]], i64 0} +// CHECK: [[META10]] = !{!"Simple C++ TBAA"} +// CHECK: [[INTPTR_TBAA11]] = !{[[META12:![0-9]+]], [[META12]], i64 0} +// CHECK: [[META12]] = !{!"p1 int", [[META13:![0-9]+]], i64 0} +// CHECK: [[META13]] = !{!"any pointer", [[META9]], i64 0} +//. diff --git a/llvm/lib/CodeGen/WinEHPrepare.cpp b/llvm/lib/CodeGen/WinEHPrepare.cpp index 66d29cb5d65e4..c1fd9720f8478 100644 --- a/llvm/lib/CodeGen/WinEHPrepare.cpp +++ b/llvm/lib/CodeGen/WinEHPrepare.cpp @@ -341,7 +341,7 @@ void llvm::calculateSEHStateForAsynchEH(const BasicBlock *BB, int State, // Retrive the new State from seh_try_begin State = EHInfo.InvokeStateMap[cast(TI)]; else if (Fn && Fn->isIntrinsic() && - Fn->getIntrinsicID() == Intrinsic::seh_try_end) + Fn->getIntrinsicID() == Intrinsic::seh_try_end && State >= 0) // end of current state, retrive new state from UnwindMap State = EHInfo.SEHUnwindMap[State].ToState; }