https://github.com/dpaoliello updated https://github.com/llvm/llvm-project/pull/143577
>From 07390317820324535fc75f7bd9dd53c2e65bda41 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello <dan...@microsoft.com> Date: Fri, 6 Jun 2025 16:39:07 -0700 Subject: [PATCH] Add support for requiring Win x64 Unwind V2 --- clang/include/clang/Basic/CodeGenOptions.def | 6 +- clang/include/clang/Driver/Options.td | 17 +- clang/lib/CodeGen/CodeGenModule.cpp | 6 +- clang/lib/Driver/ToolChains/Clang.cpp | 9 +- clang/test/CodeGen/epilog-unwind.c | 10 +- clang/test/Driver/cl-options.c | 6 +- llvm/include/llvm/IR/Module.h | 4 + llvm/include/llvm/Support/CodeGen.h | 9 + llvm/lib/IR/Module.cpp | 7 + llvm/lib/Target/X86/X86WinEHUnwindV2.cpp | 152 +++++++-- .../CodeGen/X86/win64-eh-unwindv2-errors.mir | 318 ++++++++++++++++++ 11 files changed, 501 insertions(+), 43 deletions(-) create mode 100644 llvm/test/CodeGen/X86/win64-eh-unwindv2-errors.mir diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index fa9474d63ae42..32a2ee0e23200 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -481,8 +481,10 @@ CODEGENOPT(StaticClosure, 1, 0) /// Assume that UAVs/SRVs may alias CODEGENOPT(ResMayAlias, 1, 0) -/// Enables unwind v2 (epilog) information for x64 Windows. -CODEGENOPT(WinX64EHUnwindV2, 1, 0) +/// Controls how unwind v2 (epilog) information should be generated for x64 +/// Windows. +ENUM_CODEGENOPT(WinX64EHUnwindV2, llvm::WinX64EHUnwindV2Mode, + 2, llvm::WinX64EHUnwindV2Mode::Disabled) /// FIXME: Make DebugOptions its own top-level .def file. #include "DebugOptions.def" diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 89c63fb3397d3..d13ce5ce16467 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2167,11 +2167,14 @@ defm assume_nothrow_exception_dtor: BoolFOption<"assume-nothrow-exception-dtor", LangOpts<"AssumeNothrowExceptionDtor">, DefaultFalse, PosFlag<SetTrue, [], [ClangOption, CC1Option], "Assume that exception objects' destructors are non-throwing">, NegFlag<SetFalse>>; -defm winx64_eh_unwindv2 : BoolFOption<"winx64-eh-unwindv2", - CodeGenOpts<"WinX64EHUnwindV2">, DefaultFalse, - PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">, - NegFlag<SetFalse, [], [ClangOption], "Disable">, - BothFlags<[], [ClangOption], " unwind v2 (epilog) information for x64 Windows">>; +def winx64_eh_unwindv2 + : Joined<["-"], "fwinx64-eh-unwindv2=">, Group<f_Group>, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Generate unwind v2 (epilog) information for x64 Windows">, + Values<"disabled,best-effort,required">, + NormalizedValues<["Disabled", "BestEffort", "Required"]>, + NormalizedValuesScope<"llvm::WinX64EHUnwindV2Mode">, + MarshallingInfoEnum<CodeGenOpts<"WinX64EHUnwindV2">, "Disabled">; def fexcess_precision_EQ : Joined<["-"], "fexcess-precision=">, Group<f_Group>, Visibility<[ClangOption, CLOption]>, HelpText<"Allows control over excess precision on targets where native " @@ -8968,7 +8971,9 @@ def _SLASH_volatile_Group : OptionGroup<"</volatile group>">, Group<cl_compile_Group>; def _SLASH_d2epilogunwind : CLFlag<"d2epilogunwind">, - HelpText<"Enable unwind v2 (epilog) information for x64 Windows">; + HelpText<"Best effort generate unwind v2 (epilog) information for x64 Windows">; +def _SLASH_d2epilogunwindrequirev2 : CLFlag<"d2epilogunwindrequirev2">, + HelpText<"Require generation of unwind v2 (epilog) information for x64 Windows">; def _SLASH_EH : CLJoined<"EH">, HelpText<"Set exception handling model">; def _SLASH_EP : CLFlag<"EP">, HelpText<"Disable linemarker output and preprocess to stdout">; diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 16e49aab4fe61..148c31912acbd 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1314,8 +1314,10 @@ void CodeGenModule::Release() { 1); // Enable unwind v2 (epilog). - if (CodeGenOpts.WinX64EHUnwindV2) - getModule().addModuleFlag(llvm::Module::Warning, "winx64-eh-unwindv2", 1); + if (CodeGenOpts.getWinX64EHUnwindV2() != llvm::WinX64EHUnwindV2Mode::Disabled) + getModule().addModuleFlag( + llvm::Module::Warning, "winx64-eh-unwindv2", + static_cast<unsigned>(CodeGenOpts.getWinX64EHUnwindV2())); // Indicate whether this Module was compiled with -fopenmp if (getLangOpts().OpenMP && !getLangOpts().OpenMPSimd) diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 65f101ddf1d0a..dce5d132b10a7 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7471,8 +7471,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, } // Unwind v2 (epilog) information for x64 Windows. - Args.addOptInFlag(CmdArgs, options::OPT_fwinx64_eh_unwindv2, - options::OPT_fno_winx64_eh_unwindv2); + Args.AddLastArg(CmdArgs, options::OPT_winx64_eh_unwindv2); // C++ "sane" operator new. Args.addOptOutFlag(CmdArgs, options::OPT_fassume_sane_operator_new, @@ -8529,8 +8528,10 @@ void Clang::AddClangCLArgs(const ArgList &Args, types::ID InputType, CmdArgs.push_back("-fms-kernel"); // Unwind v2 (epilog) information for x64 Windows. - if (Args.hasArg(options::OPT__SLASH_d2epilogunwind)) - CmdArgs.push_back("-fwinx64-eh-unwindv2"); + if (Args.hasArg(options::OPT__SLASH_d2epilogunwindrequirev2)) + CmdArgs.push_back("-fwinx64-eh-unwindv2=required"); + else if (Args.hasArg(options::OPT__SLASH_d2epilogunwind)) + CmdArgs.push_back("-fwinx64-eh-unwindv2=best-effort"); for (const Arg *A : Args.filtered(options::OPT__SLASH_guard)) { StringRef GuardArgs = A->getValue(); diff --git a/clang/test/CodeGen/epilog-unwind.c b/clang/test/CodeGen/epilog-unwind.c index 991ff09fb37cf..b2f7497b455b6 100644 --- a/clang/test/CodeGen/epilog-unwind.c +++ b/clang/test/CodeGen/epilog-unwind.c @@ -1,9 +1,11 @@ // RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s -check-prefix=DISABLED -// RUN: %clang_cc1 -fwinx64-eh-unwindv2 -emit-llvm %s -o - | FileCheck %s -check-prefix=ENABLED -// RUN: %clang -fwinx64-eh-unwindv2 -S -emit-llvm %s -o - | FileCheck %s -check-prefix=ENABLED -// RUN: %clang -fno-winx64-eh-unwindv2 -S -emit-llvm %s -o - | FileCheck %s -check-prefix=DISABLED +// RUN: %clang_cc1 -fwinx64-eh-unwindv2=disabled -emit-llvm %s -o - | FileCheck %s -check-prefix=DISABLED +// RUN: %clang_cc1 -fwinx64-eh-unwindv2=best-effort -emit-llvm %s -o - | FileCheck %s -check-prefix=BESTEFFORT +// RUN: %clang_cc1 -fwinx64-eh-unwindv2=required -emit-llvm %s -o - | FileCheck %s -check-prefix=REQUIRED +// RUN: %clang -fwinx64-eh-unwindv2=best-effort -S -emit-llvm %s -o - | FileCheck %s -check-prefix=BESTEFFORT void f(void) {} -// ENABLED: !"winx64-eh-unwindv2", i32 1} +// BESTEFFORT: !"winx64-eh-unwindv2", i32 1} +// REQUIRED: !"winx64-eh-unwindv2", i32 2} // DISABLED-NOT: "winx64-eh-unwindv2" diff --git a/clang/test/Driver/cl-options.c b/clang/test/Driver/cl-options.c index 0535285862b9f..eb079895a0a88 100644 --- a/clang/test/Driver/cl-options.c +++ b/clang/test/Driver/cl-options.c @@ -821,7 +821,11 @@ // ARM64EC_OVERRIDE: warning: /arm64EC has been overridden by specified target: x86_64-pc-windows-msvc; option ignored // RUN: %clang_cl /d2epilogunwind /c -### -- %s 2>&1 | FileCheck %s --check-prefix=EPILOGUNWIND -// EPILOGUNWIND: -fwinx64-eh-unwindv2 +// EPILOGUNWIND: -fwinx64-eh-unwindv2=best-effort + +// RUN: %clang_cl /d2epilogunwindrequirev2 /c -### -- %s 2>&1 | FileCheck %s --check-prefix=EPILOGUNWINDREQUIREV2 +// RUN: %clang_cl /d2epilogunwindrequirev2 /d2epilogunwind /c -### -- %s 2>&1 | FileCheck %s --check-prefix=EPILOGUNWINDREQUIREV2 +// EPILOGUNWINDREQUIREV2: -fwinx64-eh-unwindv2=require // RUN: %clang_cl /funcoverride:override_me1 /funcoverride:override_me2 /c -### -- %s 2>&1 | FileCheck %s --check-prefix=FUNCOVERRIDE // FUNCOVERRIDE: -loader-replaceable-function=override_me1 diff --git a/llvm/include/llvm/IR/Module.h b/llvm/include/llvm/IR/Module.h index 7a26efb74b324..9d90a5755f7ed 100644 --- a/llvm/include/llvm/IR/Module.h +++ b/llvm/include/llvm/IR/Module.h @@ -1061,6 +1061,10 @@ class LLVM_ABI Module { /// Returns target-abi from MDString, null if target-abi is absent. StringRef getTargetABIFromMD(); + + /// Get how unwind v2 (epilog) information should be generated for x64 + /// Windows. + WinX64EHUnwindV2Mode getWinX64EHUnwindV2Mode() const; }; /// Given "llvm.used" or "llvm.compiler.used" as a global name, collect the diff --git a/llvm/include/llvm/Support/CodeGen.h b/llvm/include/llvm/Support/CodeGen.h index 0e42789ba932e..48745f7f4d2a6 100644 --- a/llvm/include/llvm/Support/CodeGen.h +++ b/llvm/include/llvm/Support/CodeGen.h @@ -130,6 +130,15 @@ namespace llvm { Invalid = 2, ///< Not used. }; + enum class WinX64EHUnwindV2Mode { + // Don't use unwind v2 (i.e., use v1). + Disabled = 0, + // Use unwind v2 here possible, otherwise fallback to v1. + BestEffort = 1, + // Use unwind v2 everywhere, otherwise raise an error. + Required = 2, + }; + } // namespace llvm #endif diff --git a/llvm/lib/IR/Module.cpp b/llvm/lib/IR/Module.cpp index 0a47f98619691..7b3a0bb333c05 100644 --- a/llvm/lib/IR/Module.cpp +++ b/llvm/lib/IR/Module.cpp @@ -919,3 +919,10 @@ StringRef Module::getTargetABIFromMD() { TargetABI = TargetABIMD->getString(); return TargetABI; } + +WinX64EHUnwindV2Mode Module::getWinX64EHUnwindV2Mode() const { + Metadata *MD = getModuleFlag("winx64-eh-unwindv2"); + if (auto *CI = mdconst::dyn_extract_or_null<ConstantInt>(MD)) + return static_cast<WinX64EHUnwindV2Mode>(CI->getZExtValue()); + return WinX64EHUnwindV2Mode::Disabled; +} diff --git a/llvm/lib/Target/X86/X86WinEHUnwindV2.cpp b/llvm/lib/Target/X86/X86WinEHUnwindV2.cpp index 2c1f9a5746e38..e9081a4ae4e72 100644 --- a/llvm/lib/Target/X86/X86WinEHUnwindV2.cpp +++ b/llvm/lib/Target/X86/X86WinEHUnwindV2.cpp @@ -20,6 +20,7 @@ #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/TargetInstrInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/Module.h" using namespace llvm; @@ -31,6 +32,15 @@ STATISTIC(MeetsUnwindV2Criteria, STATISTIC(FailsUnwindV2Criteria, "Number of functions that fail Unwind v2 criteria"); +static cl::opt<unsigned> MaximumUnwindCodes( + "x86-wineh-unwindv2-max-unwind-codes", cl::Hidden, + cl::desc("Maximum number of unwind codes permitted in each unwind info."), + cl::init(UINT8_MAX)); + +static cl::opt<unsigned> + ForceMode("x86-wineh-unwindv2-force-mode", cl::Hidden, + cl::desc("Overwrites the Unwind v2 mode for testing purposes.")); + namespace { class X86WinEHUnwindV2 : public MachineFunctionPass { @@ -44,10 +54,12 @@ class X86WinEHUnwindV2 : public MachineFunctionPass { StringRef getPassName() const override { return "WinEH Unwind V2"; } bool runOnMachineFunction(MachineFunction &MF) override; - bool rejectCurrentFunction() const { - FailsUnwindV2Criteria++; - return false; - } + +private: + /// Rejects the current function due to an internal error within LLVM. + static bool rejectCurrentFunctionInternalError(const MachineFunction &MF, + WinX64EHUnwindV2Mode Mode, + StringRef Reason); }; enum class FunctionState { @@ -69,8 +81,21 @@ FunctionPass *llvm::createX86WinEHUnwindV2Pass() { return new X86WinEHUnwindV2(); } +DebugLoc findDebugLoc(const MachineBasicBlock &MBB) { + for (const MachineInstr &MI : MBB) + if (MI.getDebugLoc()) + return MI.getDebugLoc(); + + return DebugLoc::getUnknown(); +} + bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { - if (!MF.getFunction().getParent()->getModuleFlag("winx64-eh-unwindv2")) + WinX64EHUnwindV2Mode Mode = + ForceMode.getNumOccurrences() + ? static_cast<WinX64EHUnwindV2Mode>(ForceMode.getValue()) + : MF.getFunction().getParent()->getWinX64EHUnwindV2Mode(); + + if (Mode == WinX64EHUnwindV2Mode::Disabled) return false; // Current state of processing the function. We'll assume that all functions @@ -80,6 +105,7 @@ bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { // Prolog information. SmallVector<int64_t> PushedRegs; bool HasStackAlloc = false; + unsigned ApproximatePrologCodeCount = 0; // Requested changes. SmallVector<MachineInstr *> UnwindV2StartLocations; @@ -99,6 +125,7 @@ bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { case X86::SEH_PushReg: if (State != FunctionState::InProlog) llvm_unreachable("SEH_PushReg outside of prolog"); + ApproximatePrologCodeCount++; PushedRegs.push_back(MI.getOperand(0).getImm()); break; @@ -106,9 +133,26 @@ bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { case X86::SEH_SetFrame: if (State != FunctionState::InProlog) llvm_unreachable("SEH_StackAlloc or SEH_SetFrame outside of prolog"); + // Assume a large alloc... + ApproximatePrologCodeCount += + (MI.getOpcode() == X86::SEH_StackAlloc) ? 3 : 1; HasStackAlloc = true; break; + case X86::SEH_SaveReg: + case X86::SEH_SaveXMM: + if (State != FunctionState::InProlog) + llvm_unreachable("SEH_SaveXMM or SEH_SaveReg outside of prolog"); + // Assume a big reg... + ApproximatePrologCodeCount += 3; + break; + + case X86::SEH_PushFrame: + if (State != FunctionState::InProlog) + llvm_unreachable("SEH_PushFrame outside of prolog"); + ApproximatePrologCodeCount++; + break; + case X86::SEH_EndPrologue: if (State != FunctionState::InProlog) llvm_unreachable("SEH_EndPrologue outside of prolog"); @@ -127,10 +171,16 @@ bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { case X86::SEH_EndEpilogue: if (State != FunctionState::InEpilog) llvm_unreachable("SEH_EndEpilogue outside of epilog"); - if ((HasStackAlloc != HasStackDealloc) || - (PoppedRegCount != PushedRegs.size())) - // Non-canonical epilog, reject the function. - return rejectCurrentFunction(); + if (HasStackAlloc != HasStackDealloc) + return rejectCurrentFunctionInternalError( + MF, Mode, + "The prolog made a stack allocation, " + "but the epilog did not deallocate it"); + if (PoppedRegCount != PushedRegs.size()) + return rejectCurrentFunctionInternalError( + MF, Mode, + "The prolog pushed more registers than " + "the epilog popped"); // If we didn't find the start location, then use the end of the // epilog. @@ -145,13 +195,26 @@ bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { if (State == FunctionState::InEpilog) { // If the prolog contains a stack allocation, then the first // instruction in the epilog must be to adjust the stack pointer. - if (!HasStackAlloc || HasStackDealloc || (PoppedRegCount > 0)) { - return rejectCurrentFunction(); - } + if (!HasStackAlloc) + return rejectCurrentFunctionInternalError( + MF, Mode, + "The epilog is deallocating a stack " + "allocation, but the prolog did " + "not allocate one"); + if (HasStackDealloc) + return rejectCurrentFunctionInternalError( + MF, Mode, + "The epilog is deallocating the stack " + "allocation more than once"); + if (PoppedRegCount > 0) + llvm_unreachable( + "Should have raised an error: either popping before " + "deallocating or deallocating without an allocation"); + HasStackDealloc = true; } else if (State == FunctionState::FinishedEpilog) - // Unexpected instruction after the epilog. - return rejectCurrentFunction(); + return rejectCurrentFunctionInternalError( + MF, Mode, "Unexpected mov or add instruction after the epilog"); break; case X86::POP64r: @@ -159,12 +222,22 @@ bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { // After the stack pointer has been adjusted, the epilog must // POP each register in reverse order of the PUSHes in the prolog. PoppedRegCount++; - if ((HasStackAlloc != HasStackDealloc) || - (PoppedRegCount > PushedRegs.size()) || - (PushedRegs[PushedRegs.size() - PoppedRegCount] != - MI.getOperand(0).getReg())) { - return rejectCurrentFunction(); - } + if (HasStackAlloc != HasStackDealloc) + return rejectCurrentFunctionInternalError( + MF, Mode, + "Cannot pop registers before the stack " + "allocation has been deallocated"); + if (PoppedRegCount > PushedRegs.size()) + return rejectCurrentFunctionInternalError( + MF, Mode, + "The epilog is popping more registers than the prolog pushed"); + if (PushedRegs[PushedRegs.size() - PoppedRegCount] != + MI.getOperand(0).getReg()) + return rejectCurrentFunctionInternalError( + MF, Mode, + "The epilog is popping a registers in " + "a different order than the " + "prolog pushed them"); // Unwind v2 records the size of the epilog not from where we place // SEH_BeginEpilogue (as that contains the instruction to adjust the @@ -176,7 +249,8 @@ bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { } } else if (State == FunctionState::FinishedEpilog) // Unexpected instruction after the epilog. - return rejectCurrentFunction(); + return rejectCurrentFunctionInternalError( + MF, Mode, "Registers are being popped after the epilog"); break; default: @@ -191,7 +265,8 @@ bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { if ((State == FunctionState::FinishedEpilog) || (State == FunctionState::InEpilog)) // Unknown instruction in or after the epilog. - return rejectCurrentFunction(); + return rejectCurrentFunctionInternalError( + MF, Mode, "Unexpected instruction in or after the epilog"); } } } @@ -203,6 +278,25 @@ bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { return false; } + MachineBasicBlock &FirstMBB = MF.front(); + // Assume +1 for the "header" UOP_Epilog that contains the epilog size, and + // that we won't be able to use the "last epilog at the end of function" + // optimization. + if (ApproximatePrologCodeCount + UnwindV2StartLocations.size() + 1 > + static_cast<unsigned>(MaximumUnwindCodes)) { + if (Mode == WinX64EHUnwindV2Mode::Required) + MF.getFunction().getContext().diagnose(DiagnosticInfoGenericWithLoc( + "Windows x64 Unwind v2 is required, but the function '" + + MF.getName() + + "' has too many unwind codes. Try splitting the function or " + "reducing the number of places where it exits early with a tail " + "call.", + MF.getFunction(), findDebugLoc(FirstMBB))); + + FailsUnwindV2Criteria++; + return false; + } + MeetsUnwindV2Criteria++; // Emit the pseudo instruction that marks the start of each epilog. @@ -212,10 +306,20 @@ bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { TII->get(X86::SEH_UnwindV2Start)); } // Note that the function is using Unwind v2. - MachineBasicBlock &FirstMBB = MF.front(); - BuildMI(FirstMBB, FirstMBB.front(), FirstMBB.front().getDebugLoc(), + BuildMI(FirstMBB, FirstMBB.front(), findDebugLoc(FirstMBB), TII->get(X86::SEH_UnwindVersion)) .addImm(2); return true; } + +bool X86WinEHUnwindV2::rejectCurrentFunctionInternalError( + const MachineFunction &MF, WinX64EHUnwindV2Mode Mode, StringRef Reason) { + if (Mode == WinX64EHUnwindV2Mode::Required) + reportFatalInternalError("Windows x64 Unwind v2 is required, but LLVM has " + "generated incompatible code in function '" + + MF.getName() + "': " + Reason); + + FailsUnwindV2Criteria++; + return false; +} diff --git a/llvm/test/CodeGen/X86/win64-eh-unwindv2-errors.mir b/llvm/test/CodeGen/X86/win64-eh-unwindv2-errors.mir new file mode 100644 index 0000000000000..dd886f8ee6172 --- /dev/null +++ b/llvm/test/CodeGen/X86/win64-eh-unwindv2-errors.mir @@ -0,0 +1,318 @@ +# RUN: split-file %s %t + +# If we force "best effort" mode, then we won't see any errors, but we won't use +# v2. +# BESTEFFORT-NOT: SEH_UnwindVersion +# BESTEFFORT-NOT: SEH_UnwindV2Start + +;--- alloc_no_dealloc.mir +# RUN: not llc -mtriple=x86_64-unknown-windows-msvc -o - \ +# RUN: %t/alloc_no_dealloc.mir -run-pass=x86-wineh-unwindv2 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ALLOC-NO-DEALLOC +# RUN: llc -mtriple=x86_64-unknown-windows-msvc -o - %t/alloc_no_dealloc.mir \ +# RUN: -run-pass=x86-wineh-unwindv2 -x86-wineh-unwindv2-force-mode=1 | \ +# RUN: FileCheck %s --check-prefix=BESTEFFORT +# ALLOC-NO-DEALLOC: LLVM ERROR: Windows x64 Unwind v2 is required, but LLVM has generated incompatible code in function 'alloc_no_dealloc': +# ALLOC-NO-DEALLOC-SAME: The prolog made a stack allocation, but the epilog did not deallocate it + +--- | + define dso_local void @alloc_no_dealloc() local_unnamed_addr { + entry: + ret void + } + !llvm.module.flags = !{!0} + !0 = !{i32 1, !"winx64-eh-unwindv2", i32 2} +... +--- +name: alloc_no_dealloc +body: | + bb.0.entry: + $rsp = frame-setup SUB64ri32 $rsp, 40, implicit-def dead $eflags + frame-setup SEH_StackAlloc 40 + frame-setup SEH_EndPrologue + SEH_BeginEpilogue + SEH_EndEpilogue + RET64 +... + +;--- missed_push.mir +# RUN: not llc -mtriple=x86_64-unknown-windows-msvc -o - %t/missed_push.mir \ +# RUN: -run-pass=x86-wineh-unwindv2 2>&1 | FileCheck %s \ +# RUN: --check-prefix=MISSED-PUSH +# RUN: llc -mtriple=x86_64-unknown-windows-msvc -o - %t/missed_push.mir \ +# RUN: -run-pass=x86-wineh-unwindv2 -x86-wineh-unwindv2-force-mode=1 | \ +# RUN: FileCheck %s --check-prefix=BESTEFFORT +# MISSED-PUSH: LLVM ERROR: Windows x64 Unwind v2 is required, but LLVM has generated incompatible code in function 'missed_push': +# MISSED-PUSH-SAME: The prolog pushed more registers than the epilog popped + +--- | + define dso_local void @missed_push() local_unnamed_addr { + entry: + ret void + } + !llvm.module.flags = !{!0} + !0 = !{i32 1, !"winx64-eh-unwindv2", i32 2} +... +--- +name: missed_push +body: | + bb.0.entry: + frame-setup PUSH64r killed $rsi, implicit-def $rsp, implicit $rsp + frame-setup SEH_PushReg 60 + frame-setup PUSH64r killed $rdi, implicit-def $rsp, implicit $rsp + frame-setup SEH_PushReg 55 + frame-setup SEH_EndPrologue + SEH_BeginEpilogue + $rdi = frame-destroy POP64r implicit-def $rsp, implicit $rsp + SEH_EndEpilogue + RET64 +... + +;--- dealloc_no_alloc.mir +# RUN: not llc -mtriple=x86_64-unknown-windows-msvc -o - \ +# RUN: %t/dealloc_no_alloc.mir -run-pass=x86-wineh-unwindv2 2>&1 | \ +# RUN: FileCheck %s --check-prefix=DEALLOC-NO-ALLOC +# RUN: llc -mtriple=x86_64-unknown-windows-msvc -o - %t/dealloc_no_alloc.mir \ +# RUN: -run-pass=x86-wineh-unwindv2 -x86-wineh-unwindv2-force-mode=1 | \ +# RUN: FileCheck %s --check-prefix=BESTEFFORT +# DEALLOC-NO-ALLOC: LLVM ERROR: Windows x64 Unwind v2 is required, but LLVM has generated incompatible code in function 'dealloc_no_alloc': +# DEALLOC-NO-ALLOC-SAME: The epilog is deallocating a stack allocation, but the prolog did not allocate one + +--- | + define dso_local void @dealloc_no_alloc() local_unnamed_addr { + entry: + ret void + } + !llvm.module.flags = !{!0} + !0 = !{i32 1, !"winx64-eh-unwindv2", i32 2} +... +--- +name: dealloc_no_alloc +body: | + bb.0.entry: + frame-setup SEH_EndPrologue + SEH_BeginEpilogue + $rsp = frame-destroy ADD64ri32 $rsp, 40, implicit-def dead $eflags + SEH_EndEpilogue + RET64 +... + +;--- double_dealloc.mir +# RUN: not llc -mtriple=x86_64-unknown-windows-msvc -o - %t/double_dealloc.mir \ +# RUN: -run-pass=x86-wineh-unwindv2 2>&1 | FileCheck %s \ +# RUN: --check-prefix=DOUBLE-DEALLOC +# RUN: llc -mtriple=x86_64-unknown-windows-msvc -o - %t/double_dealloc.mir \ +# RUN: -run-pass=x86-wineh-unwindv2 -x86-wineh-unwindv2-force-mode=1 | \ +# RUN: FileCheck %s --check-prefix=BESTEFFORT +# DOUBLE-DEALLOC: LLVM ERROR: Windows x64 Unwind v2 is required, but LLVM has generated incompatible code in function 'double_dealloc': +# DOUBLE-DEALLOC-SAME: The epilog is deallocating the stack allocation more than once + +--- | + define dso_local void @double_dealloc() local_unnamed_addr { + entry: + ret void + } + !llvm.module.flags = !{!0} + !0 = !{i32 1, !"winx64-eh-unwindv2", i32 2} +... +--- +name: double_dealloc +body: | + bb.0.entry: + $rsp = frame-setup SUB64ri32 $rsp, 40, implicit-def dead $eflags + frame-setup SEH_StackAlloc 40 + frame-setup SEH_EndPrologue + SEH_BeginEpilogue + $rsp = frame-destroy ADD64ri32 $rsp, 40, implicit-def dead $eflags + $rsp = frame-destroy ADD64ri32 $rsp, 40, implicit-def dead $eflags + SEH_EndEpilogue + RET64 +... + +;--- dealloc_after_epilog.mir +# RUN: not llc -mtriple=x86_64-unknown-windows-msvc -o - \ +# RUN: %t/dealloc_after_epilog.mir -run-pass=x86-wineh-unwindv2 2>&1 | \ +# RUN: FileCheck %s --check-prefix=DEALLOC-AFTER-EPILOG +# RUN: llc -mtriple=x86_64-unknown-windows-msvc -o - \ +# RUN: %t/dealloc_after_epilog.mir -run-pass=x86-wineh-unwindv2 \ +# RUN: -x86-wineh-unwindv2-force-mode=1 | FileCheck %s \ +# RUN: --check-prefix=BESTEFFORT +# DEALLOC-AFTER-EPILOG: LLVM ERROR: Windows x64 Unwind v2 is required, but LLVM has generated incompatible code in function 'dealloc_after_epilog': +# DEALLOC-AFTER-EPILOG-SAME: Unexpected mov or add instruction after the epilog + +--- | + define dso_local void @dealloc_after_epilog() local_unnamed_addr { + entry: + ret void + } + !llvm.module.flags = !{!0} + !0 = !{i32 1, !"winx64-eh-unwindv2", i32 2} +... +--- +name: dealloc_after_epilog +body: | + bb.0.entry: + frame-setup SEH_EndPrologue + SEH_BeginEpilogue + SEH_EndEpilogue + $rsp = frame-destroy ADD64ri32 $rsp, 40, implicit-def dead $eflags + RET64 +... + +;--- pop_before_dealloc.mir +# RUN: not llc -mtriple=x86_64-unknown-windows-msvc -o - \ +# RUN: %t/pop_before_dealloc.mir -run-pass=x86-wineh-unwindv2 2>&1 | \ +# RUN: FileCheck %s --check-prefix=POP-BEFORE-DEALLOC +# RUN: llc -mtriple=x86_64-unknown-windows-msvc -o - %t/pop_before_dealloc.mir \ +# RUN: -run-pass=x86-wineh-unwindv2 -x86-wineh-unwindv2-force-mode=1 | \ +# RUN: FileCheck %s --check-prefix=BESTEFFORT +# POP-BEFORE-DEALLOC: LLVM ERROR: Windows x64 Unwind v2 is required, but LLVM has generated incompatible code in function 'pop_before_dealloc': +# POP-BEFORE-DEALLOC-SAME: Cannot pop registers before the stack allocation has been deallocated + +--- | + define dso_local void @pop_before_dealloc() local_unnamed_addr { + entry: + ret void + } + !llvm.module.flags = !{!0} + !0 = !{i32 1, !"winx64-eh-unwindv2", i32 2} +... +--- +name: pop_before_dealloc +body: | + bb.0.entry: + frame-setup PUSH64r killed $rdi, implicit-def $rsp, implicit $rsp + frame-setup SEH_PushReg 55 + $rsp = frame-setup SUB64ri32 $rsp, 40, implicit-def dead $eflags + frame-setup SEH_StackAlloc 40 + frame-setup SEH_EndPrologue + SEH_BeginEpilogue + $rdi = frame-destroy POP64r implicit-def $rsp, implicit $rsp + $rsp = frame-destroy ADD64ri32 $rsp, 40, implicit-def dead $eflags + SEH_EndEpilogue + RET64 +... + +;--- too_many_pops.mir +# RUN: not llc -mtriple=x86_64-unknown-windows-msvc -o - %t/too_many_pops.mir \ +# RUN: -run-pass=x86-wineh-unwindv2 2>&1 | FileCheck %s \ +# RUN: --check-prefix=TOO-MANY-POPS +# RUN: llc -mtriple=x86_64-unknown-windows-msvc -o - %t/too_many_pops.mir \ +# RUN: -run-pass=x86-wineh-unwindv2 -x86-wineh-unwindv2-force-mode=1 | \ +# RUN: FileCheck %s --check-prefix=BESTEFFORT +# TOO-MANY-POPS: LLVM ERROR: Windows x64 Unwind v2 is required, but LLVM has generated incompatible code in function 'too_many_pops': +# TOO-MANY-POPS-SAME: The epilog is popping more registers than the prolog pushed + +--- | + define dso_local void @too_many_pops() local_unnamed_addr { + entry: + ret void + } + !llvm.module.flags = !{!0} + !0 = !{i32 1, !"winx64-eh-unwindv2", i32 2} +... +--- +name: too_many_pops +body: | + bb.0.entry: + frame-setup PUSH64r killed $rdi, implicit-def $rsp, implicit $rsp + frame-setup SEH_PushReg 55 + frame-setup SEH_EndPrologue + SEH_BeginEpilogue + $rdi = frame-destroy POP64r implicit-def $rsp, implicit $rsp + $rsi = frame-destroy POP64r implicit-def $rsp, implicit $rsp + SEH_EndEpilogue + RET64 +... + +;--- pop_in_wrong_order.mir +# RUN: not llc -mtriple=x86_64-unknown-windows-msvc -o - \ +# RUN: %t/pop_in_wrong_order.mir -run-pass=x86-wineh-unwindv2 2>&1 | \ +# RUN: FileCheck %s --check-prefix=POP-WRONG-ORDER +# RUN: llc -mtriple=x86_64-unknown-windows-msvc -o - %t/pop_in_wrong_order.mir \ +# RUN: -run-pass=x86-wineh-unwindv2 -x86-wineh-unwindv2-force-mode=1 | \ +# RUN: FileCheck %s --check-prefix=BESTEFFORT +# POP-WRONG-ORDER: LLVM ERROR: Windows x64 Unwind v2 is required, but LLVM has generated incompatible code in function 'pop_in_wrong_order': +# POP-WRONG-ORDER-SAME: The epilog is popping a registers in a different order than the prolog pushed them + +--- | + define dso_local void @pop_in_wrong_order() local_unnamed_addr { + entry: + ret void + } + !llvm.module.flags = !{!0} + !0 = !{i32 1, !"winx64-eh-unwindv2", i32 2} +... +--- +name: pop_in_wrong_order +body: | + bb.0.entry: + frame-setup PUSH64r killed $rdi, implicit-def $rsp, implicit $rsp + frame-setup SEH_PushReg 55 + frame-setup PUSH64r killed $rsi, implicit-def $rsp, implicit $rsp + frame-setup SEH_PushReg 60 + frame-setup SEH_EndPrologue + SEH_BeginEpilogue + $rdi = frame-destroy POP64r implicit-def $rsp, implicit $rsp + $rsi = frame-destroy POP64r implicit-def $rsp, implicit $rsp + SEH_EndEpilogue + RET64 +... + +;--- pop_after_epilog.mir +# RUN: not llc -mtriple=x86_64-unknown-windows-msvc -o - \ +# RUN: %t/pop_after_epilog.mir -run-pass=x86-wineh-unwindv2 2>&1 | \ +# RUN: FileCheck %s --check-prefix=POP-AFTER-EPILOG +# RUN: llc -mtriple=x86_64-unknown-windows-msvc -o - %t/pop_after_epilog.mir \ +# RUN: -run-pass=x86-wineh-unwindv2 -x86-wineh-unwindv2-force-mode=1 | \ +# RUN: FileCheck %s --check-prefix=BESTEFFORT +# POP-AFTER-EPILOG: LLVM ERROR: Windows x64 Unwind v2 is required, but LLVM has generated incompatible code in function 'pop_after_epilog': +# POP-AFTER-EPILOG-SAME: Registers are being popped after the epilog + +--- | + define dso_local void @pop_after_epilog() local_unnamed_addr { + entry: + ret void + } + !llvm.module.flags = !{!0} + !0 = !{i32 1, !"winx64-eh-unwindv2", i32 2} +... +--- +name: pop_after_epilog +body: | + bb.0.entry: + frame-setup SEH_EndPrologue + SEH_BeginEpilogue + SEH_EndEpilogue + $rdi = frame-destroy POP64r implicit-def $rsp, implicit $rsp + RET64 +... + +;--- instr_after_epilog.mir +# RUN: not llc -mtriple=x86_64-unknown-windows-msvc -o - \ +# RUN: %t/instr_after_epilog.mir -run-pass=x86-wineh-unwindv2 2>&1 | \ +# RUN: FileCheck %s --check-prefix=INSTR-AFTER-END +# RUN: llc -mtriple=x86_64-unknown-windows-msvc -o - %t/instr_after_epilog.mir \ +# RUN: -run-pass=x86-wineh-unwindv2 -x86-wineh-unwindv2-force-mode=1 | \ +# RUN: FileCheck %s --check-prefix=BESTEFFORT +# INSTR-AFTER-END: LLVM ERROR: Windows x64 Unwind v2 is required, but LLVM has generated incompatible code in function 'instr_after_epilog': +# INSTR-AFTER-END-SAME: Unexpected instruction in or after the epilog + +--- | + define dso_local void @instr_after_epilog() local_unnamed_addr { + entry: + ret void + } + !llvm.module.flags = !{!0} + !0 = !{i32 1, !"winx64-eh-unwindv2", i32 2} +... +--- +name: instr_after_epilog +body: | + bb.0.entry: + frame-setup SEH_EndPrologue + SEH_BeginEpilogue + SEH_EndEpilogue + $ecx = MOV32rr killed $eax + RET64 +... _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits