[llvm-branch-commits] [llvm] release/23.x: [X86][APX] Fix per-function V3 unwind for EGPR functions on Windows x64 (#212924) (PR #213759)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Aug 3 13:54:07 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-x86

Author: llvmbot

<details>
<summary>Changes</summary>

Backport 9466be2c87689a07c9a171f24cb5c9b0c2f0cda8

Requested by: @<!-- -->mikolaj-pirog

---
Full diff: https://github.com/llvm/llvm-project/pull/213759.diff


12 Files Affected:

- (modified) llvm/lib/Target/X86/X86.h (+7) 
- (modified) llvm/lib/Target/X86/X86FrameLowering.cpp (+21-9) 
- (modified) llvm/lib/Target/X86/X86InstrCompiler.td (+2-1) 
- (modified) llvm/lib/Target/X86/X86WinEHUnwindV2.cpp (+6) 
- (modified) llvm/lib/Target/X86/X86WinEHUnwindV3.cpp (+30-21) 
- (added) llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-funclet.ll (+31) 
- (added) llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-no-wincfi.ll (+16) 
- (added) llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-per-function.ll (+32) 
- (removed) llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-required.ll (-16) 
- (added) llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-v2-module.ll (+39) 
- (added) llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-version-switch-v1.ll (+53) 
- (added) llvm/test/CodeGen/X86/win64-eh-unwindv3-egpr-version-switch.ll (+47) 


``````````diff
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}

``````````

</details>


https://github.com/llvm/llvm-project/pull/213759


More information about the llvm-branch-commits mailing list