https://github.com/sivadeilra updated https://github.com/llvm/llvm-project/pull/144745
>From 50c418d4259ac5039d9ab7532afd518a0cfbbc64 Mon Sep 17 00:00:00 2001 From: Arlie Davis <arlie.da...@microsoft.com> Date: Mon, 16 Jun 2025 11:09:23 -0700 Subject: [PATCH 1/4] Fix IP2State tables --- .../CodeGenCXX/microsoft-abi-eh-async.cpp | 180 +++++++++++++ .../CodeGenCXX/microsoft-abi-eh-disabled.cpp | 139 ++++++++++ .../CodeGenCXX/microsoft-abi-eh-ip2state.cpp | 241 ++++++++++++++++++ llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 4 +- llvm/lib/CodeGen/AsmPrinter/WinException.cpp | 16 +- llvm/lib/CodeGen/AsmPrinter/WinException.h | 1 - llvm/lib/Target/X86/X86AsmPrinter.h | 2 + llvm/lib/Target/X86/X86MCInstLower.cpp | 151 +++++++++-- .../test/CodeGen/WinEH/wineh-noret-cleanup.ll | 16 +- 9 files changed, 706 insertions(+), 44 deletions(-) create mode 100644 clang/test/CodeGenCXX/microsoft-abi-eh-async.cpp create mode 100644 clang/test/CodeGenCXX/microsoft-abi-eh-disabled.cpp create mode 100644 clang/test/CodeGenCXX/microsoft-abi-eh-ip2state.cpp diff --git a/clang/test/CodeGenCXX/microsoft-abi-eh-async.cpp b/clang/test/CodeGenCXX/microsoft-abi-eh-async.cpp new file mode 100644 index 0000000000000..00bcdaf55aacc --- /dev/null +++ b/clang/test/CodeGenCXX/microsoft-abi-eh-async.cpp @@ -0,0 +1,180 @@ +// RUN: %clang_cl -c --target=x86_64-windows-msvc /EHa -O2 /GS- \ +// RUN: -Xclang=-import-call-optimization \ +// RUN: /clang:-S /clang:-o- %s 2>&1 \ +// RUN: | FileCheck %s + +#ifdef __clang__ +#define NO_TAIL __attribute((disable_tail_calls)) +#else +#define NO_TAIL +#endif + +void might_throw(); +void other_func(int x); + +void does_not_throw() noexcept(true); + +extern "C" void __declspec(dllimport) some_dll_import(); + +class HasDtor { + int x; + char foo[40]; + +public: + explicit HasDtor(int x); + ~HasDtor(); +}; + +class BadError { +public: + int errorCode; +}; + +void normal_has_regions() { + // CHECK-LABEL: .def "?normal_has_regions@@YAXXZ" + // CHECK: .seh_endprologue + + // <-- state -1 (none) + { + HasDtor hd{42}; + + // <-- state goes from -1 to 0 + // because state changes, we expect the HasDtor::HasDtor() call to have a NOP + // CHECK: call "??0HasDtor@@QEAA@H@Z" + // CHECK-NEXT: nop + + might_throw(); + // CHECK: call "?might_throw@@YAXXZ" + // CHECK-NEXT: nop + + // <-- state goes from 0 to -1 because we're about to call HasDtor::~HasDtor() + // CHECK: call "??1HasDtor@@QEAA@XZ" + // <-- state -1 + } + + // <-- state -1 + other_func(10); + // CHECK: call "?other_func@@YAXH@Z" + // CHECK-NEXT: nop + // CHECK: .seh_startepilogue + + // <-- state -1 +} + +// This tests a tail call to a destructor. +void case_dtor_arg_empty_body(HasDtor x) +{ + // CHECK-LABEL: .def "?case_dtor_arg_empty_body@@YAXVHasDtor@@@Z" + // CHECK: jmp "??1HasDtor@@QEAA@XZ" +} + +int case_dtor_arg_empty_with_ret(HasDtor x) +{ + // CHECK-LABEL: .def "?case_dtor_arg_empty_with_ret@@YAHVHasDtor@@@Z" + // CHECK: .seh_endprologue + + // CHECK: call "??1HasDtor@@QEAA@XZ" + // CHECK-NOT: nop + + // The call to HasDtor::~HasDtor() should NOT have a NOP because the + // following "mov eax, 100" instruction is in the same EH state. + + return 100; + + // CHECK: mov eax, 100 + // CHECK: .seh_startepilogue + // CHECK: .seh_endepilogue + // CHECK: .seh_endproc +} + +int case_noexcept_dtor(HasDtor x) noexcept(true) +{ + // CHECK: .def "?case_noexcept_dtor@@YAHVHasDtor@@@Z" + // CHECK: call "??1HasDtor@@QEAA@XZ" + // CHECK-NEXT: mov eax, 100 + // CHECK: .seh_startepilogue + return 100; +} + +void case_except_simple_call() NO_TAIL +{ + does_not_throw(); +} + +void case_noexcept_simple_call() noexcept(true) NO_TAIL +{ + does_not_throw(); +} + +// This tests that the destructor is called right before SEH_BeginEpilogue, +// but in a function that has a return value. +int case_dtor_arg_calls_no_throw(HasDtor x) +{ + does_not_throw(); // no NOP expected + return 100; +} + +// Check the behavior of CALLs that are at the end of MBBs. If a CALL is within +// a non-null EH state (state -1) and is at the end of an MBB, then we expect +// to find an EH_LABEL after the CALL. This causes us to insert a NOP, which +// is the desired result. +void case_dtor_runs_after_join(int x) { + // CHECK-LABEL: .def "?case_dtor_runs_after_join@@YAXH@Z" + // CHECK: .seh_endprologue + + // <-- EH state -1 + + // ctor call does not need a NOP, because it has real instructions after it + HasDtor hd{42}; + // CHECK: call "??0HasDtor@@QEAA@H@Z" + // CHECK-NEXT: nop + // CHECK: test + + // <-- EH state transition from -1 0 + if (x) { + might_throw(); // <-- NOP expected (at end of BB w/ EH_LABEL) + // CHECK: call "?might_throw@@YAXXZ" + // CHECK-NEXT: nop + } else { + other_func(10); // <-- NOP expected (at end of BB w/ EH_LABEL) + // CHECK: call "?other_func@@YAXH@Z" + // CHECK-NEXT: nop + } + does_not_throw(); + // <-- EH state transition 0 to -1 + // ~HasDtor() runs + + // CHECK: .seh_endproc + + // CHECK: "$ip2state$?case_dtor_runs_after_join@@YAXH@Z": + // CHECK-NEXT: .long [[func_begin:.Lfunc_begin([0-9]+)@IMGREL]] + // CHECK-NEXT: .long -1 + // CHECK-NEXT: .long [[tmp1:.Ltmp([0-9]+)]]@IMGREL + // CHECK-NEXT: .long 0 + // CHECK-NEXT: .long [[tmp2:.Ltmp([0-9]+)]]@IMGREL + // CHECK-NEXT: .long -1 +} + + +// Check the behavior of NOP padding around tail calls. +// We do not expect to insert NOPs around tail calls. +// However, the first call (to other_func()) does get a NOP +// because it comes before .seh_startepilogue. +void case_tail_call_no_eh() { + // CHECK-LABEL: .def "?case_tail_call_no_eh@@YAXXZ" + // CHECK: .seh_endprologue + + // ordinary call + other_func(10); + // CHECK: call "?other_func@@YAXH@Z" + // CHECK-NEXT: nop + + // tail call; no NOP padding after JMP + does_not_throw(); + + // CHECK: .seh_startepilogue + // CHECK: .seh_endepilogue + // CHECK: jmp "?does_not_throw@@YAXXZ" + // CHECK-NOT: nop + // CHECK: .seh_endproc +} diff --git a/clang/test/CodeGenCXX/microsoft-abi-eh-disabled.cpp b/clang/test/CodeGenCXX/microsoft-abi-eh-disabled.cpp new file mode 100644 index 0000000000000..16fb381d814f2 --- /dev/null +++ b/clang/test/CodeGenCXX/microsoft-abi-eh-disabled.cpp @@ -0,0 +1,139 @@ +// RUN: %clang_cl -c --target=x86_64-windows-msvc /EHs-c- -O2 /GS- \ +// RUN: -Xclang=-import-call-optimization \ +// RUN: /clang:-S /clang:-o- %s 2>&1 \ +// RUN: | FileCheck %s + +#ifdef __clang__ +#define NO_TAIL __attribute((disable_tail_calls)) +#else +#define NO_TAIL +#endif + +void might_throw(); +void other_func(int x); + +void does_not_throw() noexcept(true); + +extern "C" void __declspec(dllimport) some_dll_import(); + +class HasDtor { + int x; + char foo[40]; + +public: + explicit HasDtor(int x); + ~HasDtor(); +}; + +void normal_has_regions() { + { + HasDtor hd{42}; + + // because state changes, we expect the HasDtor::HasDtor() call to have a NOP + might_throw(); + } + + other_func(10); +} +// CHECK-LABEL: .def "?normal_has_regions@@YAXXZ" +// CHECK: .seh_endprologue +// CHECK: call "??0HasDtor@@QEAA@H@Z" +// CHECK-NEXT: call "?might_throw@@YAXXZ" +// CHECK-NEXT: mov +// CHECK: call "??1HasDtor@@QEAA@XZ" +// CHECK-NEXT: mov ecx, 10 +// CHECK-NEXT: call "?other_func@@YAXH@Z" +// CHECK-NEXT: nop +// CHECK-NEXT: .seh_startepilogue +// CHECK-NOT: "$ip2state$?normal_has_regions@@YAXXZ" + +// This tests a tail call to a destructor. +void case_dtor_arg_empty_body(HasDtor x) +{ +} +// CHECK-LABEL: .def "?case_dtor_arg_empty_body@@YAXVHasDtor@@@Z" +// CHECK: jmp "??1HasDtor@@QEAA@XZ" + +int case_dtor_arg_empty_with_ret(HasDtor x) +{ + // The call to HasDtor::~HasDtor() should NOT have a NOP because the + // following "mov eax, 100" instruction is in the same EH state. + return 100; +} +// CHECK-LABEL: .def "?case_dtor_arg_empty_with_ret@@YAHVHasDtor@@@Z" +// CHECK: .seh_endprologue +// CHECK: call "??1HasDtor@@QEAA@XZ" +// CHECK-NOT: nop +// CHECK: mov eax, 100 +// CHECK: .seh_startepilogue +// CHECK: .seh_endepilogue +// CHECK: .seh_endproc + +void case_except_simple_call() NO_TAIL +{ + does_not_throw(); +} + +// This tests that the destructor is called right before SEH_BeginEpilogue, +// but in a function that has a return value. +int case_dtor_arg_calls_no_throw(HasDtor x) +{ + does_not_throw(); // no NOP expected + return 100; +} + +// Check the behavior of CALLs that are at the end of MBBs. If a CALL is within +// a non-null EH state (state -1) and is at the end of an MBB, then we expect +// to find an EH_LABEL after the CALL. This causes us to insert a NOP, which +// is the desired result. +void case_dtor_runs_after_join(int x) { + + // ctor call does not need a NOP, because it has real instructions after it + HasDtor hd{42}; + + if (x) { + might_throw(); + } else { + other_func(10); + } + does_not_throw(); + // ~HasDtor() runs +} + +// CHECK-LABEL: .def "?case_dtor_runs_after_join@@YAXH@Z" +// CHECK: .seh_endprologue +// CHECK: call "??0HasDtor@@QEAA@H@Z" +// CHECK-NEXT: test +// CHECK: call "?might_throw@@YAXXZ" +// CHECK-NEXT: jmp +// CHECK: call "?other_func@@YAXH@Z" +// CHECK-NEXT: .LBB +// CHECK: call "?does_not_throw@@YAXXZ" +// CHECK-NEXT: lea +// CHECK-NEXT: call "??1HasDtor@@QEAA@XZ" +// CHECK-NEXT: nop +// CHECK-NEXT: .seh_startepilogue +// CHECK-NOT: "$ip2state$?case_dtor_runs_after_join@@YAXH@Z": + + +// Check the behavior of NOP padding around tail calls. +// We do not expect to insert NOPs around tail calls. +// However, the first call (to other_func()) does get a NOP +// because it comes before .seh_startepilogue. +void case_tail_call_no_eh() { + // ordinary call + other_func(10); + + // tail call; no NOP padding after JMP + does_not_throw(); +} + +// CHECK-LABEL: .def "?case_tail_call_no_eh@@YAXXZ" +// CHECK: .seh_endprologue +// CHECK: call "?other_func@@YAXH@Z" +// CHECK-NEXT: nop +// CHECK-NEXT: .seh_startepilogue +// CHECK: .seh_endepilogue +// CHECK: jmp "?does_not_throw@@YAXXZ" +// CHECK-NOT: nop +// CHECK: .seh_endproc diff --git a/clang/test/CodeGenCXX/microsoft-abi-eh-ip2state.cpp b/clang/test/CodeGenCXX/microsoft-abi-eh-ip2state.cpp new file mode 100644 index 0000000000000..5e3f89c4b4680 --- /dev/null +++ b/clang/test/CodeGenCXX/microsoft-abi-eh-ip2state.cpp @@ -0,0 +1,241 @@ +// RUN: %clang_cl -c --target=x86_64-windows-msvc -O2 /EHsc /GS- \ +// RUN: -Xclang=-import-call-optimization \ +// RUN: /clang:-S /clang:-o- %s 2>&1 \ +// RUN: | FileCheck %s + +#ifdef __clang__ +#define NO_TAIL __attribute((disable_tail_calls)) +#else +#define NO_TAIL +#endif + +void might_throw(); +void other_func(int x); + +void does_not_throw() noexcept(true); + +extern "C" void __declspec(dllimport) some_dll_import(); + +class HasDtor { + int x; + char foo[40]; + +public: + explicit HasDtor(int x); + ~HasDtor(); +}; + +class BadError { +public: + int errorCode; +}; + +// Verify that when NOP padding for IP2State is active *and* Import Call +// Optimization is active that we see both forms of NOP padding. +void case_calls_dll_import() NO_TAIL { + some_dll_import(); +} +// CHECK-LABEL: .def "?case_calls_dll_import@@YAXXZ" +// CHECK: .seh_endprologue +// CHECK: .Limpcall{{[0-9]+}}: +// CHECK-NEXT: rex64 +// CHECK-NEXT: call __imp_some_dll_import +// CHECK-NEXT: nop dword ptr {{\[.*\]}} +// CHECK-NEXT: nop +// CHECK-NEXT: .seh_startepilogue + +void normal_has_regions() { + + // <-- state -1 (none) + { + HasDtor hd{42}; + + // <-- state goes from -1 to 0 + // because state changes, we expect the HasDtor::HasDtor() call to have a NOP + + might_throw(); + + // <-- state goes from 0 to -1 because we're about to call HasDtor::~HasDtor() + // <-- state -1 + } + + // <-- state -1 + other_func(10); + + // <-- state -1 +} +// CHECK-LABEL: .def "?normal_has_regions@@YAXXZ" +// CHECK: .seh_endprologue +// CHECK: call "??0HasDtor@@QEAA@H@Z" +// CHECK-NEXT: nop +// CHECK: call "?might_throw@@YAXXZ" +// CHECK-NEXT: nop +// CHECK: call "??1HasDtor@@QEAA@XZ" +// CHECK: call "?other_func@@YAXH@Z" +// CHECK-NEXT: nop +// CHECK: .seh_startepilogue + +// This tests a tail call to a destructor. +void case_dtor_arg_empty_body(HasDtor x) +{ +} +// CHECK-LABEL: .def "?case_dtor_arg_empty_body@@YAXVHasDtor@@@Z" +// CHECK: jmp "??1HasDtor@@QEAA@XZ" + +int case_dtor_arg_empty_with_ret(HasDtor x) +{ + // CHECK-LABEL: .def "?case_dtor_arg_empty_with_ret@@YAHVHasDtor@@@Z" + // CHECK: .seh_endprologue + + // CHECK: call "??1HasDtor@@QEAA@XZ" + // CHECK-NOT: nop + + // The call to HasDtor::~HasDtor() should NOT have a NOP because the + // following "mov eax, 100" instruction is in the same EH state. + + return 100; + + // CHECK: mov eax, 100 + // CHECK: .seh_startepilogue + // CHECK: .seh_endepilogue + // CHECK: .seh_endproc +} + +int case_noexcept_dtor(HasDtor x) noexcept(true) +{ + // CHECK: .def "?case_noexcept_dtor@@YAHVHasDtor@@@Z" + // CHECK: call "??1HasDtor@@QEAA@XZ" + // CHECK-NEXT: mov eax, 100 + // CHECK-NEXT: .seh_startepilogue + return 100; +} + +// Simple call of a function that can throw +void case_except_simple_call() NO_TAIL +{ + might_throw(); +} +// CHECK-LABEL: .def "?case_except_simple_call@@YAXXZ" +// CHECK: .seh_endprologue +// CHECK-NEXT: call "?might_throw@@YAXXZ" +// CHECK-NEXT: nop +// CHECK-NEXT: .seh_startepilogue + +// Simple call of a function that cannot throw, in a noexcept context. +void case_noexcept_simple_call() noexcept(true) NO_TAIL +{ + does_not_throw(); +} +// CHECK-LABEL: .def "?case_noexcept_simple_call@@YAXXZ" +// CHECK: .seh_endprologue +// CHECK-NEXT: call "?does_not_throw@@YAXXZ" +// CHECK-NEXT: nop +// CHECK-NEXT: .seh_startepilogue + + +// This tests that the destructor is called right before SEH_BeginEpilogue, +// but in a function that has a return value. +int case_dtor_arg_calls_no_throw(HasDtor x) +{ + does_not_throw(); // no NOP expected + return 100; +} + +// Check the behavior of CALLs that are at the end of MBBs. If a CALL is within +// a non-null EH state (state -1) and is at the end of an MBB, then we expect +// to find an EH_LABEL after the CALL. This causes us to insert a NOP, which +// is the desired result. +void case_dtor_runs_after_join(int x) { + // CHECK-LABEL: .def "?case_dtor_runs_after_join@@YAXH@Z" + // CHECK: .seh_endprologue + + // <-- EH state -1 + + // ctor call does not need a NOP, because it has real instructions after it + HasDtor hd{42}; + // CHECK: call "??0HasDtor@@QEAA@H@Z" + // CHECK-NEXT: test + + // <-- EH state transition from -1 0 + if (x) { + might_throw(); // <-- NOP expected (at end of BB w/ EH_LABEL) + // CHECK: call "?might_throw@@YAXXZ" + // CHECK-NEXT: nop + } else { + other_func(10); // <-- NOP expected (at end of BB w/ EH_LABEL) + // CHECK: call "?other_func@@YAXH@Z" + // CHECK-NEXT: nop + } + does_not_throw(); + // <-- EH state transition 0 to -1 + // ~HasDtor() runs + + // CHECK: .seh_endproc + + // CHECK: "$ip2state$?case_dtor_runs_after_join@@YAXH@Z": + // CHECK-NEXT: .long [[func_begin:.Lfunc_begin([0-9]+)@IMGREL]] + // CHECK-NEXT: .long -1 + // CHECK-NEXT: .long [[tmp1:.Ltmp([0-9]+)]]@IMGREL + // CHECK-NEXT: .long 0 + // CHECK-NEXT: .long [[tmp2:.Ltmp([0-9]+)]]@IMGREL + // CHECK-NEXT: .long -1 +} + + +// Check the behavior of NOP padding around tail calls. +// We do not expect to insert NOPs around tail calls. +// However, the first call (to other_func()) does get a NOP +// because it comes before .seh_startepilogue. +void case_tail_call_no_eh() { + // CHECK-LABEL: .def "?case_tail_call_no_eh@@YAXXZ" + // CHECK: .seh_endprologue + + // ordinary call + other_func(10); + // CHECK: call "?other_func@@YAXH@Z" + // CHECK-NEXT: nop + + // tail call; no NOP padding after JMP + does_not_throw(); + + // CHECK: .seh_startepilogue + // CHECK: .seh_endepilogue + // CHECK: jmp "?does_not_throw@@YAXXZ" + // CHECK-NOT: nop + // CHECK: .seh_endproc +} + + +// Check the behavior of a try/catch +int case_try_catch() { + // CHECK-LABEL: .def "?case_try_catch@@YAHXZ" + // CHECK: .seh_endprologue + + // Because of the EH_LABELs, the ctor and other_func() get NOPs. + + int result = 0; + try { + // CHECK: call "??0HasDtor@@QEAA@H@Z" + // CHECK-NEXT: nop + HasDtor hd{20}; + + // CHECK: call "?other_func@@YAXH@Z" + // CHECK-NEXT: nop + other_func(10); + + // CHECK: call "??1HasDtor@@QEAA@XZ" + // CHECK: mov + } catch (BadError& e) { + result = 1; + } + return result; + + // CHECK: .seh_endproc + + // CHECK: .def "?dtor$4@?0??case_try_catch@@YAHXZ@4HA" + // CHECK: .seh_endprologue + // CHECK: call "??1HasDtor@@QEAA@XZ" + // CHECK-NEXT: nop + // CHECK: .seh_startepilogue + // CHECK: .seh_endproc +} diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index e13e92378d4aa..6545b8b6404e0 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -1858,6 +1858,7 @@ void AsmPrinter::emitFunctionBody() { OutStreamer->emitLabel(MI.getOperand(0).getMCSymbol()); break; case TargetOpcode::EH_LABEL: + OutStreamer->AddComment("EH_LABEL"); OutStreamer->emitLabel(MI.getOperand(0).getMCSymbol()); // For AsynchEH, insert a Nop if followed by a trap inst // Or the exception won't be caught. @@ -1932,8 +1933,9 @@ void AsmPrinter::emitFunctionBody() { auto CountInstruction = [&](const MachineInstr &MI) { // Skip Meta instructions inside bundles. - if (MI.isMetaInstruction()) + if (MI.isMetaInstruction()) { return; + } ++NumInstsInFunction; if (CanDoExtraAnalysis) { StringRef Name = getMIMnemonic(MI, *OutStreamer); diff --git a/llvm/lib/CodeGen/AsmPrinter/WinException.cpp b/llvm/lib/CodeGen/AsmPrinter/WinException.cpp index 55d1350e446ab..258349c241369 100644 --- a/llvm/lib/CodeGen/AsmPrinter/WinException.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/WinException.cpp @@ -325,12 +325,6 @@ const MCExpr *WinException::getLabel(const MCSymbol *Label) { Asm->OutContext); } -const MCExpr *WinException::getLabelPlusOne(const MCSymbol *Label) { - return MCBinaryExpr::createAdd(getLabel(Label), - MCConstantExpr::create(1, Asm->OutContext), - Asm->OutContext); -} - const MCExpr *WinException::getOffset(const MCSymbol *OffsetOf, const MCSymbol *OffsetFrom) { return MCBinaryExpr::createSub( @@ -657,7 +651,7 @@ void WinException::emitSEHActionsForRange(const WinEHFuncInfo &FuncInfo, AddComment("LabelStart"); OS.emitValue(getLabel(BeginLabel), 4); AddComment("LabelEnd"); - OS.emitValue(getLabelPlusOne(EndLabel), 4); + OS.emitValue(getLabel(EndLabel), 4); AddComment(UME.IsFinally ? "FinallyFunclet" : UME.Filter ? "FilterFunction" : "CatchAll"); OS.emitValue(FilterOrFinally, 4); @@ -952,13 +946,7 @@ void WinException::computeIP2StateTable( if (!ChangeLabel) ChangeLabel = StateChange.PreviousEndLabel; // Emit an entry indicating that PCs after 'Label' have this EH state. - // NOTE: On ARM architectures, the StateFromIp automatically takes into - // account that the return address is after the call instruction (whose EH - // state we should be using), but on other platforms we need to +1 to the - // label so that we are using the correct EH state. - const MCExpr *LabelExpression = (isAArch64 || isThumb) - ? getLabel(ChangeLabel) - : getLabelPlusOne(ChangeLabel); + const MCExpr *LabelExpression = getLabel(ChangeLabel); IPToStateTable.push_back( std::make_pair(LabelExpression, StateChange.NewState)); // FIXME: assert that NewState is between CatchLow and CatchHigh. diff --git a/llvm/lib/CodeGen/AsmPrinter/WinException.h b/llvm/lib/CodeGen/AsmPrinter/WinException.h index 638589adf0ddc..47dd30cef133d 100644 --- a/llvm/lib/CodeGen/AsmPrinter/WinException.h +++ b/llvm/lib/CodeGen/AsmPrinter/WinException.h @@ -80,7 +80,6 @@ class LLVM_LIBRARY_VISIBILITY WinException : public EHStreamer { const MCExpr *create32bitRef(const MCSymbol *Value); const MCExpr *create32bitRef(const GlobalValue *GV); const MCExpr *getLabel(const MCSymbol *Label); - const MCExpr *getLabelPlusOne(const MCSymbol *Label); const MCExpr *getOffset(const MCSymbol *OffsetOf, const MCSymbol *OffsetFrom); const MCExpr *getOffsetPlusOne(const MCSymbol *OffsetOf, const MCSymbol *OffsetFrom); diff --git a/llvm/lib/Target/X86/X86AsmPrinter.h b/llvm/lib/Target/X86/X86AsmPrinter.h index efb951b73532f..6c04f8729f1ff 100644 --- a/llvm/lib/Target/X86/X86AsmPrinter.h +++ b/llvm/lib/Target/X86/X86AsmPrinter.h @@ -151,6 +151,8 @@ class LLVM_LIBRARY_VISIBILITY X86AsmPrinter : public AsmPrinter { MCSymbol *LazyPointer) override; void emitCallInstruction(const llvm::MCInst &MCI); + bool needsNopAfterCallForWindowsEH(const MachineInstr *MI); + void emitNopAfterCallForWindowsEH(const MachineInstr *MI); // Emits a label to mark the next instruction as being relevant to Import Call // Optimization. diff --git a/llvm/lib/Target/X86/X86MCInstLower.cpp b/llvm/lib/Target/X86/X86MCInstLower.cpp index 55d57d15f8d42..26780d44a6493 100644 --- a/llvm/lib/Target/X86/X86MCInstLower.cpp +++ b/llvm/lib/Target/X86/X86MCInstLower.cpp @@ -32,6 +32,7 @@ #include "llvm/CodeGen/MachineModuleInfoImpls.h" #include "llvm/CodeGen/MachineOperand.h" #include "llvm/CodeGen/StackMaps.h" +#include "llvm/CodeGen/WinEHFuncInfo.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/Mangler.h" @@ -2538,26 +2539,6 @@ void X86AsmPrinter::emitInstruction(const MachineInstr *MI) { case X86::SEH_BeginEpilogue: { assert(MF->hasWinCFI() && "SEH_ instruction in function without WinCFI?"); - // Windows unwinder will not invoke function's exception handler if IP is - // either in prologue or in epilogue. This behavior causes a problem when a - // call immediately precedes an epilogue, because the return address points - // into the epilogue. To cope with that, we insert a 'nop' if it ends up - // immediately after a CALL in the final emitted code. - MachineBasicBlock::const_iterator MBBI(MI); - // Check if preceded by a call and emit nop if so. - for (MBBI = PrevCrossBBInst(MBBI); - MBBI != MachineBasicBlock::const_iterator(); - MBBI = PrevCrossBBInst(MBBI)) { - // Pseudo instructions that aren't a call are assumed to not emit any - // code. If they do, we worst case generate unnecessary noops after a - // call. - if (MBBI->isCall() || !MBBI->isPseudo()) { - if (MBBI->isCall()) - EmitAndCountInstruction(MCInstBuilder(X86::NOOP)); - break; - } - } - EmitSEHInstruction(MI); return; } @@ -2586,6 +2567,7 @@ void X86AsmPrinter::emitInstruction(const MachineInstr *MI) { EmitAndCountInstruction(MCInstBuilder(X86::REX64_PREFIX)); emitCallInstruction(TmpInst); emitNop(*OutStreamer, 5, Subtarget); + emitNopAfterCallForWindowsEH(MI); return; } @@ -2606,6 +2588,7 @@ void X86AsmPrinter::emitInstruction(const MachineInstr *MI) { // For Import Call Optimization to work, we need a 3-byte nop after the // call instruction. emitNop(*OutStreamer, 3, Subtarget); + emitNopAfterCallForWindowsEH(MI); return; } break; @@ -2639,6 +2622,7 @@ void X86AsmPrinter::emitInstruction(const MachineInstr *MI) { if (MI->isCall()) { emitCallInstruction(TmpInst); + emitNopAfterCallForWindowsEH(MI); return; } @@ -2660,6 +2644,133 @@ void X86AsmPrinter::emitCallInstruction(const llvm::MCInst &MCI) { OutStreamer->emitInstruction(MCI, getSubtargetInfo()); } +// Checks whether a NOP is required after a CALL and inserts the NOP, if +// necessary. +void X86AsmPrinter::emitNopAfterCallForWindowsEH(const MachineInstr *MI) { + if (needsNopAfterCallForWindowsEH(MI)) + EmitAndCountInstruction(MCInstBuilder(X86::NOOP)); +} + +// Determines whether a NOP is required after a CALL, so that Windows EH +// IP2State tables have the correct information. +// +// On most Windows platforms (AMD64, ARM64, ARM32, IA64, but *not* x86-32), +// exception handling works by looking up instruction pointers in lookup +// tables. These lookup tables are stored in .xdata sections in executables. +// One element of the lookup tables are the "IP2State" tables (Instruction +// Pointer to State). +// +// If a function has any instructions that require cleanup during exception +// unwinding, then it will have an IP2State table. Each entry in the IP2State +// table describes a range of bytes in the function's instruction stream, and +// associates an "EH state number" with that range of instructions. A value of +// -1 means "the null state", which does not require any code to execute. +// A value other than -1 is an index into the State table. +// +// The entries in the IP2State table contain byte offsets within the instruction +// stream of the function. The Windows ABI requires that these offsets are +// aligned to instruction boundaries; they are not permitted to point to a byte +// that is not the first byte of an instruction. +// +// Unfortunately, CALL instructions present a problem during unwinding. CALL +// instructions push the address of the instruction after the CALL instruction, +// so that execution can resume after the CALL. If the CALL is the last +// instruction within an IP2State region, then the return address (on the stack) +// points to the *next* IP2State region. This means that the unwinder will +// use the wrong cleanup funclet during unwinding. +// +// To fix this problem, MSVC will insert a NOP after a CALL instruction, if the +// CALL instruction is the last instruction within an IP2State region. The NOP +// is placed within the same IP2State region as the CALL, so that the return +// address points to the NOP and the unwinder will locate the correct region. +// +// Previously, LLVM fixed this by adding 1 to the instruction offsets in the +// IP2State table. This caused the instruction boundary to point *within* the +// instruction after a CALL. This works for the purposes of unwinding, since +// there are no AMD64 instructions that can be encoded in a single byte and +// which throw C++ exceptions. Unfortunately, this violates the Windows ABI +// specification, which requires that the IP2State table entries point to the +// boundaries between exceptions. +// +// To fix this properly, LLVM will now insert a 1-byte NOP after CALL +// instructions, in the same situations that MSVC does. In performance tests, +// the NOP has no detectable significance. The NOP is rarely inserted, since +// it is only inserted when the CALL is the last instruction before an IP2State +// transition or the CALL is the last instruction before the function epilogue. +// +// NOP padding is only necessary on Windows AMD64 targets. On ARM64 and ARM32, +// instructions have a fixed size so the unwinder knows how to "back up" by +// one instruction. +// +// Interaction with Import Call Optimization (ICO): +// +// Import Call Optimization (ICO) is a compiler + OS feature on Windows which +// improves the performance and security of DLL imports. ICO relies on using a +// specific CALL idiom that can be replaced by the OS DLL loader. This removes +// a load and indirect CALL and replaces it with a single direct CALL. +// +// To achieve this, ICO also inserts NOPs after the CALL instruction. If the +// end of the CALL is aligned with an EH state transition, we *also* insert +// a single-byte NOP. **Both forms of NOPs must be preserved.** They cannot +// be combined into a single larger NOP; nor can the second NOP be removed. +// +// This is necessary because, if ICO is active and the call site is modified +// by the loader, the loader will end up overwriting the NOPs that were inserted +// for ICO. That means that those NOPs cannot be used for the correct +// termination of the exception handling region (the IP2State transition), +// so we still need an additional NOP instruction. The NOPs cannot be combined +// into a longer NOP (which is ordinarily desirable) because then ICO would +// split one instruction, producing a malformed instruction after the ICO call. +bool X86AsmPrinter::needsNopAfterCallForWindowsEH(const MachineInstr *MI) { + // We only need to insert NOPs after CALLs when targeting Windows on AMD64. + // Since this code is already restricted to X86, we just test for Win64. + if (!this->Subtarget->isTargetWin64()) { + return false; + } + + MachineBasicBlock::const_iterator MBBI(MI); + ++MBBI; // Step over MI + auto End = MI->getParent()->end(); + for (; MBBI != End; ++MBBI) { + // Check the instruction that follows this CALL. + const MachineInstr &NextMI = *MBBI; + + // If there is an EH_LABEL after this CALL, then there is an EH state + // transition after this CALL. This is exactly the situation which requires + // NOP padding. + if (NextMI.isEHLabel()) { + return true; + } + + // Somewhat similarly, if the CALL is the last instruction before the + // SEH prologue, then we also need a NOP. This is necessary because the + // Windows stack unwinder will not invoke a function's exception handler if + // the instruction pointer is in the function prologue or epilogue. + if (NextMI.getOpcode() == X86::SEH_BeginEpilogue) { + return true; + } + + if (!NextMI.isPseudo() && !NextMI.isMetaInstruction()) { + // We found a real instruction. During the CALL, the return IP will point + // to this instruction. Since this instruction has the same EH state as + // the call itself (because there is no intervening EH_LABEL), the + // IP2State table will be accurate; there is no need to insert a NOP. + return false; + } + + // The next instruction is a pseudo-op. Ignore it and keep searching. + // Because these instructions do not generate any machine code, they cannot + // prevent the IP2State table from pointing at the wrong instruction during + // a CALL. + } + + // The CALL is at the end of the MBB. We do not insert a NOP. If the CALL + // is at the end of an MBB that is within a non-null EH state, then the + // MBB will already include an EH_LABEL at the end of this MBB. In that + // case, we would see the EH_LABEL, not the CALL, at the end of the MBB. + return false; +} + void X86AsmPrinter::emitLabelAndRecordForImportCallOptimization( ImportCallKind Kind) { assert(EnableImportCallOptimization); diff --git a/llvm/test/CodeGen/WinEH/wineh-noret-cleanup.ll b/llvm/test/CodeGen/WinEH/wineh-noret-cleanup.ll index e42b005cf64bd..f35248cf8d44d 100644 --- a/llvm/test/CodeGen/WinEH/wineh-noret-cleanup.ll +++ b/llvm/test/CodeGen/WinEH/wineh-noret-cleanup.ll @@ -46,15 +46,15 @@ catch.body.2: ; CXX-LABEL: $ip2state$test: ; CXX-NEXT: .long .Lfunc_begin0@IMGREL ; CXX-NEXT: .long -1 -; CXX-NEXT: .long .Ltmp0@IMGREL+1 +; CXX-NEXT: .long .Ltmp0@IMGREL ; CXX-NEXT: .long 1 -; CXX-NEXT: .long .Ltmp1@IMGREL+1 +; CXX-NEXT: .long .Ltmp1@IMGREL ; CXX-NEXT: .long -1 ; CXX-NEXT: .long "?catch$3@?0?test@4HA"@IMGREL ; CXX-NEXT: .long 2 -; CXX-NEXT: .long .Ltmp2@IMGREL+1 +; CXX-NEXT: .long .Ltmp2@IMGREL ; CXX-NEXT: .long 3 -; CXX-NEXT: .long .Ltmp3@IMGREL+1 +; CXX-NEXT: .long .Ltmp3@IMGREL ; CXX-NEXT: .long 2 ; CXX-NEXT: .long "?catch$5@?0?test@4HA"@IMGREL ; CXX-NEXT: .long 4 @@ -62,19 +62,19 @@ catch.body.2: ; SEH-LABEL: test: ; SEH-LABEL: .Llsda_begin0: ; SEH-NEXT: .long .Ltmp0@IMGREL -; SEH-NEXT: .long .Ltmp1@IMGREL+1 +; SEH-NEXT: .long .Ltmp1@IMGREL ; SEH-NEXT: .long dummy_filter@IMGREL ; SEH-NEXT: .long .LBB0_3@IMGREL ; SEH-NEXT: .long .Ltmp0@IMGREL -; SEH-NEXT: .long .Ltmp1@IMGREL+1 +; SEH-NEXT: .long .Ltmp1@IMGREL ; SEH-NEXT: .long dummy_filter@IMGREL ; SEH-NEXT: .long .LBB0_5@IMGREL ; SEH-NEXT: .long .Ltmp2@IMGREL -; SEH-NEXT: .long .Ltmp3@IMGREL+1 +; SEH-NEXT: .long .Ltmp3@IMGREL ; SEH-NEXT: .long "?dtor$2@?0?test@4HA"@IMGREL ; SEH-NEXT: .long 0 ; SEH-NEXT: .long .Ltmp2@IMGREL -; SEH-NEXT: .long .Ltmp3@IMGREL+1 +; SEH-NEXT: .long .Ltmp3@IMGREL ; SEH-NEXT: .long dummy_filter@IMGREL ; SEH-NEXT: .long .LBB0_5@IMGREL ; SEH-NEXT: .Llsda_end0: >From f194c87ddf7e777ef86fd0eb7456dc99c46d7ecb Mon Sep 17 00:00:00 2001 From: Arlie Davis <arlie.da...@microsoft.com> Date: Wed, 18 Jun 2025 09:38:27 -0700 Subject: [PATCH 2/4] style: revert one change --- llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index 6545b8b6404e0..82d91e7587bd9 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -1933,9 +1933,8 @@ void AsmPrinter::emitFunctionBody() { auto CountInstruction = [&](const MachineInstr &MI) { // Skip Meta instructions inside bundles. - if (MI.isMetaInstruction()) { + if (MI.isMetaInstruction()) return; - } ++NumInstsInFunction; if (CanDoExtraAnalysis) { StringRef Name = getMIMnemonic(MI, *OutStreamer); >From a3f5003a4a8120ff7fc2137c549650830a440f3a Mon Sep 17 00:00:00 2001 From: Arlie Davis <arlie.da...@microsoft.com> Date: Wed, 18 Jun 2025 11:17:15 -0700 Subject: [PATCH 3/4] Adjust tests --- .../test/CodeGen/X86/catchret-empty-fallthrough.ll | 2 +- llvm/test/CodeGen/X86/conditional-tailcall-pgso.ll | 1 - llvm/test/CodeGen/X86/conditional-tailcall.ll | 1 - llvm/test/CodeGen/X86/seh-catch-all.ll | 2 +- llvm/test/CodeGen/X86/seh-catchpad.ll | 10 +++++----- llvm/test/CodeGen/X86/seh-except-finally.ll | 6 +++--- llvm/test/CodeGen/X86/seh-finally.ll | 2 +- llvm/test/CodeGen/X86/seh-safe-div.ll | 5 +++-- .../CodeGen/X86/seh-unwind-inline-asm-codegen.ll | 4 ++-- llvm/test/CodeGen/X86/stack-coloring-wineh.ll | 2 +- llvm/test/CodeGen/X86/taildup-heapallocsite.ll | 2 ++ llvm/test/CodeGen/X86/win-catchpad-nested-cxx.ll | 8 ++++---- llvm/test/CodeGen/X86/win-catchpad.ll | 8 ++++---- llvm/test/CodeGen/X86/win-cleanuppad.ll | 2 +- llvm/test/CodeGen/X86/win32-eh-states.ll | 14 +++++++------- .../CodeGen/X86/win64-seh-epilogue-statepoint.ll | 1 - llvm/test/CodeGen/X86/wineh-coreclr.ll | 14 ++++++++++++++ .../Inputs/mips64_eh.ll.expected | 6 +++--- 18 files changed, 52 insertions(+), 38 deletions(-) diff --git a/llvm/test/CodeGen/X86/catchret-empty-fallthrough.ll b/llvm/test/CodeGen/X86/catchret-empty-fallthrough.ll index ab9fa2287ffad..24d3030ea4bdb 100644 --- a/llvm/test/CodeGen/X86/catchret-empty-fallthrough.ll +++ b/llvm/test/CodeGen/X86/catchret-empty-fallthrough.ll @@ -48,6 +48,6 @@ return: ; preds = %catch, %entry ; CHECK-NEXT: .long (.Llsda_end0-.Llsda_begin0)/16 ; CHECK-NEXT: .Llsda_begin0: ; CHECK-NEXT: .long .Ltmp0@IMGREL -; CHECK-NEXT: .long .Ltmp1@IMGREL+1 +; CHECK-NEXT: .long .Ltmp1@IMGREL ; CHECK-NEXT: .long 1 ; CHECK-NEXT: .long .LBB0_[[catch]]@IMGREL diff --git a/llvm/test/CodeGen/X86/conditional-tailcall-pgso.ll b/llvm/test/CodeGen/X86/conditional-tailcall-pgso.ll index b3d5d836874cb..85610e7f673f1 100644 --- a/llvm/test/CodeGen/X86/conditional-tailcall-pgso.ll +++ b/llvm/test/CodeGen/X86/conditional-tailcall-pgso.ll @@ -121,7 +121,6 @@ define void @f_non_leaf(i32 %x, i32 %y) !prof !14 { ; WIN64-NEXT: # encoding: [0xeb,A] ; WIN64-NEXT: # fixup A - offset: 1, value: foo-1, kind: FK_PCRel_1 ; WIN64-NEXT: .LBB1_2: # %bb2 -; WIN64-NEXT: nop # encoding: [0x90] ; WIN64-NEXT: .seh_startepilogue ; WIN64-NEXT: popq %rbx # encoding: [0x5b] ; WIN64-NEXT: .seh_endepilogue diff --git a/llvm/test/CodeGen/X86/conditional-tailcall.ll b/llvm/test/CodeGen/X86/conditional-tailcall.ll index 4c990d81810be..4c45a06d99eae 100644 --- a/llvm/test/CodeGen/X86/conditional-tailcall.ll +++ b/llvm/test/CodeGen/X86/conditional-tailcall.ll @@ -121,7 +121,6 @@ define void @f_non_leaf(i32 %x, i32 %y) optsize { ; WIN64-NEXT: # encoding: [0xeb,A] ; WIN64-NEXT: # fixup A - offset: 1, value: foo-1, kind: FK_PCRel_1 ; WIN64-NEXT: .LBB1_2: # %bb2 -; WIN64-NEXT: nop # encoding: [0x90] ; WIN64-NEXT: .seh_startepilogue ; WIN64-NEXT: popq %rbx # encoding: [0x5b] ; WIN64-NEXT: .seh_endepilogue diff --git a/llvm/test/CodeGen/X86/seh-catch-all.ll b/llvm/test/CodeGen/X86/seh-catch-all.ll index 5250bb9312b78..4e25aabfeef58 100644 --- a/llvm/test/CodeGen/X86/seh-catch-all.ll +++ b/llvm/test/CodeGen/X86/seh-catch-all.ll @@ -40,7 +40,7 @@ catchall: ; CHECK-NEXT: .long (.Llsda_end0-.Llsda_begin0)/16 ; CHECK-NEXT: .Llsda_begin0: ; CHECK-NEXT: .long .Ltmp{{[0-9]+}}@IMGREL -; CHECK-NEXT: .long .Ltmp{{[0-9]+}}@IMGREL+1 +; CHECK-NEXT: .long .Ltmp{{[0-9]+}}@IMGREL ; CHECK-NEXT: .long 1 ; CHECK-NEXT: .long .LBB0_2@IMGREL ; CHECK-NEXT: .Llsda_end0: diff --git a/llvm/test/CodeGen/X86/seh-catchpad.ll b/llvm/test/CodeGen/X86/seh-catchpad.ll index d958580e5925b..cb85f39439e02 100644 --- a/llvm/test/CodeGen/X86/seh-catchpad.ll +++ b/llvm/test/CodeGen/X86/seh-catchpad.ll @@ -123,23 +123,23 @@ __except.ret: ; preds = %catch.dispatch.7 ; CHECK-NEXT: .long (.Llsda_end0-.Llsda_begin0)/16 ; CHECK-NEXT: .Llsda_begin0: ; CHECK-NEXT: .long .Ltmp0@IMGREL -; CHECK-NEXT: .long .Ltmp1@IMGREL+1 +; CHECK-NEXT: .long .Ltmp1@IMGREL ; CHECK-NEXT: .long 1 ; CHECK-NEXT: .long .LBB1_[[except1bb]]@IMGREL ; CHECK-NEXT: .long .Ltmp0@IMGREL -; CHECK-NEXT: .long .Ltmp1@IMGREL+1 +; CHECK-NEXT: .long .Ltmp1@IMGREL ; CHECK-NEXT: .long "?filt$0@0@main@@"@IMGREL ; CHECK-NEXT: .long .LBB1_[[except2bb]]@IMGREL ; CHECK-NEXT: .long .Ltmp2@IMGREL -; CHECK-NEXT: .long .Ltmp3@IMGREL+1 +; CHECK-NEXT: .long .Ltmp3@IMGREL ; CHECK-NEXT: .long "?dtor$[[finbb:[0-9]+]]@?0?main@4HA"@IMGREL ; CHECK-NEXT: .long 0 ; CHECK-NEXT: .long .Ltmp2@IMGREL -; CHECK-NEXT: .long .Ltmp3@IMGREL+1 +; CHECK-NEXT: .long .Ltmp3@IMGREL ; CHECK-NEXT: .long "?filt$0@0@main@@"@IMGREL ; CHECK-NEXT: .long .LBB1_3@IMGREL ; CHECK-NEXT: .long .Ltmp6@IMGREL -; CHECK-NEXT: .long .Ltmp7@IMGREL+1 +; CHECK-NEXT: .long .Ltmp7@IMGREL ; CHECK-NEXT: .long "?filt$0@0@main@@"@IMGREL ; CHECK-NEXT: .long .LBB1_3@IMGREL ; CHECK-NEXT: .Llsda_end0: diff --git a/llvm/test/CodeGen/X86/seh-except-finally.ll b/llvm/test/CodeGen/X86/seh-except-finally.ll index 7f706552fbd7a..539d776430c76 100644 --- a/llvm/test/CodeGen/X86/seh-except-finally.ll +++ b/llvm/test/CodeGen/X86/seh-except-finally.ll @@ -83,15 +83,15 @@ __try.cont: ; preds = %__except, %invoke.c ; CHECK-NEXT: .long (.Llsda_end0-.Llsda_begin0)/16 ; CHECK-NEXT: .Llsda_begin0: ; CHECK-NEXT: .long .Ltmp0@IMGREL -; CHECK-NEXT: .long .Ltmp1@IMGREL+1 +; CHECK-NEXT: .long .Ltmp1@IMGREL ; CHECK-NEXT: .long "?dtor$2@?0?use_both@4HA"@IMGREL ; CHECK-NEXT: .long 0 ; CHECK-NEXT: .long .Ltmp0@IMGREL -; CHECK-NEXT: .long .Ltmp1@IMGREL+1 +; CHECK-NEXT: .long .Ltmp1@IMGREL ; CHECK-NEXT: .long "?filt$0@0@use_both@@"@IMGREL ; CHECK-NEXT: .long .LBB0_{{[0-9]+}}@IMGREL ; CHECK-NEXT: .long .Ltmp4@IMGREL -; CHECK-NEXT: .long .Ltmp5@IMGREL+1 +; CHECK-NEXT: .long .Ltmp5@IMGREL ; CHECK-NEXT: .long "?filt$0@0@use_both@@"@IMGREL ; CHECK-NEXT: .long .LBB0_{{[0-9]+}}@IMGREL ; CHECK-NEXT: .Llsda_end0: diff --git a/llvm/test/CodeGen/X86/seh-finally.ll b/llvm/test/CodeGen/X86/seh-finally.ll index 41823dfb38f0a..6093e5e437910 100644 --- a/llvm/test/CodeGen/X86/seh-finally.ll +++ b/llvm/test/CodeGen/X86/seh-finally.ll @@ -30,7 +30,7 @@ lpad: ; preds = %entry ; X64-NEXT: .long (.Llsda_end0-.Llsda_begin0)/16 # Number of call sites ; X64-NEXT: .Llsda_begin0: ; X64-NEXT: .long .Ltmp0@IMGREL # LabelStart -; X64-NEXT: .long .Ltmp1@IMGREL+1 # LabelEnd +; X64-NEXT: .long .Ltmp1@IMGREL # LabelEnd ; X64-NEXT: .long "?dtor$2@?0?main@4HA"@IMGREL # FinallyFunclet ; X64-NEXT: .long 0 # Null ; X64-NEXT: .Llsda_end0: diff --git a/llvm/test/CodeGen/X86/seh-safe-div.ll b/llvm/test/CodeGen/X86/seh-safe-div.ll index 542d9f67e6126..20169f868a0bf 100644 --- a/llvm/test/CodeGen/X86/seh-safe-div.ll +++ b/llvm/test/CodeGen/X86/seh-safe-div.ll @@ -60,6 +60,7 @@ __try.cont: ; CHECK: .Ltmp0: ; CHECK: leaq [[rloc:.*\(%rbp\)]], %rcx ; CHECK: callq try_body +; CHECK: nop ; CHECK-NEXT: .Ltmp1 ; CHECK: [[cont_bb:\.LBB0_[0-9]+]]: ; CHECK: movl [[rloc]], %eax @@ -82,11 +83,11 @@ __try.cont: ; CHECK-NEXT: .long (.Llsda_end0-.Llsda_begin0)/16 ; CHECK-NEXT: .Llsda_begin0: ; CHECK-NEXT: .long .Ltmp0@IMGREL -; CHECK-NEXT: .long .Ltmp1@IMGREL+1 +; CHECK-NEXT: .long .Ltmp1@IMGREL ; CHECK-NEXT: .long safe_div_filt0@IMGREL ; CHECK-NEXT: .long [[handler0]]@IMGREL ; CHECK-NEXT: .long .Ltmp0@IMGREL -; CHECK-NEXT: .long .Ltmp1@IMGREL+1 +; CHECK-NEXT: .long .Ltmp1@IMGREL ; CHECK-NEXT: .long safe_div_filt1@IMGREL ; CHECK-NEXT: .long [[handler1]]@IMGREL ; CHECK-NEXT: .Llsda_end0: diff --git a/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll b/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll index 2c576df1b7549..5a6aeb6020344 100644 --- a/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll +++ b/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll @@ -56,8 +56,8 @@ declare dso_local void @printf(ptr, ...) ; CHECK-NEXT:$ip2state$test: ; CHECK-NEXT: .long .Lfunc_begin0@IMGREL # IP ; CHECK-NEXT: .long -1 # ToState -; CHECK-NEXT: .long .Ltmp0@IMGREL+1 # IP +; CHECK-NEXT: .long .Ltmp0@IMGREL # IP ; CHECK-NEXT: .long 0 # ToState -; CHECK-NEXT: .long .Ltmp1@IMGREL+1 # IP +; CHECK-NEXT: .long .Ltmp1@IMGREL # IP ; CHECK-NEXT: .long -1 # ToState diff --git a/llvm/test/CodeGen/X86/stack-coloring-wineh.ll b/llvm/test/CodeGen/X86/stack-coloring-wineh.ll index e2de2ff4a392e..74fe07e88aa33 100644 --- a/llvm/test/CodeGen/X86/stack-coloring-wineh.ll +++ b/llvm/test/CodeGen/X86/stack-coloring-wineh.ll @@ -84,12 +84,12 @@ define void @pr66984(ptr %arg) personality ptr @__CxxFrameHandler3 { ; X86_64-NEXT: movq %rcx, {{[-0-9]+}}(%r{{[sb]}}p) # 8-byte Spill ; X86_64-NEXT: .Ltmp0: ; X86_64-NEXT: callq throw +; X86_64-NEXT: nop ; X86_64-NEXT: .Ltmp1: ; X86_64-NEXT: # %bb.1: # %bb14 ; X86_64-NEXT: .LBB0_3: # Block address taken ; X86_64-NEXT: # %exit ; X86_64-NEXT: $ehgcr_0_3: -; X86_64-NEXT: nop ; X86_64-NEXT: .seh_startepilogue ; X86_64-NEXT: addq $64, %rsp ; X86_64-NEXT: popq %rbp diff --git a/llvm/test/CodeGen/X86/taildup-heapallocsite.ll b/llvm/test/CodeGen/X86/taildup-heapallocsite.ll index 967e125f81352..f3bef4743ef88 100644 --- a/llvm/test/CodeGen/X86/taildup-heapallocsite.ll +++ b/llvm/test/CodeGen/X86/taildup-heapallocsite.ll @@ -37,9 +37,11 @@ cond.end: ; preds = %entry, %cond.true ; CHECK: testq ; CHECK: je ; CHECK: callq alloc +; CHECK-NEXT: nop ; CHECK-NEXT: [[L1:.Ltmp[0-9]+]] ; CHECK: jmp f2 # TAILCALL ; CHECK: callq alloc +; CHECK-NEXT: nop ; CHECK-NEXT: [[L3:.Ltmp[0-9]+]] ; CHECK: jmp f2 # TAILCALL diff --git a/llvm/test/CodeGen/X86/win-catchpad-nested-cxx.ll b/llvm/test/CodeGen/X86/win-catchpad-nested-cxx.ll index bfb9c43b3fd16..0bf8370fa24fa 100644 --- a/llvm/test/CodeGen/X86/win-catchpad-nested-cxx.ll +++ b/llvm/test/CodeGen/X86/win-catchpad-nested-cxx.ll @@ -103,15 +103,15 @@ handler2: ; X64: $ip2state$try_in_catch: ; X64-NEXT: .long .Lfunc_begin0@IMGREL ; X64-NEXT: .long -1 -; X64-NEXT: .long .Ltmp0@IMGREL+1 +; X64-NEXT: .long .Ltmp0@IMGREL ; X64-NEXT: .long 0 -; X64-NEXT: .long .Ltmp1@IMGREL+1 +; X64-NEXT: .long .Ltmp1@IMGREL ; X64-NEXT: .long -1 ; X64-NEXT: .long "?catch$2@?0?try_in_catch@4HA"@IMGREL ; X64-NEXT: .long 1 -; X64-NEXT: .long .Ltmp2@IMGREL+1 +; X64-NEXT: .long .Ltmp2@IMGREL ; X64-NEXT: .long 2 -; X64-NEXT: .long .Ltmp3@IMGREL+1 +; X64-NEXT: .long .Ltmp3@IMGREL ; X64-NEXT: .long 1 ; X64-NEXT: .long "?catch$4@?0?try_in_catch@4HA"@IMGREL ; X64-NEXT: .long 3 diff --git a/llvm/test/CodeGen/X86/win-catchpad.ll b/llvm/test/CodeGen/X86/win-catchpad.ll index 249194610e9f8..62ea5109f9df2 100644 --- a/llvm/test/CodeGen/X86/win-catchpad.ll +++ b/llvm/test/CodeGen/X86/win-catchpad.ll @@ -214,9 +214,9 @@ try.cont: ; X64: $ip2state$try_catch_catch: ; X64-NEXT: .long .Lfunc_begin0@IMGREL ; X64-NEXT: .long -1 -; X64-NEXT: .long .Ltmp0@IMGREL+1 +; X64-NEXT: .long .Ltmp0@IMGREL ; X64-NEXT: .long 0 -; X64-NEXT: .long .Ltmp1@IMGREL+1 +; X64-NEXT: .long .Ltmp1@IMGREL ; X64-NEXT: .long -1 ; X64-NEXT: .long "?catch$[[catch1bb]]@?0?try_catch_catch@4HA"@IMGREL ; X64-NEXT: .long 1 @@ -357,9 +357,9 @@ try.cont: ; X64-LABEL: $ip2state$branch_to_normal_dest: ; X64-NEXT: .long .Lfunc_begin1@IMGREL ; X64-NEXT: .long -1 -; X64-NEXT: .long .Ltmp[[before_call]]@IMGREL+1 +; X64-NEXT: .long .Ltmp[[before_call]]@IMGREL ; X64-NEXT: .long 0 -; X64-NEXT: .long .Ltmp[[after_call]]@IMGREL+1 +; X64-NEXT: .long .Ltmp[[after_call]]@IMGREL ; X64-NEXT: .long -1 ; X64-NEXT: .long "?catch$[[catchbb]]@?0?branch_to_normal_dest@4HA"@IMGREL ; X64-NEXT: .long 1 diff --git a/llvm/test/CodeGen/X86/win-cleanuppad.ll b/llvm/test/CodeGen/X86/win-cleanuppad.ll index e3f7f5be0049e..e9265a1ed42e3 100644 --- a/llvm/test/CodeGen/X86/win-cleanuppad.ll +++ b/llvm/test/CodeGen/X86/win-cleanuppad.ll @@ -191,7 +191,7 @@ cleanup.outer: ; preds = %invoke.cont.1, %c ; X64-NEXT: .long 1 ; X64-NEXT: .long .Ltmp6@IMGREL ; X64-NEXT: .long 0 -; X64-NEXT: .long .Ltmp7@IMGREL+1 +; X64-NEXT: .long .Ltmp7@IMGREL ; X64-NEXT: .long -1 attributes #0 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "frame-pointer"="none" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } diff --git a/llvm/test/CodeGen/X86/win32-eh-states.ll b/llvm/test/CodeGen/X86/win32-eh-states.ll index 42ae5b060e6f4..e645199f84602 100644 --- a/llvm/test/CodeGen/X86/win32-eh-states.ll +++ b/llvm/test/CodeGen/X86/win32-eh-states.ll @@ -86,11 +86,11 @@ catch.7: ; X64-LABEL: $ip2state$f: ; X64-NEXT: .long .Lfunc_begin0@IMGREL ; X64-NEXT: .long -1 -; X64-NEXT: .long .Ltmp{{.*}}@IMGREL+1 +; X64-NEXT: .long .Ltmp{{.*}}@IMGREL ; X64-NEXT: .long 0 -; X64-NEXT: .long .Ltmp{{.*}}@IMGREL+1 +; X64-NEXT: .long .Ltmp{{.*}}@IMGREL ; X64-NEXT: .long 1 -; X64-NEXT: .long .Ltmp{{.*}}@IMGREL+1 +; X64-NEXT: .long .Ltmp{{.*}}@IMGREL ; X64-NEXT: .long -1 ; X64-NEXT: .long "?catch${{.*}}@?0?f@4HA"@IMGREL ; X64-NEXT: .long 2 @@ -189,15 +189,15 @@ unreachable: ; preds = %entry ; X64-LABEL: $ip2state$g: ; X64-NEXT: .long .Lfunc_begin1@IMGREL ; X64-NEXT: .long -1 -; X64-NEXT: .long .Ltmp{{.*}}@IMGREL+1 +; X64-NEXT: .long .Ltmp{{.*}}@IMGREL ; X64-NEXT: .long 1 -; X64-NEXT: .long .Ltmp{{.*}}@IMGREL+1 +; X64-NEXT: .long .Ltmp{{.*}}@IMGREL ; X64-NEXT: .long -1 ; X64-NEXT: .long "?catch${{.*}}@?0?g@4HA"@IMGREL ; X64-NEXT: .long 2 -; X64-NEXT: .long .Ltmp{{.*}}@IMGREL+1 +; X64-NEXT: .long .Ltmp{{.*}}@IMGREL ; X64-NEXT: .long 3 -; X64-NEXT: .long .Ltmp{{.*}}@IMGREL+1 +; X64-NEXT: .long .Ltmp{{.*}}@IMGREL ; X64-NEXT: .long 2 diff --git a/llvm/test/CodeGen/X86/win64-seh-epilogue-statepoint.ll b/llvm/test/CodeGen/X86/win64-seh-epilogue-statepoint.ll index bc5be7af6c7cf..eec029a0bd89e 100644 --- a/llvm/test/CodeGen/X86/win64-seh-epilogue-statepoint.ll +++ b/llvm/test/CodeGen/X86/win64-seh-epilogue-statepoint.ll @@ -9,7 +9,6 @@ define i32 @foobar() gc "statepoint-example" personality ptr @__gxx_personality_ ; CHECK-NEXT: .seh_endprologue ; CHECK-NEXT: callq bar ; CHECK-NEXT: .Ltmp0: -; CHECK-NEXT: nop ; CHECK-NEXT: .seh_startepilogue ; CHECK-NEXT: addq $40, %rsp ; CHECK-NEXT: .seh_endepilogue diff --git a/llvm/test/CodeGen/X86/wineh-coreclr.ll b/llvm/test/CodeGen/X86/wineh-coreclr.ll index baf5eaa29d281..a3d0fde76c458 100644 --- a/llvm/test/CodeGen/X86/wineh-coreclr.ll +++ b/llvm/test/CodeGen/X86/wineh-coreclr.ll @@ -38,6 +38,7 @@ entry: ; CHECK: [[test1_before_f1:.+]]: ; CHECK-NEXT: movl $1, %ecx ; CHECK-NEXT: callq f +; CHECK-NEXT: nop ; CHECK-NEXT: [[test1_after_f1:.+]]: invoke void @f(i32 1) to label %inner_try unwind label %finally @@ -46,6 +47,7 @@ inner_try: ; CHECK: [[test1_before_f2:.+]]: ; CHECK-NEXT: movl $2, %ecx ; CHECK-NEXT: callq f +; CHECK-NEXT: nop ; CHECK-NEXT: [[test1_after_f2:.+]]: invoke void @f(i32 2) to label %finally.clone unwind label %exn.dispatch @@ -69,6 +71,7 @@ catch1: ; CHECK: [[test1_before_f3:.+]]: ; CHECK-NEXT: movl $3, %ecx ; CHECK-NEXT: callq f +; CHECK-NEXT: nop ; CHECK-NEXT: [[test1_after_f3:.+]]: invoke void @f(i32 3) [ "funclet"(token %catch.pad1) ] to label %catch1.ret unwind label %finally @@ -92,6 +95,7 @@ catch2: ; CHECK: [[test1_before_f4:.+]]: ; CHECK-NEXT: movl $4, %ecx ; CHECK-NEXT: callq f +; CHECK-NEXT: nop ; CHECK-NEXT: [[test1_after_f4:.+]]: invoke void @f(i32 4) [ "funclet"(token %catch.pad2) ] to label %try_in_catch unwind label %finally @@ -100,6 +104,7 @@ try_in_catch: ; CHECK: [[test1_before_f5:.+]]: ; CHECK-NEXT: movl $5, %ecx ; CHECK-NEXT: callq f +; CHECK-NEXT: nop ; CHECK-NEXT: [[test1_after_f5:.+]]: invoke void @f(i32 5) [ "funclet"(token %catch.pad2) ] to label %catch2.ret unwind label %fault @@ -116,6 +121,7 @@ fault: ; CHECK: [[test1_before_f6:.+]]: ; CHECK-NEXT: movl $6, %ecx ; CHECK-NEXT: callq f +; CHECK-NEXT: nop ; CHECK-NEXT: [[test1_after_f6:.+]]: invoke void @f(i32 6) [ "funclet"(token %fault.pad) ] to label %fault.ret unwind label %finally @@ -312,6 +318,7 @@ unreachable: ; CHECK: [[test2_before_f1:.+]]: ; CHECK-NEXT: movl $1, %ecx ; CHECK-NEXT: callq f +; CHECK-NEXT: nop ; CHECK-NEXT: [[test2_after_f1:.+]]: ; CHECK: .seh_proc [[test2_catch1:[^ ]+]] ; CHECK: .seh_proc [[test2_catch2:[^ ]+]] @@ -320,6 +327,7 @@ unreachable: ; CHECK: [[test2_before_f2:.+]]: ; CHECK-NEXT: movl $2, %ecx ; CHECK-NEXT: callq f +; CHECK-NEXT: nop ; CHECK-NEXT: [[test2_after_f2:.+]]: ; CHECK: int3 ; CHECK: [[test2_end:.*func_end.*]]: @@ -448,6 +456,7 @@ entry: ; CHECK: [[test3_before_f1:.+]]: ; CHECK-NEXT: movl $1, %ecx ; CHECK-NEXT: callq f +; CHECK-NEXT: nop ; CHECK-NEXT: [[test3_after_f1:.+]]: invoke void @f(i32 1) to label %exit unwind label %fault1 @@ -474,6 +483,7 @@ fault4: ; CHECK: [[test3_before_f6:.+]]: ; CHECK-NEXT: movl $6, %ecx ; CHECK-NEXT: callq f +; CHECK-NEXT: nop ; CHECK-NEXT: [[test3_after_f6:.+]]: invoke void @f(i32 6) ["funclet"(token %fault.pad4)] to label %fault4.cont unwind label %exn.dispatch1 @@ -482,6 +492,7 @@ fault4.cont: ; CHECK: [[test3_before_f7:.+]]: ; CHECK-NEXT: movl $7, %ecx ; CHECK-NEXT: callq f +; CHECK-NEXT: nop ; CHECK-NEXT: [[test3_after_f7:.+]]: invoke void @f(i32 7) ["funclet"(token %fault.pad4)] to label %unreachable unwind label %fault5 @@ -512,6 +523,7 @@ unreachable: ; CHECK: [[test3_before_f4:.+]]: ; CHECK-NEXT: movl $4, %ecx ; CHECK-NEXT: callq f +; CHECK-NEXT: nop ; CHECK-NEXT: [[test3_after_f4:.+]]: ; CHECK: int3 ; CHECK: .seh_proc [[test3_fault2:[^ ]+]] @@ -520,6 +532,7 @@ unreachable: ; CHECK: [[test3_before_f3:.+]]: ; CHECK-NEXT: movl $3, %ecx ; CHECK-NEXT: callq f +; CHECK-NEXT: nop ; CHECK-NEXT: [[test3_after_f3:.+]]: ; CHECK: int3 ; CHECK: .seh_proc [[test3_fault1:[^ ]+]] @@ -528,6 +541,7 @@ unreachable: ; CHECK: [[test3_before_f2:.+]]: ; CHECK-NEXT: movl $2, %ecx ; CHECK-NEXT: callq f +; CHECK-NEXT: nop ; CHECK-NEXT: [[test3_after_f2:.+]]: ; CHECK: int3 ; CHECK: [[test3_end:.*func_end.*]]: diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/mips64_eh.ll.expected b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/mips64_eh.ll.expected index 897209a566149..56058bbc4c402 100644 --- a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/mips64_eh.ll.expected +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/mips64_eh.ll.expected @@ -8,17 +8,17 @@ define i32 @main() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to ; CHECK-NEXT: .cfi_def_cfa_offset 16 ; CHECK-NEXT: sd $ra, 8($sp) # 8-byte Folded Spill ; CHECK-NEXT: .cfi_offset 31, -8 -; CHECK-NEXT: .Ltmp0: +; CHECK-NEXT: .Ltmp0: # EH_LABEL ; CHECK-NEXT: jal foo ; CHECK-NEXT: nop -; CHECK-NEXT: .Ltmp1: +; CHECK-NEXT: .Ltmp1: # EH_LABEL ; CHECK-NEXT: # %bb.1: # %good ; CHECK-NEXT: addiu $2, $zero, 5 ; CHECK-NEXT: ld $ra, 8($sp) # 8-byte Folded Reload ; CHECK-NEXT: jr $ra ; CHECK-NEXT: daddiu $sp, $sp, 16 ; CHECK-NEXT: .LBB0_2: # %bad -; CHECK-NEXT: .Ltmp2: +; CHECK-NEXT: .Ltmp2: # EH_LABEL ; CHECK-NEXT: jal _Unwind_Resume ; CHECK-NEXT: nop %1 = invoke i32 @foo() to label %good unwind label %bad >From c231d98c2052911af6850c1c20d9597e788f838a Mon Sep 17 00:00:00 2001 From: Arlie Davis <arlie.da...@microsoft.com> Date: Wed, 18 Jun 2025 11:47:01 -0700 Subject: [PATCH 4/4] adjust another test --- llvm/test/CodeGen/X86/win64_frame.ll | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/llvm/test/CodeGen/X86/win64_frame.ll b/llvm/test/CodeGen/X86/win64_frame.ll index c4b36c5e263c8..95fb03d16efae 100644 --- a/llvm/test/CodeGen/X86/win64_frame.ll +++ b/llvm/test/CodeGen/X86/win64_frame.ll @@ -1,6 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py -; RUN: llc < %s -mtriple=x86_64-pc-win32 | FileCheck %s -; RUN: llc < %s -mtriple=x86_64-pc-win32 -mattr=+sahf | FileCheck %s +; RUN: llc < %s -mtriple=x86_64-pc-win32 | FileCheck --check-prefixes=CHECK,WIN64 %s +; RUN: llc < %s -mtriple=x86_64-pc-win32 -mattr=+sahf | FileCheck --check-prefixes=CHECK,WIN64 %s ; RUN: llc < %s -mtriple=x86_64-uefi | FileCheck %s ; RUN: llc < %s -mtriple=x86_64-uefi -mattr=+sahf | FileCheck %s @@ -101,7 +101,8 @@ define void @f5() "frame-pointer"="all" { ; CHECK-NEXT: .seh_endprologue ; CHECK-NEXT: leaq -92(%rbp), %rcx ; CHECK-NEXT: callq external -; CHECK-NEXT: nop +; UEFI does not have SEH, so does not require NOP +; WIN64-NEXT: nop ; CHECK-NEXT: .seh_startepilogue ; CHECK-NEXT: addq $336, %rsp # imm = 0x150 ; CHECK-NEXT: popq %rbp @@ -125,7 +126,8 @@ define void @f6(i32 %p, ...) "frame-pointer"="all" { ; CHECK-NEXT: .seh_endprologue ; CHECK-NEXT: leaq -92(%rbp), %rcx ; CHECK-NEXT: callq external -; CHECK-NEXT: nop +; UEFI does not have SEH, so does not require NOP +; WIN64-NEXT: nop ; CHECK-NEXT: .seh_startepilogue ; CHECK-NEXT: addq $336, %rsp # imm = 0x150 ; CHECK-NEXT: popq %rbp _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits