Skip to content

[SPIRV] Extend lowering of variadic functions#178980

Merged
sarnex merged 4 commits intollvm:mainfrom
sarnex:vararg
Feb 13, 2026
Merged

[SPIRV] Extend lowering of variadic functions#178980
sarnex merged 4 commits intollvm:mainfrom
sarnex:vararg

Conversation

@sarnex
Copy link
Member

@sarnex sarnex commented Jan 30, 2026

Variadic function lowering for SPIR-V was initially added in #175076.

However, I tried a full OpenMP offloading example that includes a vararg call and hit a few issues:

  1. The OpenMP Deivce library function ompx::printf was incorrectly being considered a builtin printf function that would be handled specifically by the SPIR-V backend.

The fix here is to remove the printf special handling.

  1. We were getting an assert in ModuleVerifier saying the LLVM lifetime intrinsics were being called with an argument that was neither an alloca ptr or poison. The problem is the alloca was replaced with a SPIR-V intrinsic alloca in SPIRVPrepareFunctions, but the lifetime intrinsic added in ExpandVariadics was not lowered to the SPIR-V lifetime intrinsic since ExpandVariadics is run after SPIRVPrepareFunctions,

The fix here is to just run ExpandVariadics first.

  1. There were va intrinsics taking in a addrspace(4) pointer that were not being expanded.

The fix here is to extend ExpandVariadics to support expanding va intrinsics with target-specific address spaces.

@github-actions
Copy link

github-actions bot commented Jan 30, 2026

🐧 Linux x64 Test Results

  • 190038 tests passed
  • 5073 tests skipped

✅ The build succeeded and all tests passed.

Signed-off-by: Nick Sarnie <nick.sarnie@intel.com>
@sarnex
Copy link
Member Author

sarnex commented Feb 2, 2026

Two SPIR-V test fails not related, they fail in a fresh clone.

@sarnex sarnex marked this pull request as ready for review February 2, 2026 17:40
@llvmbot
Copy link
Member

llvmbot commented Feb 2, 2026

@llvm/pr-subscribers-backend-spir-v

Author: Nick Sarnie (sarnex)

Changes

Variadic function lowering for SPIR-V was initially added in #175076.

However, I tried a full OpenMP offloading example that includes a vararg call and hit a few issues:

  1. The OpenMP Deivce library function ompx::printf was incorrectly being considered a builtin printf function that would be handled specifically by the SPIR-V backend.

The fix here is to be more restrictive, only allow printf itself or what seems like an intrinsic (starting with _ and including printf).

  1. We were getting an assert in ModuleVerifier saying the LLVM lifetime intrinsics were being called with an argument that was neither an alloca ptr or poison. The problem is the alloca was replaced with a SPIR-V intrinsic alloca in SPIRVPrepareFunctions, but the lifetime intrinsic added in ExpandVariadics was not lowered to the SPIR-V lifetime intrinsic since ExpandVariadics is run after SPIRVPrepareFunctions,

The fix here is to just run ExpandVariadics first.

  1. There were va intrinsics taking in a addrspace(4) pointer that were not being expanded.

The fix here is to extend ExpandVariadics to support expanding va intrinsics with target-specific address spaces.


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

4 Files Affected:

  • (modified) llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp (+6-4)
  • (modified) llvm/lib/Transforms/IPO/ExpandVariadics.cpp (+25-2)
  • (added) llvm/test/CodeGen/SPIRV/function/variadics-lowering-namespace-printf.ll (+41)
  • (modified) llvm/test/CodeGen/SPIRV/llc-pipeline.ll (+4-2)
diff --git a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
index 2d70972d6fbdb..35edfa1846421 100644
--- a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
@@ -176,14 +176,16 @@ TargetPassConfig *SPIRVTargetMachine::createPassConfig(PassManagerBase &PM) {
 void SPIRVPassConfig::addIRPasses() {
   TargetPassConfig::addIRPasses();
 
-  addPass(createSPIRVRegularizerPass());
-  addPass(createSPIRVPrepareFunctionsPass(TM));
-  addPass(createSPIRVPrepareGlobalsPass());
-
   // Variadic function calls aren't supported in shader code.
+  // This needs to come before SPIRVPrepareFunctions because this
+  // may introduce intrinsic calls.
   if (!TM.getSubtargetImpl()->isShader()) {
     addPass(createExpandVariadicsPass(ExpandVariadicsMode::Lowering));
   }
+
+  addPass(createSPIRVRegularizerPass());
+  addPass(createSPIRVPrepareFunctionsPass(TM));
+  addPass(createSPIRVPrepareGlobalsPass());
 }
 
 void SPIRVPassConfig::addISelPrepare() {
diff --git a/llvm/lib/Transforms/IPO/ExpandVariadics.cpp b/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
index a908db0d1830a..78e402e476332 100644
--- a/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
+++ b/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
@@ -53,6 +53,7 @@
 
 #include "llvm/Transforms/IPO/ExpandVariadics.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/Demangle/Demangle.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/Module.h"
@@ -131,6 +132,12 @@ class VariadicABIInfo {
   bool vaEndIsNop() { return true; }
   bool vaCopyIsMemcpy() { return true; }
 
+  // Any additional address spaces used in va intrinsics that should be
+  // expanded.
+  virtual SmallVector<unsigned> getTargetSpecificVaIntrinAddrSpaces() const {
+    return {};
+  }
+
   virtual ~VariadicABIInfo() = default;
 };
 
@@ -364,13 +371,21 @@ bool ExpandVariadics::runOnModule(Module &M) {
   // variadic functions have also been replaced.
 
   {
-    // 0 and AllocaAddrSpace are sufficient for the targets implemented so far
     unsigned Addrspace = 0;
     Changed |= expandVAIntrinsicUsersWithAddrspace(M, Builder, Addrspace);
 
     Addrspace = DL.getAllocaAddrSpace();
     if (Addrspace != 0)
       Changed |= expandVAIntrinsicUsersWithAddrspace(M, Builder, Addrspace);
+
+    // Process any addrspaces targets declare to be important.
+    const SmallVector<unsigned> &TargetASVec =
+        ABI->getTargetSpecificVaIntrinAddrSpaces();
+    for (unsigned TargetAS : TargetASVec) {
+      if (TargetAS == 0 || TargetAS == DL.getAllocaAddrSpace())
+        continue;
+      Changed |= expandVAIntrinsicUsersWithAddrspace(M, Builder, TargetAS);
+    }
   }
 
   if (Mode != ExpandVariadicsMode::Lowering)
@@ -981,7 +996,15 @@ struct SPIRV final : public VariadicABIInfo {
   // The SPIR-V backend has special handling for SPIR-V mangled printf
   // functions.
   bool ignoreFunction(Function *F) override {
-    return F->getName().starts_with('_') && F->getName().contains("printf");
+    StringRef DemangledName =
+        llvm::itaniumDemangle(F->getName(), /*ParseArgs=*/false);
+    return DemangledName == "printf" ||
+           (DemangledName.starts_with('_') && DemangledName.contains("printf"));
+  }
+
+  // We will likely see va intrinsics in the generic addrspace (4).
+  SmallVector<unsigned> getTargetSpecificVaIntrinAddrSpaces() const override {
+    return {4};
   }
 };
 
diff --git a/llvm/test/CodeGen/SPIRV/function/variadics-lowering-namespace-printf.ll b/llvm/test/CodeGen/SPIRV/function/variadics-lowering-namespace-printf.ll
new file mode 100644
index 0000000000000..6e2fda6a7e5eb
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/function/variadics-lowering-namespace-printf.ll
@@ -0,0 +1,41 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_function_pointers < %s 2>&1 | FileCheck %s
+
+; CHECK: OpName [[MAIN:%.*]] "main"
+; CHECK: [[MAIN]] = OpFunction
+; CHECK-NOT: OPFunctionEnd
+; CHECK: OpLifetimeStart
+; CHECK-NOT: OPFunctionEnd
+; CHECK: OpLifetimeStop
+; CHECK: OpFunctionEnd
+
+@.str = private unnamed_addr addrspace(1) constant [3 x i8] c"%s\00", align 1
+@.str.1 = private unnamed_addr addrspace(1) constant [4 x i8] c"hey\00", align 1
+
+declare spir_func noundef i32 @_Z7vprintfPKcz(ptr addrspace(4) noundef %0, ...) addrspace(9)
+
+define spir_func noundef i32 @_ZN4ompx6printfEPKcz(ptr addrspace(4) noundef %Format, ...) addrspace(9) {
+entry:
+  %retval = alloca i32, align 4
+  %Format.addr = alloca ptr addrspace(4), align 8
+  %vlist = alloca ptr addrspace(4), align 8
+  %retval.ascast = addrspacecast ptr %retval to ptr addrspace(4)
+  %Format.addr.ascast = addrspacecast ptr %Format.addr to ptr addrspace(4)
+  %vlist.ascast = addrspacecast ptr %vlist to ptr addrspace(4)
+  store ptr addrspace(4) %Format, ptr addrspace(4) %Format.addr.ascast, align 8
+  call addrspace(9) void @llvm.va_start.p4(ptr addrspace(4) %vlist.ascast)
+  %0 = load ptr addrspace(4), ptr addrspace(4) %Format.addr.ascast, align 8
+  %1 = load ptr addrspace(4), ptr addrspace(4) %vlist.ascast, align 8
+  %call = call spir_func noundef addrspace(9) i32 (ptr addrspace(4), ...) @_Z7vprintfPKcz(ptr addrspace(4) noundef %0, ptr addrspace(4) noundef %1)
+  ret i32 %call
+}
+
+declare void @llvm.va_start.p4(ptr addrspace(4)) addrspace(9)
+
+define noundef i32 @main() addrspace(9) {
+entry:
+  %retval = alloca i32, align 4
+  %retval.ascast = addrspacecast ptr %retval to ptr addrspace(4)
+  store i32 0, ptr addrspace(4) %retval.ascast, align 4
+  %call = call spir_func noundef addrspace(9) i32 (ptr addrspace(4), ...) @_ZN4ompx6printfEPKcz(ptr addrspace(4) noundef addrspacecast (ptr addrspace(1) @.str to ptr addrspace(4)), ptr addrspace(4) noundef addrspacecast (ptr addrspace(1) @.str.1 to ptr addrspace(4))) #5
+  ret i32 0
+}
diff --git a/llvm/test/CodeGen/SPIRV/llc-pipeline.ll b/llvm/test/CodeGen/SPIRV/llc-pipeline.ll
index 2f8b9decaaba3..1c17dfd6df28f 100644
--- a/llvm/test/CodeGen/SPIRV/llc-pipeline.ll
+++ b/llvm/test/CodeGen/SPIRV/llc-pipeline.ll
@@ -30,10 +30,11 @@
 ; SPIRV-O0-NEXT:      Instrument function entry/exit with calls to e.g. mcount() (post inlining)
 ; SPIRV-O0-NEXT:      Scalarize Masked Memory Intrinsics
 ; SPIRV-O0-NEXT:      Expand reduction intrinsics
+; SPIRV-O0-NEXT:    Expand variadic functions
+; SPIRV-O0-NEXT:    FunctionPass Manager
 ; SPIRV-O0-NEXT:      SPIR-V Regularizer
 ; SPIRV-O0-NEXT:    SPIRV prepare functions
 ; SPIRV-O0-NEXT:    SPIRV prepare global variables
-; SPIRV-O0-NEXT:    Expand variadic functions
 ; SPIRV-O0-NEXT:    FunctionPass Manager
 ; SPIRV-O0-NEXT:      Lower invoke and unwind, for unwindless code generators
 ; SPIRV-O0-NEXT:      Remove unreachable blocks from the CFG
@@ -134,10 +135,11 @@
 ; SPIRV-Opt-NEXT:      Instrument function entry/exit with calls to e.g. mcount() (post inlining)
 ; SPIRV-Opt-NEXT:      Scalarize Masked Memory Intrinsics
 ; SPIRV-Opt-NEXT:      Expand reduction intrinsics
+; SPIRV-Opt-NEXT:    Expand variadic functions
+; SPIRV-Opt-NEXT:    FunctionPass Manager
 ; SPIRV-Opt-NEXT:      SPIR-V Regularizer
 ; SPIRV-Opt-NEXT:    SPIRV prepare functions
 ; SPIRV-Opt-NEXT:    SPIRV prepare global variables
-; SPIRV-Opt-NEXT:    Expand variadic functions
 ; SPIRV-Opt-NEXT:    FunctionPass Manager
 ; SPIRV-Opt-NEXT:      Dominator Tree Construction
 ; SPIRV-Opt-NEXT:      Natural Loop Information

@llvmbot
Copy link
Member

llvmbot commented Feb 2, 2026

@llvm/pr-subscribers-llvm-transforms

Author: Nick Sarnie (sarnex)

Changes

Variadic function lowering for SPIR-V was initially added in #175076.

However, I tried a full OpenMP offloading example that includes a vararg call and hit a few issues:

  1. The OpenMP Deivce library function ompx::printf was incorrectly being considered a builtin printf function that would be handled specifically by the SPIR-V backend.

The fix here is to be more restrictive, only allow printf itself or what seems like an intrinsic (starting with _ and including printf).

  1. We were getting an assert in ModuleVerifier saying the LLVM lifetime intrinsics were being called with an argument that was neither an alloca ptr or poison. The problem is the alloca was replaced with a SPIR-V intrinsic alloca in SPIRVPrepareFunctions, but the lifetime intrinsic added in ExpandVariadics was not lowered to the SPIR-V lifetime intrinsic since ExpandVariadics is run after SPIRVPrepareFunctions,

The fix here is to just run ExpandVariadics first.

  1. There were va intrinsics taking in a addrspace(4) pointer that were not being expanded.

The fix here is to extend ExpandVariadics to support expanding va intrinsics with target-specific address spaces.


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

4 Files Affected:

  • (modified) llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp (+6-4)
  • (modified) llvm/lib/Transforms/IPO/ExpandVariadics.cpp (+25-2)
  • (added) llvm/test/CodeGen/SPIRV/function/variadics-lowering-namespace-printf.ll (+41)
  • (modified) llvm/test/CodeGen/SPIRV/llc-pipeline.ll (+4-2)
diff --git a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
index 2d70972d6fbdb..35edfa1846421 100644
--- a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
@@ -176,14 +176,16 @@ TargetPassConfig *SPIRVTargetMachine::createPassConfig(PassManagerBase &PM) {
 void SPIRVPassConfig::addIRPasses() {
   TargetPassConfig::addIRPasses();
 
-  addPass(createSPIRVRegularizerPass());
-  addPass(createSPIRVPrepareFunctionsPass(TM));
-  addPass(createSPIRVPrepareGlobalsPass());
-
   // Variadic function calls aren't supported in shader code.
+  // This needs to come before SPIRVPrepareFunctions because this
+  // may introduce intrinsic calls.
   if (!TM.getSubtargetImpl()->isShader()) {
     addPass(createExpandVariadicsPass(ExpandVariadicsMode::Lowering));
   }
+
+  addPass(createSPIRVRegularizerPass());
+  addPass(createSPIRVPrepareFunctionsPass(TM));
+  addPass(createSPIRVPrepareGlobalsPass());
 }
 
 void SPIRVPassConfig::addISelPrepare() {
diff --git a/llvm/lib/Transforms/IPO/ExpandVariadics.cpp b/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
index a908db0d1830a..78e402e476332 100644
--- a/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
+++ b/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
@@ -53,6 +53,7 @@
 
 #include "llvm/Transforms/IPO/ExpandVariadics.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/Demangle/Demangle.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/Module.h"
@@ -131,6 +132,12 @@ class VariadicABIInfo {
   bool vaEndIsNop() { return true; }
   bool vaCopyIsMemcpy() { return true; }
 
+  // Any additional address spaces used in va intrinsics that should be
+  // expanded.
+  virtual SmallVector<unsigned> getTargetSpecificVaIntrinAddrSpaces() const {
+    return {};
+  }
+
   virtual ~VariadicABIInfo() = default;
 };
 
@@ -364,13 +371,21 @@ bool ExpandVariadics::runOnModule(Module &M) {
   // variadic functions have also been replaced.
 
   {
-    // 0 and AllocaAddrSpace are sufficient for the targets implemented so far
     unsigned Addrspace = 0;
     Changed |= expandVAIntrinsicUsersWithAddrspace(M, Builder, Addrspace);
 
     Addrspace = DL.getAllocaAddrSpace();
     if (Addrspace != 0)
       Changed |= expandVAIntrinsicUsersWithAddrspace(M, Builder, Addrspace);
+
+    // Process any addrspaces targets declare to be important.
+    const SmallVector<unsigned> &TargetASVec =
+        ABI->getTargetSpecificVaIntrinAddrSpaces();
+    for (unsigned TargetAS : TargetASVec) {
+      if (TargetAS == 0 || TargetAS == DL.getAllocaAddrSpace())
+        continue;
+      Changed |= expandVAIntrinsicUsersWithAddrspace(M, Builder, TargetAS);
+    }
   }
 
   if (Mode != ExpandVariadicsMode::Lowering)
@@ -981,7 +996,15 @@ struct SPIRV final : public VariadicABIInfo {
   // The SPIR-V backend has special handling for SPIR-V mangled printf
   // functions.
   bool ignoreFunction(Function *F) override {
-    return F->getName().starts_with('_') && F->getName().contains("printf");
+    StringRef DemangledName =
+        llvm::itaniumDemangle(F->getName(), /*ParseArgs=*/false);
+    return DemangledName == "printf" ||
+           (DemangledName.starts_with('_') && DemangledName.contains("printf"));
+  }
+
+  // We will likely see va intrinsics in the generic addrspace (4).
+  SmallVector<unsigned> getTargetSpecificVaIntrinAddrSpaces() const override {
+    return {4};
   }
 };
 
diff --git a/llvm/test/CodeGen/SPIRV/function/variadics-lowering-namespace-printf.ll b/llvm/test/CodeGen/SPIRV/function/variadics-lowering-namespace-printf.ll
new file mode 100644
index 0000000000000..6e2fda6a7e5eb
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/function/variadics-lowering-namespace-printf.ll
@@ -0,0 +1,41 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_function_pointers < %s 2>&1 | FileCheck %s
+
+; CHECK: OpName [[MAIN:%.*]] "main"
+; CHECK: [[MAIN]] = OpFunction
+; CHECK-NOT: OPFunctionEnd
+; CHECK: OpLifetimeStart
+; CHECK-NOT: OPFunctionEnd
+; CHECK: OpLifetimeStop
+; CHECK: OpFunctionEnd
+
+@.str = private unnamed_addr addrspace(1) constant [3 x i8] c"%s\00", align 1
+@.str.1 = private unnamed_addr addrspace(1) constant [4 x i8] c"hey\00", align 1
+
+declare spir_func noundef i32 @_Z7vprintfPKcz(ptr addrspace(4) noundef %0, ...) addrspace(9)
+
+define spir_func noundef i32 @_ZN4ompx6printfEPKcz(ptr addrspace(4) noundef %Format, ...) addrspace(9) {
+entry:
+  %retval = alloca i32, align 4
+  %Format.addr = alloca ptr addrspace(4), align 8
+  %vlist = alloca ptr addrspace(4), align 8
+  %retval.ascast = addrspacecast ptr %retval to ptr addrspace(4)
+  %Format.addr.ascast = addrspacecast ptr %Format.addr to ptr addrspace(4)
+  %vlist.ascast = addrspacecast ptr %vlist to ptr addrspace(4)
+  store ptr addrspace(4) %Format, ptr addrspace(4) %Format.addr.ascast, align 8
+  call addrspace(9) void @llvm.va_start.p4(ptr addrspace(4) %vlist.ascast)
+  %0 = load ptr addrspace(4), ptr addrspace(4) %Format.addr.ascast, align 8
+  %1 = load ptr addrspace(4), ptr addrspace(4) %vlist.ascast, align 8
+  %call = call spir_func noundef addrspace(9) i32 (ptr addrspace(4), ...) @_Z7vprintfPKcz(ptr addrspace(4) noundef %0, ptr addrspace(4) noundef %1)
+  ret i32 %call
+}
+
+declare void @llvm.va_start.p4(ptr addrspace(4)) addrspace(9)
+
+define noundef i32 @main() addrspace(9) {
+entry:
+  %retval = alloca i32, align 4
+  %retval.ascast = addrspacecast ptr %retval to ptr addrspace(4)
+  store i32 0, ptr addrspace(4) %retval.ascast, align 4
+  %call = call spir_func noundef addrspace(9) i32 (ptr addrspace(4), ...) @_ZN4ompx6printfEPKcz(ptr addrspace(4) noundef addrspacecast (ptr addrspace(1) @.str to ptr addrspace(4)), ptr addrspace(4) noundef addrspacecast (ptr addrspace(1) @.str.1 to ptr addrspace(4))) #5
+  ret i32 0
+}
diff --git a/llvm/test/CodeGen/SPIRV/llc-pipeline.ll b/llvm/test/CodeGen/SPIRV/llc-pipeline.ll
index 2f8b9decaaba3..1c17dfd6df28f 100644
--- a/llvm/test/CodeGen/SPIRV/llc-pipeline.ll
+++ b/llvm/test/CodeGen/SPIRV/llc-pipeline.ll
@@ -30,10 +30,11 @@
 ; SPIRV-O0-NEXT:      Instrument function entry/exit with calls to e.g. mcount() (post inlining)
 ; SPIRV-O0-NEXT:      Scalarize Masked Memory Intrinsics
 ; SPIRV-O0-NEXT:      Expand reduction intrinsics
+; SPIRV-O0-NEXT:    Expand variadic functions
+; SPIRV-O0-NEXT:    FunctionPass Manager
 ; SPIRV-O0-NEXT:      SPIR-V Regularizer
 ; SPIRV-O0-NEXT:    SPIRV prepare functions
 ; SPIRV-O0-NEXT:    SPIRV prepare global variables
-; SPIRV-O0-NEXT:    Expand variadic functions
 ; SPIRV-O0-NEXT:    FunctionPass Manager
 ; SPIRV-O0-NEXT:      Lower invoke and unwind, for unwindless code generators
 ; SPIRV-O0-NEXT:      Remove unreachable blocks from the CFG
@@ -134,10 +135,11 @@
 ; SPIRV-Opt-NEXT:      Instrument function entry/exit with calls to e.g. mcount() (post inlining)
 ; SPIRV-Opt-NEXT:      Scalarize Masked Memory Intrinsics
 ; SPIRV-Opt-NEXT:      Expand reduction intrinsics
+; SPIRV-Opt-NEXT:    Expand variadic functions
+; SPIRV-Opt-NEXT:    FunctionPass Manager
 ; SPIRV-Opt-NEXT:      SPIR-V Regularizer
 ; SPIRV-Opt-NEXT:    SPIRV prepare functions
 ; SPIRV-Opt-NEXT:    SPIRV prepare global variables
-; SPIRV-Opt-NEXT:    Expand variadic functions
 ; SPIRV-Opt-NEXT:    FunctionPass Manager
 ; SPIRV-Opt-NEXT:      Dominator Tree Construction
 ; SPIRV-Opt-NEXT:      Natural Loop Information

@jhuber6
Copy link
Contributor

jhuber6 commented Feb 2, 2026

I'm really not a fan of backends doing these kinds of rewrites, I wish we could do away with it entirely and just call a printf symbol like CUDA does now that variadics actually 'work'. The better solution here is to prevent this renaming entirely if the module has the openmp flag and then disable this special handling for OpenMP as well. Only reason I did this was because there were some printf related tests that failed.

I'm very tired of every GPU target putting magic handling into the compiler just to work around problems that already have solutions.

@jhuber6
Copy link
Contributor

jhuber6 commented Feb 2, 2026

That being said, I don't know exactly how SPIR-V supports printf. If it's OpenMP then presumably the goal would be to build libc and figure out if rpc works properly.

@sarnex
Copy link
Member Author

sarnex commented Feb 2, 2026

Just to be clear, there are three fixes here and your concern only seems to be with the first one about the printf matching, the later two fixes have nothing to do with printf and those root issues can be reproduced with any variadic function with the right conditions.

I'll take a look at the printf handling.

Signed-off-by: Nick Sarnie <nick.sarnie@intel.com>
@sarnex
Copy link
Member Author

sarnex commented Feb 2, 2026

@jhuber6 Hopefully the change looks better with the commit I just pushed

bool ignoreFunction(Function *F) override {
return F->getName().starts_with('_') && F->getName().contains("printf");
// We will likely see va intrinsics in the generic addrspace (4).
SmallVector<unsigned> getTargetSpecificVaIntrinAddrSpaces() const override {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only reason this handling existed was to preserve the 'old' printf handling. It seems a little strange to only lower functions based off of the address space, this is supposed to be the canonical variadic ABI for the target. Ideally we wouldn't leave anything unlowered. I mostly hacked around the printf issue because I don't know anything about its intended use.

Here's a fun hack if you just want the OpenMP printf to work, just rename it with asm("foobar").

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The address space handing was there previously and you didn't modify it in your change. The existing code has some weird logic about address spaces, only lowering 0 and the alloca AS. I have no idea why that is, but I think it's safer to add a known-good case than start processing everything for all targets.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jhuber6 Did you have any more feedback on this PR? I think there was a bit of a misunderstanding before, the proposed changes to ExpandVariadics should not be specific to printf (in the latest iteration of the PR), this PR actually aims to remove all of that, and I think the addition of handling target address spaces fits reasonably within the existing code that already has a list of address spaces to try to lower. Thanks.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jhuber6 Mind taking another look at this with the above context? If you don't feel comfortable reviewing I can try to find someone else, thanks

sarnex added a commit that referenced this pull request Feb 9, 2026
)

I'll merge this at the same time as some llvm-zorg changes that start
building the DeviceRTL.

We only see one new test passing because everything still fails because
of the issue described in
#178980

Once a fix for that issue is merged we will see many new passes.
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Feb 9, 2026
…esent (#180231)

I'll merge this at the same time as some llvm-zorg changes that start
building the DeviceRTL.

We only see one new test passing because everything still fails because
of the issue described in
llvm/llvm-project#178980

Once a fix for that issue is merged we will see many new passes.
Copy link
Contributor

@jhuber6 jhuber6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LG, thanks. Make the CI green before merging though

@sarnex
Copy link
Member Author

sarnex commented Feb 13, 2026

Thanks!

Those aren't related and fail with any PR that touches SPIRV files since that causes those checks to run, but I'll rebase and hope it's fixed and if not fix it in a separate PR before merging this.

@jhuber6 jhuber6 temporarily deployed to main-branch-only February 13, 2026 17:25 — with GitHub Actions Inactive
@sarnex
Copy link
Member Author

sarnex commented Feb 13, 2026

Oh cool thanks

@sarnex
Copy link
Member Author

sarnex commented Feb 13, 2026

Thanks Farzon!

@jhuber6
Copy link
Contributor

jhuber6 commented Feb 13, 2026

Seems the SPIR-V failures are all about FP rounding modes, so it's probably safe to ignore for now.

@sarnex
Copy link
Member Author

sarnex commented Feb 13, 2026

Thanks, I'll make a GH issue and then a PR to temporarily disable them so CI passes

@sarnex sarnex merged commit e4c30c1 into llvm:main Feb 13, 2026
10 of 11 checks passed
manasij7479 pushed a commit to manasij7479/llvm-project that referenced this pull request Feb 18, 2026
Variadic function lowering for SPIR-V was initially added in
llvm#175076.

However, I tried a full OpenMP offloading example that includes a vararg
call and hit a few issues:

1) The OpenMP Deivce library function `ompx::printf` was incorrectly
being considered a builtin `printf` function that would be handled
specifically by the SPIR-V backend.

The fix here is to remove the `printf` special handling.

2) We were getting an assert in ModuleVerifier saying the LLVM lifetime
intrinsics were being called with an argument that was neither an
`alloca` ptr or `poison`. The problem is the `alloca` was replaced with
a SPIR-V intrinsic `alloca` in `SPIRVPrepareFunctions`, but the lifetime
intrinsic added in `ExpandVariadics` was not lowered to the SPIR-V
lifetime intrinsic since `ExpandVariadics` is run after
`SPIRVPrepareFunctions`,

The fix here is to just run `ExpandVariadics` first.

3) There were `va` intrinsics taking in a `addrspace(4)` pointer that
were not being expanded.

The fix here is to extend `ExpandVariadics` to support expanding `va`
intrinsics with target-specific address spaces.

---------

Signed-off-by: Nick Sarnie <nick.sarnie@intel.com>
Co-authored-by: Joseph Huber <huberjn@outlook.com>
@sarnex
Copy link
Member Author

sarnex commented Feb 19, 2026

Just to close the loop there seem to be no SPIR-V CI tests failing on unrelated PRs anymore so we should be all set.

silee2 added a commit to silee2/llvm-project that referenced this pull request Feb 20, 2026
printf function is treated in a special way by SPIRV backend
It lowers to a SPIRV extension op and not a function call.
As such, should not be handled with the general expand variadics.
This PR restores filtering capability removed by llvm#178980
in a more precise way to avoid false positives.
First, target triple is checked if it SPIR-V
And checks against more precise set of mangled names.

Filter based on target triple.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants