[llvm-branch-commits] [llvm] 2629bf9 - [X86][APX] Fix per-function V3 unwind for EGPR functions on Windows x64 (#212924)

Douglas Yung via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Aug 4 05:33:35 PDT 2026


Author: Feng Zou
Date: 2026-08-04T12:33:20Z
New Revision: 2629bf96a7c92c9708baba5c09ce5e82fad581a5

URL: https://github.com/llvm/llvm-project/commit/2629bf96a7c92c9708baba5c09ce5e82fad581a5
DIFF: https://github.com/llvm/llvm-project/commit/2629bf96a7c92c9708baba5c09ce5e82fad581a5.diff

LOG: [X86][APX] Fix per-function V3 unwind for EGPR functions on Windows x64 (#212924)

A function that saves a callee-saved EGPR (R16-R31) cannot be encoded with V1/V2 unwind info, so it must use V3 even when the module default stays on V1/V2 (e.g. an APX clone created by auto-dispatch alongside a baseline generic clone). The previous code rejected such functions with a recoverable backend diagnostic ("EGPR (R16-R31) requires V3 unwind info on Windows x64") instead of emitting valid V3 unwind info.

Introduce a single shared predicate, requiresWinX64UnwindV3(MF), that returns true when the whole module is in V3 mode, or when the function needs an unwind table and may use EGPR. It is consumed by X86FrameLowering (SEH prolog/epilog layout), the X86WinEHUnwindV2 pass (which skips such functions), and the X86WinEHUnwindV3 pass (which stamps a per-function .seh_unwindversion 3 on every WinEH frame -- the entry block and each funclet). Also widen the SEH_UnwindVersion pseudo operand from i1imm to i8imm since it holds 1, 2 or 3.

(cherry picked from commit 9466be2c87689a07c9a171f24cb5c9b0c2f0cda8)

Added: 
    llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-funclet.ll
    llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-no-wincfi.ll
    llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-per-function.ll
    llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-v2-module.ll
    llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-version-switch-v1.ll
    llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-version-switch.ll

Modified: 
    llvm/lib/Target/X86/X86.h
    llvm/lib/Target/X86/X86FrameLowering.cpp
    llvm/lib/Target/X86/X86InstrCompiler.td
    llvm/lib/Target/X86/X86WinEHUnwindV2.cpp
    llvm/lib/Target/X86/X86WinEHUnwindV3.cpp

Removed: 
    llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-required.ll


################################################################################
diff  --git a/llvm/lib/Target/X86/X86.h b/llvm/lib/Target/X86/X86.h
index 48dedd9d2a758..3478e0e9c0d4b 100644
--- a/llvm/lib/Target/X86/X86.h
+++ b/llvm/lib/Target/X86/X86.h
@@ -27,6 +27,7 @@ namespace llvm {
 
 class FunctionPass;
 class InstructionSelector;
+class MachineFunction;
 class PassRegistry;
 class X86RegisterBankInfo;
 class X86Subtarget;
@@ -408,6 +409,12 @@ FunctionPass *createX86LowerAMXIntrinsicsLegacyPass();
 /// Capacity check and sub-fragment splitting for Win x64 Unwind V3.
 FunctionPass *createX86WinEHUnwindV3Pass();
 
+/// Returns true when \p MF must use Windows x64 Unwind V3: the module is in V3
+/// mode, or the function needs an unwind table and may use EGPR (R16-R31),
+/// which V1/V2 cannot encode. Shared by frame lowering and the WinEH Unwind
+/// V2/V3 passes so their decisions cannot disagree.
+bool requireWinX64UnwindV3(const MachineFunction &MF);
+
 InstructionSelector *createX86InstructionSelector(const X86TargetMachine &TM,
                                                   const X86Subtarget &,
                                                   const X86RegisterBankInfo &);

diff  --git a/llvm/lib/Target/X86/X86FrameLowering.cpp b/llvm/lib/Target/X86/X86FrameLowering.cpp
index a6e1d998c051a..a657c7d3873e5 100644
--- a/llvm/lib/Target/X86/X86FrameLowering.cpp
+++ b/llvm/lib/Target/X86/X86FrameLowering.cpp
@@ -12,6 +12,7 @@
 
 #include "X86FrameLowering.h"
 #include "MCTargetDesc/X86MCTargetDesc.h"
+#include "X86.h"
 #include "X86InstrBuilder.h"
 #include "X86InstrInfo.h"
 #include "X86MachineFunctionInfo.h"
@@ -46,6 +47,23 @@ STATISTIC(NumFunctionUsingPush2Pop2, "Number of functions using push2/pop2");
 
 using namespace llvm;
 
+bool llvm::requireWinX64UnwindV3(const MachineFunction &MF) {
+  const Function &Fn = MF.getFunction();
+
+  // Whole module is in V3 mode.
+  if (Fn.getParent()->getWinX64EHUnwindMode() == WinX64EHUnwindMode::V3)
+    return true;
+
+  // Otherwise promote a function that may use EGPR (R16-R31), which V1/V2
+  // unwind codes cannot encode. The per-function "+egpr" feature is the signal,
+  // so an auto-dispatch APX clone gets V3 while the baseline clone stays on the
+  // module default. We conservatively promote any egpr function rather than
+  // checking for an actual EGPR save, keeping this a cheap query. (PUSH2/POP2
+  // does not need V3: V1/V2 describe a PUSH2 as two SEH_PushReg codes.)
+  return Fn.needsUnwindTableEntry() &&
+         MF.getSubtarget<X86Subtarget>().hasEGPR();
+}
+
 static const TargetRegisterClass *
 getCalleeSavedSpillRC(MCRegister Reg, const X86Subtarget &STI,
                       const TargetRegisterInfo &TRI) {
@@ -1624,9 +1642,7 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
                      MF.getFunction().getParent()->getCodeViewFlag();
   bool NeedsWinCFI = NeedsWin64CFI || NeedsWinFPO;
   bool NeedsDwarfCFI = needsDwarfCFI(MF);
-  bool IsWin64UnwindV3 =
-      NeedsWin64CFI &&
-      Fn.getParent()->getWinX64EHUnwindMode() == WinX64EHUnwindMode::V3;
+  bool IsWin64UnwindV3 = NeedsWin64CFI && requireWinX64UnwindV3(MF);
   Register FramePtr = TRI->getFrameRegister(MF);
   const Register MachineFramePtr =
       STI.isTarget64BitILP32() ? Register(getX86SubSuperRegister(FramePtr, 64))
@@ -2508,9 +2524,7 @@ void X86FrameLowering::emitEpilogue(MachineFunction &MF,
   // For V3 unwind, epilog SEH pseudos are emitted inline before each
   // unwind-effecting instruction.
   bool IsWin64UnwindV3 =
-      NeedsWin64CFI && MF.hasWinCFI() &&
-      MF.getFunction().getParent()->getWinX64EHUnwindMode() ==
-          WinX64EHUnwindMode::V3;
+      NeedsWin64CFI && MF.hasWinCFI() && requireWinX64UnwindV3(MF);
   bool IsFunclet = MBBI == MBB.end() ? false : isFuncletReturnInstr(*MBBI);
 
   // Get the number of bytes to allocate from the FrameInfo.
@@ -3292,9 +3306,7 @@ bool X86FrameLowering::restoreCalleeSavedRegisters(
 
   bool NeedsWin64CFI =
       isWin64Prologue(MF) && MF.getFunction().needsUnwindTableEntry();
-  bool IsWin64UnwindV3 =
-      NeedsWin64CFI && MF.getFunction().getParent()->getWinX64EHUnwindMode() ==
-                           WinX64EHUnwindMode::V3;
+  bool IsWin64UnwindV3 = NeedsWin64CFI && requireWinX64UnwindV3(MF);
 
   // Reload XMMs from stack frame.
   for (const CalleeSavedInfo &I : CSI) {

diff  --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td
index 5a6597affd51f..d3e8fdbdcd53c 100644
--- a/llvm/lib/Target/X86/X86InstrCompiler.td
+++ b/llvm/lib/Target/X86/X86InstrCompiler.td
@@ -260,7 +260,8 @@ let isPseudo = 1, isMeta = 1, isNotDuplicable = 1, SchedRW = [WriteSystem] in {
                             "#SEH_PushFrame $mode", []>;
   def SEH_EndPrologue : I<0, Pseudo, (outs), (ins),
                             "#SEH_EndPrologue", []>;
-  def SEH_UnwindVersion : I<0, Pseudo, (outs), (ins i1imm:$version),
+  // Widened from i1imm to i8imm to hold V3 (the version can be 1, 2 or 3).
+  def SEH_UnwindVersion : I<0, Pseudo, (outs), (ins i8imm:$version),
                             "#SEH_UnwindVersion $version", []>;
 }
 

diff  --git a/llvm/lib/Target/X86/X86WinEHUnwindV2.cpp b/llvm/lib/Target/X86/X86WinEHUnwindV2.cpp
index 254a91174de7c..79b91206e9d5b 100644
--- a/llvm/lib/Target/X86/X86WinEHUnwindV2.cpp
+++ b/llvm/lib/Target/X86/X86WinEHUnwindV2.cpp
@@ -383,6 +383,12 @@ bool runX86WinEHUnwindV2(MachineFunction &MF) {
       Mode != WinX64EHUnwindMode::V2Required)
     return false;
 
+  // A function that requires V3 (see requireWinX64UnwindV3()) is emitted as V3
+  // instead; skip it here so this pass does not stamp V2 pseudos that conflict
+  // with the V3 layout.
+  if (requireWinX64UnwindV3(MF))
+    return false;
+
   // Requested changes.
   SmallVector<FrameInfo> FrameInfos;
   MachineFunction::iterator Iter = MF.begin();

diff  --git a/llvm/lib/Target/X86/X86WinEHUnwindV3.cpp b/llvm/lib/Target/X86/X86WinEHUnwindV3.cpp
index 162633e2fddf3..4efedcd796f21 100644
--- a/llvm/lib/Target/X86/X86WinEHUnwindV3.cpp
+++ b/llvm/lib/Target/X86/X86WinEHUnwindV3.cpp
@@ -13,7 +13,10 @@
 ///   2. Check V3 capacity limits (<=31 prolog/epilog ops, <=7 epilogs).
 ///   3. Insert sub-fragment split points if limits are exceeded.
 ///
-/// The unwind version is set module-wide, not per-function.
+/// The unwind version is normally module-wide. When only an individual function
+/// needs V3 (see requireWinX64UnwindV3()), this pass stamps each of its frames
+/// -- the entry block and every funclet -- with a per-function
+/// .seh_unwindversion 3, leaving the rest of the module on its default version.
 ///
 /// See https://learn.microsoft.com/en-us/cpp/build/x64-unwind-information-v3
 ///
@@ -225,30 +228,21 @@ FuncletInfo X86WinEHUnwindV3::analyzeFunclet(MachineFunction &MF,
 }
 
 bool X86WinEHUnwindV3::runOnMachineFunction(MachineFunction &MF) {
-  WinX64EHUnwindMode Mode =
-      MF.getFunction().getParent()->getWinX64EHUnwindMode();
-
   Function &F = MF.getFunction();
   LLVMContext &Ctx = F.getContext();
 
-  // EGPR (R16-R31) requires V3 unwind info because V1/V2 cannot encode
-  // registers beyond R15. Only enforce this for functions that actually
-  // emit SEH unwind info — `nounwind` functions and targets that don't
-  // require unwind tables (e.g. cross-compilation host defaults) can use
-  // EGPR with any unwind mode since no SEH metadata is generated.
-  if (Mode != WinX64EHUnwindMode::V3) {
-    if (!F.needsUnwindTableEntry())
-      return false;
-    const auto &STI = MF.getSubtarget<X86Subtarget>();
-    if (STI.hasEGPR()) {
-      Ctx.diagnose(DiagnosticInfoUnsupported(
-          F, "EGPR (R16-R31) requires V3 unwind info on Windows x64"));
-      // Stripping the SEH pseudos modifies the function, so report a change.
-      suppressWinCFI(MF);
-      return true;
-    }
+  if (!requireWinX64UnwindV3(MF))
     return false;
-  }
+
+  // Emit a per-function .seh_unwindversion 3 only when V3 is enabled for this
+  // function alone: in module-wide V3 the AsmPrinter emits it once, so stamping
+  // here would duplicate it. The gate also requires WinCFI -- without a
+  // .seh_proc there is nothing to version, and a lone SEH pseudo would trip an
+  // AsmPrinter assertion. The marker is per .seh_proc, hence stamped on each
+  // funclet in the loop below.
+  bool PerFunctionV3 =
+      MF.hasWinCFI() && MF.getFunction().getParent()->getWinX64EHUnwindMode() !=
+                            WinX64EHUnwindMode::V3;
 
   bool Changed = false;
   unsigned ApproxBytePos = 0;
@@ -259,6 +253,21 @@ bool X86WinEHUnwindV3::runOnMachineFunction(MachineFunction &MF) {
   // Process each funclet (and the main function body) independently.
   // Each funclet gets its own UNWIND_INFO, so V3 limits apply per funclet.
   while (Iter != MF.end()) {
+    // Iter points at the first block of a frame -- the entry frame on the
+    // first iteration, an EH funclet on later ones. Each frame is its own
+    // .seh_proc, so stamp the version on each here before analyzeFunclet
+    // advances past it.
+    if (PerFunctionV3) {
+      const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
+      MachineBasicBlock &FuncletEntry = *Iter;
+      BuildMI(FuncletEntry, FuncletEntry.begin(),
+              FuncletEntry.findDebugLoc(FuncletEntry.begin()),
+              TII->get(X86::SEH_UnwindVersion))
+          .addImm(3)
+          .setMIFlag(MachineInstr::FrameSetup);
+      Changed = true;
+    }
+
     FuncletInfo Info = analyzeFunclet(MF, Iter, ApproxBytePos);
 
     if (Info.PrologOpCount > MaxV3PrologOps) {

diff  --git a/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-funclet.ll b/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-funclet.ll
new file mode 100644
index 0000000000000..dec4b0364db03
--- /dev/null
+++ b/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-funclet.ll
@@ -0,0 +1,31 @@
+; RUN: llc -mtriple=x86_64-unknown-windows-msvc -o - %s | FileCheck %s
+
+; A per-function V3 promotion stamps every WinEH frame -- the entry frame and
+; each EH funclet (each its own .seh_proc) -- with .seh_unwindversion 3.
+
+; Entry frame is V3.
+; CHECK-LABEL:  f_eh:
+; CHECK:        .seh_proc f_eh
+; CHECK:        .seh_unwindversion 3
+; CHECK:        .seh_endproc
+
+; The cleanup funclet is a separate .seh_proc and must also be V3.
+; CHECK:        .seh_proc "?dtor${{[^"]*}}"
+; CHECK:        .seh_unwindversion 3
+; CHECK:        .seh_endproc
+
+define dso_local void @f_eh() #0 personality ptr @__C_specific_handler {
+entry:
+  invoke void @c() to label %ok unwind label %cu
+ok:
+  ret void
+cu:
+  %tok = cleanuppad within none []
+  call void @d() [ "funclet"(token %tok) ]
+  cleanupret from %tok unwind to caller
+}
+
+declare void @c()
+declare void @d()
+declare dso_local i32 @__C_specific_handler(...)
+attributes #0 = { uwtable "target-features"="+egpr" }

diff  --git a/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-no-wincfi.ll b/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-no-wincfi.ll
new file mode 100644
index 0000000000000..722341418e0df
--- /dev/null
+++ b/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-no-wincfi.ll
@@ -0,0 +1,16 @@
+; RUN: llc -mtriple=x86_64-unknown-windows-msvc -o - %s | FileCheck %s
+
+; An egpr leaf with no WinCFI (saves nothing) must NOT get a .seh_proc or
+; .seh_unwindversion: the per-function marker is gated on MF.hasWinCFI().
+
+; CHECK-LABEL:  leaf:
+; CHECK-NOT:    .seh_proc
+; CHECK-NOT:    .seh_unwindversion
+; CHECK:        retq
+
+define dso_local void @leaf() #0 {
+entry:
+  ret void
+}
+
+attributes #0 = { uwtable "target-features"="+egpr" }

diff  --git a/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-per-function.ll b/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-per-function.ll
new file mode 100644
index 0000000000000..bdc36804e990a
--- /dev/null
+++ b/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-per-function.ll
@@ -0,0 +1,32 @@
+; RUN: llc -mtriple=x86_64-unknown-windows-msvc -o - %s | FileCheck %s
+
+; Models auto-dispatch: in a V1-default module, only the egpr clone
+; is promoted to V3; the baseline clone stays on the module default.
+
+; The baseline function must NOT get a per-function unwind version.
+; CHECK-LABEL:  baseline:
+; CHECK:        .seh_proc baseline
+; CHECK-NOT:    .seh_unwindversion
+; CHECK:        .seh_endproc
+
+; The APX clone may use EGPR and is promoted to V3 individually.
+; CHECK-LABEL:  apx_clone:
+; CHECK:        .seh_proc apx_clone
+; CHECK:        .seh_unwindversion 3
+; CHECK:        .seh_endproc
+
+define dso_local void @baseline() #1 {
+entry:
+  call void @ext()
+  ret void
+}
+
+define dso_local void @apx_clone() #0 {
+entry:
+  call void @ext()
+  ret void
+}
+
+declare void @ext()
+attributes #0 = { uwtable "target-features"="+egpr" }
+attributes #1 = { uwtable }

diff  --git a/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-required.ll b/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-required.ll
deleted file mode 100644
index f33893a9e1d88..0000000000000
--- a/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-required.ll
+++ /dev/null
@@ -1,16 +0,0 @@
-; RUN: not llc -mtriple=x86_64-unknown-windows-msvc -mattr=+egpr -o /dev/null %s 2>&1 | FileCheck %s
-; CHECK: error: {{.*}}: in function func {{.*}}: EGPR (R16-R31) requires V3 unwind info on Windows x64
-
-; EGPR enabled without V3 unwind (default V1) should produce a recoverable
-; backend diagnostic (no crash, no stack trace).
-; The uwtable attribute and the call site force a stack frame and SEH unwind
-; info emission so the V3 pass runs regardless of the host platform's default
-; (matters for cross-compilation on Linux/macOS hosts targeting Windows).
-
-declare void @other()
-
-define dso_local void @func() uwtable {
-entry:
-  call void @other()
-  ret void
-}

diff  --git a/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-v2-module.ll b/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-v2-module.ll
new file mode 100644
index 0000000000000..f914b20b0e29d
--- /dev/null
+++ b/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-v2-module.ll
@@ -0,0 +1,39 @@
+; RUN: llc -mtriple=x86_64-unknown-windows-msvc -o - %s | FileCheck %s
+
+; In a V2-default module, the egpr function is promoted to V3 (with no V2
+; markers) while the non-egpr function stays on V2.
+
+; The non-EGPR function stays on V2 with its V2 epilog markers.
+; CHECK-LABEL:  v2_baseline:
+; CHECK:        .seh_proc v2_baseline
+; CHECK:        .seh_unwindversion 2
+; CHECK-NOT:    .seh_unwindversion 3
+; CHECK:        .seh_unwindv2start
+; CHECK:        .seh_endproc
+
+; The EGPR function is emitted as V3 with no V2 markers.
+; CHECK-LABEL:  v2_egpr:
+; CHECK:        .seh_proc v2_egpr
+; CHECK:        .seh_unwindversion 3
+; CHECK-NOT:    .seh_unwindversion 2
+; CHECK-NOT:    .seh_unwindv2start
+; CHECK:        .seh_endproc
+
+define dso_local void @v2_baseline() #1 {
+entry:
+  call void @ext()
+  ret void
+}
+
+define dso_local void @v2_egpr() #0 {
+entry:
+  call void @ext()
+  ret void
+}
+
+declare void @ext()
+attributes #0 = { uwtable "target-features"="+egpr" }
+attributes #1 = { uwtable }
+
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"winx64-eh-unwind", i32 2}

diff  --git a/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-version-switch-v1.ll b/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-version-switch-v1.ll
new file mode 100644
index 0000000000000..efd707e8072e6
--- /dev/null
+++ b/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-version-switch-v1.ll
@@ -0,0 +1,53 @@
+; RUN: llc -mtriple=x86_64-unknown-windows-msvc -o - %s | FileCheck %s
+
+; Like win64-eh-unwindv3-egpr-version-switch.ll but in a V1-default
+; module: each egpr function toggles to V3, each non-egpr function stays on V1
+; (no marker).
+
+; CHECK-LABEL:  a_egpr:
+; CHECK:        .seh_proc a_egpr
+; CHECK:        .seh_unwindversion 3
+; CHECK:        .seh_endproc
+
+; CHECK-LABEL:  b_base:
+; CHECK:        .seh_proc b_base
+; CHECK-NOT:    .seh_unwindversion
+; CHECK:        .seh_endproc
+
+; CHECK-LABEL:  c_egpr:
+; CHECK:        .seh_proc c_egpr
+; CHECK:        .seh_unwindversion 3
+; CHECK:        .seh_endproc
+
+; CHECK-LABEL:  d_base:
+; CHECK:        .seh_proc d_base
+; CHECK-NOT:    .seh_unwindversion
+; CHECK:        .seh_endproc
+
+define dso_local void @a_egpr() #0 {
+entry:
+  call void @ext()
+  ret void
+}
+
+define dso_local void @b_base() #1 {
+entry:
+  call void @ext()
+  ret void
+}
+
+define dso_local void @c_egpr() #0 {
+entry:
+  call void @ext()
+  ret void
+}
+
+define dso_local void @d_base() #1 {
+entry:
+  call void @ext()
+  ret void
+}
+
+declare void @ext()
+attributes #0 = { uwtable "target-features"="+egpr" }
+attributes #1 = { uwtable }

diff  --git a/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-version-switch.ll b/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-version-switch.ll
new file mode 100644
index 0000000000000..6136fb64516e8
--- /dev/null
+++ b/llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-version-switch.ll
@@ -0,0 +1,47 @@
+; RUN: llc -mtriple=x86_64-unknown-windows-msvc -o - %s | FileCheck %s
+
+; Interleaved egpr / non-egpr functions in a V2 module: the per-function
+; version must alternate both ways, 3, 2, 3, 2.
+
+; CHECK-LABEL:  a_egpr:
+; CHECK:        .seh_unwindversion 3
+; CHECK-NOT:    .seh_unwindversion
+; CHECK-LABEL:  b_base:
+; CHECK:        .seh_unwindversion 2
+; CHECK-NOT:    .seh_unwindversion
+; CHECK-LABEL:  c_egpr:
+; CHECK:        .seh_unwindversion 3
+; CHECK-NOT:    .seh_unwindversion
+; CHECK-LABEL:  d_base:
+; CHECK:        .seh_unwindversion 2
+
+define dso_local void @a_egpr() #0 {
+entry:
+  call void @ext()
+  ret void
+}
+
+define dso_local void @b_base() #1 {
+entry:
+  call void @ext()
+  ret void
+}
+
+define dso_local void @c_egpr() #0 {
+entry:
+  call void @ext()
+  ret void
+}
+
+define dso_local void @d_base() #1 {
+entry:
+  call void @ext()
+  ret void
+}
+
+declare void @ext()
+attributes #0 = { uwtable "target-features"="+egpr" }
+attributes #1 = { uwtable }
+
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"winx64-eh-unwind", i32 2}


        


More information about the llvm-branch-commits mailing list