[clang] [llvm] [WinEH] Fix try scopes leaking to caller on inline (PR #167176)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Nov 8 13:01:37 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Mirko (MuellerMP)
<details>
<summary>Changes</summary>
This fixes issue #<!-- -->164169
When inlining functions compiled with -EHa, try scope terminators might need to be inserted before inlined returns. This prevents leaking try scopes over to the caller. Try scopes can be ended before a ret due to the unwinder forwarding exceptions in the seh epilog to the caller.
---
Patch is 30.62 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/167176.diff
8 Files Affected:
- (modified) clang/lib/CodeGen/CGCall.cpp (+77-67)
- (modified) clang/lib/CodeGen/CGCleanup.cpp (+6-4)
- (modified) clang/lib/CodeGen/CGException.cpp (+43-2)
- (modified) clang/lib/CodeGen/CodeGenFunction.h (+7-1)
- (added) clang/test/CodeGen/windows-seh-EHa-Inline1.cpp (+62)
- (added) clang/test/CodeGen/windows-seh-EHa-Inline2.cpp (+103)
- (added) clang/test/CodeGen/windows-seh-EHa-Inline3.cpp (+87)
- (modified) llvm/lib/CodeGen/WinEHPrepare.cpp (+1-1)
``````````diff
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<NakedAttr>()) {
- // 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<llvm::GetElementPtrInst>(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<FunctionDecl>(CurCodeDecl))
+ if (auto *FD = dyn_cast<FunctionDecl>(CGF.CurCodeDecl))
RT = FD->getReturnType();
- else if (auto *MD = dyn_cast<ObjCMethodDecl>(CurCodeDecl))
+ else if (auto *MD = dyn_cast<ObjCMethodDecl>(CGF.CurCodeDecl))
RT = MD->getReturnType();
- else if (isa<BlockDecl>(CurCodeDecl))
- RT = BlockInfo->BlockExpression->getFunctionType()->getReturnType();
+ else if (isa<BlockDecl>(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<llvm::Value *, 4> 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<NakedAttr>()) {
+ // 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..6d3bbd672517a 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<llvm::OperandBundleDef, 1> 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..bc02264b4a58b 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<const Stmt *, 1> &CheckedSehTryBlockStmts) {
+ if (!S || CheckedSehTryBlockStmts.contains(S))
+ return false;
+
+ llvm::SmallVector<const Stmt *> WorkList;
+ WorkList.push_back(S);
+
+ while (!WorkList.empty()) {
+ auto *Next = WorkList.back();
+ WorkList.pop_back();
+ if (!Next)
+ continue;
+
+ if (isa<ReturnStmt>(Next))
+ return true;
+
+ if (auto *Try = dyn_cast<CXXTryStmt>(Next))
+ CheckedSehTryBlockStmts.insert(Try->getTryBlock());
+
+ if (auto *Try = dyn_cast<SEHTryStmt>(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<llvm::Value *, llvm::BasicBlock *> TerminateFunclets;
+ /// Visited try block statements that do not need a scope end.
+ llvm::SmallPtrSet<const Stmt *, 1> 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 at Z.exit" unwind label %[[CATCH_DISPATCH_I]]
+// CHECK: [[UNREACHABLE_I]]:
+// CHECK-NEXT: unreachable
+// CHECK: "?ExitOnThrow@@YAHH at Z.exit":
+// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[TMP_I]])
+// CHECK-NEXT: [[CALL1:%.*]] = tail call noundef i32 @"?AlwaysThrows@@YAHH at 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; }
+ retu...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/167176
More information about the llvm-commits
mailing list