[llvm] [SPIR-V] Simplify addrspacecast for null in composite constant preprocessing (PR #192030)

Arseniy Obolenskiy via llvm-commits llvm-commits at lists.llvm.org
Tue Apr 14 02:31:03 PDT 2026


https://github.com/aobolensk created https://github.com/llvm/llvm-project/pull/192030

Fold addrspacecast(null) to a typed null pointer during composite constant preprocessing so that null pointer members in structs and arrays get the correct SPIR-V pointer type instead of being lowered as integers. This fixes invalid SPIR-V where OpConstantNull had an integer type instead of a pointer type, and where OpSpecConstantOp was unnecessarily emitted for null casts

>From 82092433d70d64365a1e2d8f1f3ba1b8625cf558 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Tue, 14 Apr 2026 11:29:11 +0200
Subject: [PATCH] [SPIR-V] Simplify addrspacecast for null in composite
 constant preprocessing

Fold addrspacecast(null) to a typed null pointer during composite constant preprocessing so that null pointer members in structs and arrays get the correct SPIR-V pointer type instead of being lowered as integers. This fixes invalid SPIR-V where OpConstantNull had an integer type instead of a pointer type, and where OpSpecConstantOp was unnecessarily emitted for null casts
---
 llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 11 +++++++++-
 .../fun-ptr-addrcast.ll                       |  4 +++-
 .../opencl/device_execution/execute_block.ll  |  1 +
 .../PtrCast-null-in-OpSpecConstantOp.ll       | 13 ++++++-----
 .../SPIRV/struct-null-pointer-member.ll       | 22 +++++++++++++++++++
 5 files changed, 43 insertions(+), 8 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/struct-null-pointer-member.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 7600b3a695a4e..63ad1b75888ab 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -1568,7 +1568,16 @@ void SPIRVEmitIntrinsics::preprocessCompositeConstants(IRBuilder<> &B) {
           for (unsigned i = 0; i < COp->getNumElements(); ++i)
             Args.push_back(COp->getElementAsConstant(i));
         else
-          llvm::append_range(Args, AggrConst->operands());
+          for (Value *Op : AggrConst->operands()) {
+            // Simplify addrspacecast(null) to null in the target address space
+            // so that null pointers get the correct pointer type when lowered.
+            if (auto *CE = dyn_cast<ConstantExpr>(Op);
+                CE && CE->getOpcode() == Instruction::AddrSpaceCast &&
+                isa<ConstantPointerNull>(CE->getOperand(0)))
+              Op = ConstantPointerNull::get(
+                  cast<PointerType>(CE->getType()));
+            Args.push_back(Op);
+          }
         if (!BPrepared) {
           IsPhi ? B.SetInsertPointPastAllocas(I->getParent()->getParent())
                 : B.SetInsertPoint(I);
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_function_pointers/fun-ptr-addrcast.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_function_pointers/fun-ptr-addrcast.ll
index e5111304765e2..854981ca06784 100644
--- a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_function_pointers/fun-ptr-addrcast.ll
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_function_pointers/fun-ptr-addrcast.ll
@@ -8,7 +8,9 @@
 ;   %_ptr_Generic_7 = OpTypePointer Generic %7
 ; TODO: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj --spirv-ext=+SPV_INTEL_function_pointers | spirv-val %}
 
-; CHECK-COUNT-3: %[[#]] = OpSpecConstantOp %[[#]] PtrCastToGeneric %[[#]]
+; CHECK-DAG: %[[#GENPTR:]] = OpTypePointer Generic %[[#]]
+; CHECK-DAG: %[[#]] = OpConstantNull %[[#GENPTR]]
+; CHECK-COUNT-2: %[[#]] = OpSpecConstantOp %[[#]] PtrCastToGeneric %[[#]]
 ; CHECK-COUNT-3: OpPtrCastToGeneric
 
 @G1 = addrspace(1) constant { [3 x ptr addrspace(4)] } { [3 x ptr addrspace(4)] [ptr addrspace(4) null, ptr addrspace(4) addrspacecast (ptr @foo to ptr addrspace(4)), ptr addrspace(4) addrspacecast (ptr @bar to ptr addrspace(4))] }
diff --git a/llvm/test/CodeGen/SPIRV/opencl/device_execution/execute_block.ll b/llvm/test/CodeGen/SPIRV/opencl/device_execution/execute_block.ll
index 54a6e8e38d536..ab4fec3121dd3 100644
--- a/llvm/test/CodeGen/SPIRV/opencl/device_execution/execute_block.ll
+++ b/llvm/test/CodeGen/SPIRV/opencl/device_execution/execute_block.ll
@@ -1,4 +1,5 @@
 ; 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 %}
 
 ; CHECK: %[[#bool:]] = OpTypeBool
 ; CHECK: %[[#true:]] = OpConstantTrue %[[#bool]]
diff --git a/llvm/test/CodeGen/SPIRV/pointers/PtrCast-null-in-OpSpecConstantOp.ll b/llvm/test/CodeGen/SPIRV/pointers/PtrCast-null-in-OpSpecConstantOp.ll
index 49fb43d7e9625..d8d1efc02bacb 100644
--- a/llvm/test/CodeGen/SPIRV/pointers/PtrCast-null-in-OpSpecConstantOp.ll
+++ b/llvm/test/CodeGen/SPIRV/pointers/PtrCast-null-in-OpSpecConstantOp.ll
@@ -1,12 +1,13 @@
 ; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
-; TODO: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
-; CHECK-DAG: %[[Array:.*]] = OpTypeArray %[[#]] %[[#]]
+; CHECK-DAG: %[[Char:.*]] = OpTypeInt 8 0
+; CHECK-DAG: %[[GenPtr:.*]] = OpTypePointer Generic %[[Char]]
+; CHECK-DAG: %[[Array:.*]] = OpTypeArray %[[GenPtr]] %[[#]]
 ; CHECK-DAG: %[[Struct:.*]] = OpTypeStruct %[[Array]]
-; CHECK-DAG: %[[Zero:.*]] = OpTypeInt 64 0
-; CHECK-DAG: %[[Null:.*]] = OpConstantNull %[[Zero]]
-; CHECK-DAG: %[[R:.*]] = OpSpecConstantComposite %[[Array]] %[[Null]]
-; CHECK-DAG: %[[#]] = OpSpecConstantComposite %[[Struct]] %[[R]]
+; CHECK-DAG: %[[Null:.*]] = OpConstantNull %[[GenPtr]]
+; CHECK-DAG: %[[R:.*]] = OpConstantComposite %[[Array]] %[[Null]]
+; CHECK-DAG: %[[#]] = OpConstantComposite %[[Struct]] %[[R]]
 
 @G1 = addrspace(1) constant { [1 x ptr addrspace(4)] } { [1 x ptr addrspace(4)] [ptr addrspace(4) addrspacecast (ptr null to ptr addrspace(4))] }
 @G2 = addrspace(1) constant { [1 x ptr addrspace(4)] } { [1 x ptr addrspace(4)] [ptr addrspace(4) addrspacecast (ptr addrspace(1) null to ptr addrspace(4))] }
diff --git a/llvm/test/CodeGen/SPIRV/struct-null-pointer-member.ll b/llvm/test/CodeGen/SPIRV/struct-null-pointer-member.ll
new file mode 100644
index 0000000000000..3be36985dde2e
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/struct-null-pointer-member.ll
@@ -0,0 +1,22 @@
+; RUN: llc -verify-machineinstrs -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 %}
+
+; Test that a null pointer member in a struct constant (via addrspacecast)
+; gets the correct pointer type in OpConstantComposite, not integer type.
+
+%struct = type { i32, ptr addrspace(4) }
+
+ at gv = internal addrspace(1) constant %struct { i32 42, ptr addrspace(4) addrspacecast (ptr null to ptr addrspace(4)) }, align 8
+
+; CHECK-DAG: %[[#INT:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#CHAR:]] = OpTypeInt 8 0
+; CHECK-DAG: %[[#GENPTR:]] = OpTypePointer Generic %[[#CHAR]]
+; CHECK-DAG: %[[#STRUCT:]] = OpTypeStruct %[[#INT]] %[[#GENPTR]]
+; CHECK-DAG: %[[#CONST:]] = OpConstant %[[#INT]] 42
+; CHECK-DAG: %[[#NULL:]] = OpConstantNull %[[#GENPTR]]
+; CHECK: OpConstantComposite %[[#STRUCT]] %[[#CONST]] %[[#NULL]]
+
+define spir_kernel void @test(ptr addrspace(1) %out) {
+entry:
+  ret void
+}



More information about the llvm-commits mailing list