[llvm] [SPIRV] Add support for non-interposable function aliases (PR #172730)

Manuel Carrasco via llvm-commits llvm-commits at lists.llvm.org
Thu Dec 18 03:47:34 PST 2025


https://github.com/mgcarrasco updated https://github.com/llvm/llvm-project/pull/172730

>From b223ca828e83503c05add29ec531b1426e79b0d6 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Wed, 17 Dec 2025 14:29:23 -0600
Subject: [PATCH 1/3] [SPIRV] Add support for non-interposable function aliases

This patch implements support for calling functions through
non-interposable aliases in the SPIRV backend. When a call target
is a GlobalAlias, the alias is resolved to its underlying aliasee
object before code generation (when possible).

Interposable aliases are explicitly not supported yet and will cause
compilation to fail. This was not supported prior to this patch.

Tests added for both the supported non-interposable case and the
unsupported interposable case.
---
 llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp   | 60 +++++++++++++++++--
 .../SPIRV/function-alias-interposable.ll      | 15 +++++
 .../SPIRV/function-alias-non-interposable.ll  | 22 +++++++
 3 files changed, 92 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/function-alias-interposable.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/function-alias-non-interposable.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp b/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
index c514debb63b30..f519ef822993a 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
@@ -22,12 +22,36 @@
 #include "SPIRVSubtarget.h"
 #include "SPIRVUtils.h"
 #include "llvm/CodeGen/FunctionLoweringInfo.h"
+#include "llvm/IR/GlobalAlias.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/IntrinsicsSPIRV.h"
 #include "llvm/Support/ModRef.h"
 
 using namespace llvm;
 
+// Resolves a global alias to its underlying aliasee object.
+// Updates the machine operand to reference the resolved aliasee while
+// preserving offset and target flags from the original operand.
+// Returns the resolved aliasee object, or nullptr if resolution fails.
+static const GlobalValue *resolveGlobalAlias(const GlobalAlias *GA,
+                                             MachineOperand &MO) {
+  const auto *AO = GA->getAliaseeObject();
+  if (!AO)
+    return nullptr;
+
+  // Preserve offset and target flags from the original operand
+  int64_t Offset = MO.getOffset();
+  unsigned TFlags = MO.getTargetFlags();
+
+  // Update the machine operand to reference the aliasee object.
+  // MCInstLower requires the aliasee object otherwise it will crash during
+  // lowering. If not updated, the callee will be a global alias instead of a
+  // function and fail.
+  MO = MachineOperand::CreateGA(AO, Offset, TFlags);
+
+  return AO;
+}
+
 SPIRVCallLowering::SPIRVCallLowering(const SPIRVTargetLowering &TLI,
                                      SPIRVGlobalRegistry *GR)
     : CallLowering(&TLI), GR(GR) {}
@@ -525,10 +549,36 @@ bool SPIRVCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
   // Emit a regular OpFunctionCall. If it's an externally declared function,
   // be sure to emit its type and function declaration here. It will be hoisted
   // globally later.
-  if (Info.Callee.isGlobal()) {
-    std::string FuncName = Info.Callee.getGlobal()->getName().str();
+  MachineOperand &InfoCallee = Info.Callee;
+  if (InfoCallee.isGlobal()) {
+    const GlobalValue *GV = InfoCallee.getGlobal();
+
+    // Check if the callee is a global alias and resolve it to the aliasee
+    // object (if possible).
+    if (const auto *GA = dyn_cast<GlobalAlias>(GV)) {
+
+      // Check if the alias or the aliasee may be replaced by a symbol outside
+      // the module at link time or runtime.
+      if (GA->isInterposable() || GV->isInterposable()) {
+        // The lang ref says that in this scenario "any optimization cannot
+        // replace the alias with the aliasee". We don't yet support the
+        // interposable case.
+        return false;
+      }
+
+      // Replace the alias with the aliasee.
+      GV = resolveGlobalAlias(GA, InfoCallee);
+
+      // If alias resolution failed, return early. The subsequent code expects
+      // GV to be a Function and would fail anyway, with or without alias
+      // handling.
+      if (!GV)
+        return false;
+    }
+
+    std::string FuncName = GV->getName().str();
     DemangledName = getOclOrSpirvBuiltinDemangledName(FuncName);
-    CF = dyn_cast_or_null<const Function>(Info.Callee.getGlobal());
+    CF = dyn_cast_or_null<const Function>(GV);
     // TODO: support constexpr casts and indirect calls.
     if (CF == nullptr)
       return false;
@@ -655,7 +705,7 @@ bool SPIRVCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
     CallOp = SPIRV::OpFunctionPointerCallINTEL;
     // Collect information about the indirect call to support possible
     // specification of opaque ptr types of parent function's parameters
-    Register CalleeReg = Info.Callee.getReg();
+    Register CalleeReg = InfoCallee.getReg();
     if (CalleeReg.isValid()) {
       SPIRVCallLowering::SPIRVIndirectCall IndirectCall;
       IndirectCall.Callee = CalleeReg;
@@ -686,7 +736,7 @@ bool SPIRVCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
   auto MIB = MIRBuilder.buildInstr(CallOp)
                  .addDef(ResVReg)
                  .addUse(GR->getSPIRVTypeID(RetType))
-                 .add(Info.Callee);
+                 .add(InfoCallee);
 
   for (const auto &Arg : Info.OrigArgs) {
     // Currently call args should have single vregs.
diff --git a/llvm/test/CodeGen/SPIRV/function-alias-interposable.ll b/llvm/test/CodeGen/SPIRV/function-alias-interposable.ll
new file mode 100644
index 0000000000000..34cbf3a1d06a8
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/function-alias-interposable.ll
@@ -0,0 +1,15 @@
+; RUN: not llc -O0 -mtriple=spirv64-unknown-unknown %s -o - 
+
+; Interposable aliases are not yet supported.
+ at _ZN3barC1Ev = weak alias void (), ptr addrspace(4) @_ZN3barC2Ev
+
+define spir_func void @_ZN3barC2Ev() addrspace(4) {
+entry:
+  ret void
+}
+
+define spir_kernel void @_Z11test_kernelPi() addrspace(4) {
+entry:
+  call spir_func addrspace(4) void @_ZN3barC1Ev()
+  ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/function-alias-non-interposable.ll b/llvm/test/CodeGen/SPIRV/function-alias-non-interposable.ll
new file mode 100644
index 0000000000000..9333f8bda90f0
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/function-alias-non-interposable.ll
@@ -0,0 +1,22 @@
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+ at _ZN3barC1Ev = alias void (), ptr addrspace(4) @_ZN3barC2Ev
+
+define spir_func void @_ZN3barC2Ev() addrspace(4) {
+; CHECK:         %6 = OpFunction %4 None %5 ; -- Begin function _ZN3barC2Ev
+; CHECK-NEXT:    %2 = OpLabel
+; CHECK-NEXT:    OpReturn
+; CHECK-NEXT:    OpFunctionEnd
+entry:
+  ret void
+}
+
+define spir_kernel void @_Z11test_kernelPi() addrspace(4) {
+entry:
+; CHECK: %7 = OpFunction %4 None %5              ; -- Begin function _Z11test_kernelPi
+; CHECK-NEXT: %3 = OpLabel
+; CHECK-NEXT: %8 = OpFunctionCall %4 %6
+  call spir_func addrspace(4) void @_ZN3barC1Ev()
+  ret void
+}

>From 3325da5cebcd40b7f79b741f027f2cc82c32fb02 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Thu, 18 Dec 2025 05:33:49 -0600
Subject: [PATCH 2/3] Revert "[SPIRV] Add support for non-interposable function
 aliases"

This reverts commit b223ca828e83503c05add29ec531b1426e79b0d6.
---
 llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp   | 60 ++-----------------
 .../SPIRV/function-alias-interposable.ll      | 15 -----
 .../SPIRV/function-alias-non-interposable.ll  | 22 -------
 3 files changed, 5 insertions(+), 92 deletions(-)
 delete mode 100644 llvm/test/CodeGen/SPIRV/function-alias-interposable.ll
 delete mode 100644 llvm/test/CodeGen/SPIRV/function-alias-non-interposable.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp b/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
index f519ef822993a..c514debb63b30 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
@@ -22,36 +22,12 @@
 #include "SPIRVSubtarget.h"
 #include "SPIRVUtils.h"
 #include "llvm/CodeGen/FunctionLoweringInfo.h"
-#include "llvm/IR/GlobalAlias.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/IntrinsicsSPIRV.h"
 #include "llvm/Support/ModRef.h"
 
 using namespace llvm;
 
-// Resolves a global alias to its underlying aliasee object.
-// Updates the machine operand to reference the resolved aliasee while
-// preserving offset and target flags from the original operand.
-// Returns the resolved aliasee object, or nullptr if resolution fails.
-static const GlobalValue *resolveGlobalAlias(const GlobalAlias *GA,
-                                             MachineOperand &MO) {
-  const auto *AO = GA->getAliaseeObject();
-  if (!AO)
-    return nullptr;
-
-  // Preserve offset and target flags from the original operand
-  int64_t Offset = MO.getOffset();
-  unsigned TFlags = MO.getTargetFlags();
-
-  // Update the machine operand to reference the aliasee object.
-  // MCInstLower requires the aliasee object otherwise it will crash during
-  // lowering. If not updated, the callee will be a global alias instead of a
-  // function and fail.
-  MO = MachineOperand::CreateGA(AO, Offset, TFlags);
-
-  return AO;
-}
-
 SPIRVCallLowering::SPIRVCallLowering(const SPIRVTargetLowering &TLI,
                                      SPIRVGlobalRegistry *GR)
     : CallLowering(&TLI), GR(GR) {}
@@ -549,36 +525,10 @@ bool SPIRVCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
   // Emit a regular OpFunctionCall. If it's an externally declared function,
   // be sure to emit its type and function declaration here. It will be hoisted
   // globally later.
-  MachineOperand &InfoCallee = Info.Callee;
-  if (InfoCallee.isGlobal()) {
-    const GlobalValue *GV = InfoCallee.getGlobal();
-
-    // Check if the callee is a global alias and resolve it to the aliasee
-    // object (if possible).
-    if (const auto *GA = dyn_cast<GlobalAlias>(GV)) {
-
-      // Check if the alias or the aliasee may be replaced by a symbol outside
-      // the module at link time or runtime.
-      if (GA->isInterposable() || GV->isInterposable()) {
-        // The lang ref says that in this scenario "any optimization cannot
-        // replace the alias with the aliasee". We don't yet support the
-        // interposable case.
-        return false;
-      }
-
-      // Replace the alias with the aliasee.
-      GV = resolveGlobalAlias(GA, InfoCallee);
-
-      // If alias resolution failed, return early. The subsequent code expects
-      // GV to be a Function and would fail anyway, with or without alias
-      // handling.
-      if (!GV)
-        return false;
-    }
-
-    std::string FuncName = GV->getName().str();
+  if (Info.Callee.isGlobal()) {
+    std::string FuncName = Info.Callee.getGlobal()->getName().str();
     DemangledName = getOclOrSpirvBuiltinDemangledName(FuncName);
-    CF = dyn_cast_or_null<const Function>(GV);
+    CF = dyn_cast_or_null<const Function>(Info.Callee.getGlobal());
     // TODO: support constexpr casts and indirect calls.
     if (CF == nullptr)
       return false;
@@ -705,7 +655,7 @@ bool SPIRVCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
     CallOp = SPIRV::OpFunctionPointerCallINTEL;
     // Collect information about the indirect call to support possible
     // specification of opaque ptr types of parent function's parameters
-    Register CalleeReg = InfoCallee.getReg();
+    Register CalleeReg = Info.Callee.getReg();
     if (CalleeReg.isValid()) {
       SPIRVCallLowering::SPIRVIndirectCall IndirectCall;
       IndirectCall.Callee = CalleeReg;
@@ -736,7 +686,7 @@ bool SPIRVCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
   auto MIB = MIRBuilder.buildInstr(CallOp)
                  .addDef(ResVReg)
                  .addUse(GR->getSPIRVTypeID(RetType))
-                 .add(InfoCallee);
+                 .add(Info.Callee);
 
   for (const auto &Arg : Info.OrigArgs) {
     // Currently call args should have single vregs.
diff --git a/llvm/test/CodeGen/SPIRV/function-alias-interposable.ll b/llvm/test/CodeGen/SPIRV/function-alias-interposable.ll
deleted file mode 100644
index 34cbf3a1d06a8..0000000000000
--- a/llvm/test/CodeGen/SPIRV/function-alias-interposable.ll
+++ /dev/null
@@ -1,15 +0,0 @@
-; RUN: not llc -O0 -mtriple=spirv64-unknown-unknown %s -o - 
-
-; Interposable aliases are not yet supported.
- at _ZN3barC1Ev = weak alias void (), ptr addrspace(4) @_ZN3barC2Ev
-
-define spir_func void @_ZN3barC2Ev() addrspace(4) {
-entry:
-  ret void
-}
-
-define spir_kernel void @_Z11test_kernelPi() addrspace(4) {
-entry:
-  call spir_func addrspace(4) void @_ZN3barC1Ev()
-  ret void
-}
diff --git a/llvm/test/CodeGen/SPIRV/function-alias-non-interposable.ll b/llvm/test/CodeGen/SPIRV/function-alias-non-interposable.ll
deleted file mode 100644
index 9333f8bda90f0..0000000000000
--- a/llvm/test/CodeGen/SPIRV/function-alias-non-interposable.ll
+++ /dev/null
@@ -1,22 +0,0 @@
-; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
-; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
-
- at _ZN3barC1Ev = alias void (), ptr addrspace(4) @_ZN3barC2Ev
-
-define spir_func void @_ZN3barC2Ev() addrspace(4) {
-; CHECK:         %6 = OpFunction %4 None %5 ; -- Begin function _ZN3barC2Ev
-; CHECK-NEXT:    %2 = OpLabel
-; CHECK-NEXT:    OpReturn
-; CHECK-NEXT:    OpFunctionEnd
-entry:
-  ret void
-}
-
-define spir_kernel void @_Z11test_kernelPi() addrspace(4) {
-entry:
-; CHECK: %7 = OpFunction %4 None %5              ; -- Begin function _Z11test_kernelPi
-; CHECK-NEXT: %3 = OpLabel
-; CHECK-NEXT: %8 = OpFunctionCall %4 %6
-  call spir_func addrspace(4) void @_ZN3barC1Ev()
-  ret void
-}

>From 4e544454d4ed5b9ea148acb2b1224678acacfe52 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Thu, 18 Dec 2025 05:40:41 -0600
Subject: [PATCH 3/3] [review] Switch to IR pass and add tests to cover all
 cases.

---
 llvm/lib/Target/SPIRV/SPIRVPrepareGlobals.cpp | 46 ++++++++++++++++++-
 .../test/CodeGen/SPIRV/ga-interp-gv-interp.ll | 16 +++++++
 .../CodeGen/SPIRV/ga-interp-gv-noninterp.ll   | 15 ++++++
 .../CodeGen/SPIRV/ga-noninterp-gv-interp.ll   | 15 ++++++
 .../SPIRV/ga-noninterp-gv-noninterp.ll        | 22 +++++++++
 5 files changed, 112 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/ga-interp-gv-interp.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/ga-interp-gv-noninterp.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/ga-noninterp-gv-interp.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/ga-noninterp-gv-noninterp.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVPrepareGlobals.cpp b/llvm/lib/Target/SPIRV/SPIRVPrepareGlobals.cpp
index 14b75d7d16a4d..e27fdd1fb475e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPrepareGlobals.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPrepareGlobals.cpp
@@ -16,6 +16,9 @@
 
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/IR/Module.h"
+#include "llvm/Support/Debug.h"
+
+#define DEBUG_TYPE "spirv-prepare-globals"
 
 using namespace llvm;
 
@@ -77,12 +80,51 @@ bool tryExtendDynamicLDSGlobal(GlobalVariable &GV) {
   return true;
 }
 
+// The backend does not support GlobalAlias. Replace aliases with their aliasees
+// when possible and remove them from the module.
+bool tryReplaceAliasWithAliasee(GlobalAlias &GA) {
+  // According to the lang ref, aliases cannot be replaced if either the alias
+  // or the aliasee are interposable. We only replace in the case that both
+  // are not interposable.
+  if (GA.isInterposable()) {
+    LLVM_DEBUG(dbgs() << "Skipping interposable alias: " << GA.getName()
+                      << "\n");
+    return false;
+  }
+
+  GlobalObject *AO = GA.getAliaseeObject();
+  if (!AO) {
+    LLVM_DEBUG(dbgs() << "Skipping alias with no aliasee: " << GA.getName()
+                      << "\n");
+    return false;
+  }
+
+  if (AO->isInterposable()) {
+    LLVM_DEBUG(dbgs() << "Skipping interposable aliasee: " << AO->getName()
+                      << "\n");
+    return false;
+  }
+
+  LLVM_DEBUG(dbgs() << "Replacing alias " << GA.getName()
+                    << " with aliasee: " << AO->getName() << "\n");
+
+  GA.replaceAllUsesWith(AO);
+  GA.eraseFromParent();
+
+  return true;
+}
+
 bool SPIRVPrepareGlobals::runOnModule(Module &M) {
+  bool Changed = false;
+
+  for (GlobalAlias &GA : make_early_inc_range(M.aliases())) {
+    Changed |= tryReplaceAliasWithAliasee(GA);
+  }
+
   const bool IsAMD = M.getTargetTriple().getVendor() == Triple::AMD;
   if (!IsAMD)
-    return false;
+    return Changed;
 
-  bool Changed = false;
   if (GlobalVariable *Bitcode = M.getNamedGlobal("llvm.embedded.module"))
     Changed |= tryExtendLLVMBitcodeMarker(*Bitcode);
 
diff --git a/llvm/test/CodeGen/SPIRV/ga-interp-gv-interp.ll b/llvm/test/CodeGen/SPIRV/ga-interp-gv-interp.ll
new file mode 100644
index 0000000000000..7d4410bb46936
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/ga-interp-gv-interp.ll
@@ -0,0 +1,16 @@
+; RUN: not llc -O0 -mtriple=spirv64-unknown-unknown < %s 
+
+; Interposable aliases are not yet supported.
+ at bar_alias = weak alias void (), ptr addrspace(4) @bar
+
+; Interposable functions are not yet supported for aliasing resolution.
+define weak spir_func void @bar() addrspace(4) {
+entry:
+  ret void
+}
+
+define spir_kernel void @kernel() addrspace(4) {
+entry:
+  call spir_func addrspace(4) void @bar_alias()
+  ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/ga-interp-gv-noninterp.ll b/llvm/test/CodeGen/SPIRV/ga-interp-gv-noninterp.ll
new file mode 100644
index 0000000000000..e8debbb4d7dd0
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/ga-interp-gv-noninterp.ll
@@ -0,0 +1,15 @@
+; RUN: not llc -O0 -mtriple=spirv64-unknown-unknown < %s 
+
+; Interposable aliases are not yet supported.
+ at bar_alias = weak alias void (), ptr addrspace(4) @bar
+
+define spir_func void @bar() addrspace(4) {
+entry:
+  ret void
+}
+
+define spir_kernel void @kernel() addrspace(4) {
+entry:
+  call spir_func addrspace(4) void @bar_alias()
+  ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/ga-noninterp-gv-interp.ll b/llvm/test/CodeGen/SPIRV/ga-noninterp-gv-interp.ll
new file mode 100644
index 0000000000000..e8debbb4d7dd0
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/ga-noninterp-gv-interp.ll
@@ -0,0 +1,15 @@
+; RUN: not llc -O0 -mtriple=spirv64-unknown-unknown < %s 
+
+; Interposable aliases are not yet supported.
+ at bar_alias = weak alias void (), ptr addrspace(4) @bar
+
+define spir_func void @bar() addrspace(4) {
+entry:
+  ret void
+}
+
+define spir_kernel void @kernel() addrspace(4) {
+entry:
+  call spir_func addrspace(4) void @bar_alias()
+  ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/ga-noninterp-gv-noninterp.ll b/llvm/test/CodeGen/SPIRV/ga-noninterp-gv-noninterp.ll
new file mode 100644
index 0000000000000..4a82e3d54efc6
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/ga-noninterp-gv-noninterp.ll
@@ -0,0 +1,22 @@
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown -filetype=obj < %s  | spirv-val %}
+
+ at bar_alias = alias void (), ptr addrspace(4) @bar
+
+define spir_func void @bar() addrspace(4) {
+; CHECK:         %6 = OpFunction %4 None %5 ; -- Begin function bar
+; CHECK-NEXT:    %2 = OpLabel
+; CHECK-NEXT:    OpReturn
+; CHECK-NEXT:    OpFunctionEnd
+entry:
+  ret void
+}
+
+define spir_kernel void @kernel() addrspace(4) {
+entry:
+; CHECK: %7 = OpFunction %4 None %5              ; -- Begin function kernel
+; CHECK-NEXT: %3 = OpLabel
+; CHECK-NEXT: %8 = OpFunctionCall %4 %6
+  call spir_func addrspace(4) void @bar_alias()
+  ret void
+}



More information about the llvm-commits mailing list