[llvm] [SPIRV] Add logic for OpGenericCastToPtrExplicit rewriting (PR #146596)

Justin Cai via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 9 08:22:04 PDT 2025


https://github.com/jzc updated https://github.com/llvm/llvm-project/pull/146596

>From 96edcaf5d2351462f7a3f2553aa26b6467139066 Mon Sep 17 00:00:00 2001
From: "Cai, Justin" <justin.cai at intel.com>
Date: Tue, 1 Jul 2025 19:57:11 +0000
Subject: [PATCH 1/2] [SPIRV] Add logic for OpGenericCastToPtrExplicit
 rewriting

---
 llvm/lib/Target/SPIRV/CMakeLists.txt          |  1 +
 .../Target/SPIRV/SPIRVTargetTransformInfo.cpp | 40 ++++++++
 .../Target/SPIRV/SPIRVTargetTransformInfo.h   | 13 +--
 .../SPIRV/generic-cast-explicit.ll            | 95 +++++++++++++++++++
 4 files changed, 143 insertions(+), 6 deletions(-)
 create mode 100644 llvm/lib/Target/SPIRV/SPIRVTargetTransformInfo.cpp
 create mode 100644 llvm/test/Transforms/InferAddressSpaces/SPIRV/generic-cast-explicit.ll

diff --git a/llvm/lib/Target/SPIRV/CMakeLists.txt b/llvm/lib/Target/SPIRV/CMakeLists.txt
index 4a2b534b948d6..ba09451104479 100644
--- a/llvm/lib/Target/SPIRV/CMakeLists.txt
+++ b/llvm/lib/Target/SPIRV/CMakeLists.txt
@@ -44,6 +44,7 @@ add_llvm_target(SPIRVCodeGen
   SPIRVRegularizer.cpp
   SPIRVSubtarget.cpp
   SPIRVTargetMachine.cpp
+  SPIRVTargetTransformInfo.cpp
   SPIRVUtils.cpp
   SPIRVEmitNonSemanticDI.cpp
 
diff --git a/llvm/lib/Target/SPIRV/SPIRVTargetTransformInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVTargetTransformInfo.cpp
new file mode 100644
index 0000000000000..95093d2b3c263
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVTargetTransformInfo.cpp
@@ -0,0 +1,40 @@
+//===- SPIRVTargetTransformInfo.cpp - SPIR-V specific TTI -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SPIRVTargetTransformInfo.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+
+using namespace llvm;
+
+bool llvm::SPIRVTTIImpl::collectFlatAddressOperands(
+    SmallVectorImpl<int> &OpIndexes, Intrinsic::ID IID) const {
+  switch (IID) {
+  case Intrinsic::spv_generic_cast_to_ptr_explicit:
+    OpIndexes.push_back(0);
+    return true;
+  default:
+    return false;
+  }
+}
+
+Value *llvm::SPIRVTTIImpl::rewriteIntrinsicWithAddressSpace(IntrinsicInst *II,
+                                                            Value *OldV,
+                                                            Value *NewV) const {
+  auto IntrID = II->getIntrinsicID();
+  switch (IntrID) {
+  case Intrinsic::spv_generic_cast_to_ptr_explicit: {
+    unsigned NewAS = NewV->getType()->getPointerAddressSpace();
+    unsigned DstAS = II->getType()->getPointerAddressSpace();
+    return NewAS == DstAS ? NewV
+                          : ConstantPointerNull::get(
+                                PointerType::get(NewV->getContext(), DstAS));
+  }
+  default:
+    return nullptr;
+  }
+}
diff --git a/llvm/lib/Target/SPIRV/SPIRVTargetTransformInfo.h b/llvm/lib/Target/SPIRV/SPIRVTargetTransformInfo.h
index 40e561ba38881..43bf6e9dd2a6e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVTargetTransformInfo.h
+++ b/llvm/lib/Target/SPIRV/SPIRVTargetTransformInfo.h
@@ -50,14 +50,15 @@ class SPIRVTTIImpl final : public BasicTTIImplBase<SPIRVTTIImpl> {
   }
 
   unsigned getFlatAddressSpace() const override {
-    if (ST->isShader())
-      return 0;
-    // FIXME: Clang has 2 distinct address space maps. One where
+    // Clang has 2 distinct address space maps. One where
     // default=4=Generic, and one with default=0=Function. This depends on the
-    // environment. For OpenCL, we don't need to run the InferAddrSpace pass, so
-    // we can return -1, but we might want to fix this.
-    return -1;
+    // environment.
+    return ST->isShader() ? 0 : 4;
   }
+  bool collectFlatAddressOperands(SmallVectorImpl<int> &OpIndexes,
+                                  Intrinsic::ID IID) const override;
+  Value *rewriteIntrinsicWithAddressSpace(IntrinsicInst *II, Value *OldV,
+                                          Value *NewV) const override;
 };
 
 } // namespace llvm
diff --git a/llvm/test/Transforms/InferAddressSpaces/SPIRV/generic-cast-explicit.ll b/llvm/test/Transforms/InferAddressSpaces/SPIRV/generic-cast-explicit.ll
new file mode 100644
index 0000000000000..2c8076df868a3
--- /dev/null
+++ b/llvm/test/Transforms/InferAddressSpaces/SPIRV/generic-cast-explicit.ll
@@ -0,0 +1,95 @@
+; This test checks that the address space casts for SPIR-V generic pointer casts
+; are lowered correctly by the infer-address-spaces pass.
+; RUN: opt < %s -passes=infer-address-spaces -S --mtriple=spirv64-unknown-unknown | FileCheck %s
+
+; Casting a global pointer to a global pointer. 
+; The uses of c2 will be replaced with %global.
+; CHECK: @kernel1(ptr addrspace(1) %global)
+define i1 @kernel1(ptr addrspace(1) %global) {
+    %c1 = addrspacecast ptr addrspace(1) %global to ptr addrspace(4)
+    %c2 = call ptr addrspace(1) @llvm.spv.generic.cast.to.ptr.explicit(ptr addrspace(4) %c1)
+    ; CHECK: %b1 = icmp eq ptr addrspace(1) %global, null
+    %b1 = icmp eq ptr addrspace(1) %c2, null 
+    ret i1 %b1
+}
+
+; Casting a global pointer to a local pointer.
+; The uses of c2 will be replaced with null.
+; CHECK: @kernel2(ptr addrspace(1) %global)
+define i1 @kernel2(ptr addrspace(1) %global) {
+    %c1 = addrspacecast ptr addrspace(1) %global to ptr addrspace(4)
+    %c2 = call ptr addrspace(3) @llvm.spv.generic.cast.to.ptr.explicit(ptr addrspace(4) %c1)
+    ; CHECK: %b1 = icmp eq ptr addrspace(3) null, null
+    %b1 = icmp eq ptr addrspace(3) %c2, null
+    ret i1 %b1
+}
+
+; Casting a global pointer to a private pointer.
+; CHECK: @kernel3(ptr addrspace(1) %global)
+define i1 @kernel3(ptr addrspace(1) %global) {
+    %c1 = addrspacecast ptr addrspace(1) %global to ptr addrspace(4)
+    %c2 = call ptr @llvm.spv.generic.cast.to.ptr.explicit(ptr addrspace(4) %c1)
+    ; CHECK: %b1 = icmp eq ptr null, null
+    %b1 = icmp eq ptr %c2, null 
+    ret i1 %b1
+}
+
+; Casting a local pointer to a local pointer.
+; CHECK: @kernel4(ptr addrspace(3) %local)
+define i1 @kernel4(ptr addrspace(3) %local) {
+    %c1 = addrspacecast ptr addrspace(3) %local to ptr addrspace(4)
+    %c2 = call ptr addrspace(3) @llvm.spv.generic.cast.to.ptr.explicit(ptr addrspace(4) %c1)
+    ; CHECK: %b1 = icmp eq ptr addrspace(3) %local, null
+    %b1 = icmp eq ptr addrspace(3) %c2, null 
+    ret i1 %b1
+}
+
+; Casting a local pointer to a global pointer.
+; CHECK: @kernel5(ptr addrspace(3) %local)
+define i1 @kernel5(ptr addrspace(3) %local) {
+    %c1 = addrspacecast ptr addrspace(3) %local to ptr addrspace(4)
+    %c2 = call ptr addrspace(1) @llvm.spv.generic.cast.to.ptr.explicit(ptr addrspace(4) %c1)
+    ; CHECK: %b1 = icmp eq ptr addrspace(1) null, null
+    %b1 = icmp eq ptr addrspace(1) %c2, null 
+    ret i1 %b1
+}
+
+; Casting a local pointer to a private pointer.
+; CHECK: @kernel6(ptr addrspace(3) %local)
+define i1 @kernel6(ptr addrspace(3) %local) {
+    %c1 = addrspacecast ptr addrspace(3) %local to ptr addrspace(4)
+    %c2 = call ptr @llvm.spv.generic.cast.to.ptr.explicit(ptr addrspace(4) %c1)
+    ; CHECK: %b1 = icmp eq ptr null, null
+    %b1 = icmp eq ptr %c2, null 
+    ret i1 %b1
+}
+
+; Casting a private pointer to a private pointer.
+; CHECK: @kernel7(ptr %private)
+define i1 @kernel7(ptr %private) {
+    %c1 = addrspacecast ptr %private to ptr addrspace(4)
+    %c2 = call ptr @llvm.spv.generic.cast.to.ptr.explicit(ptr addrspace(4) %c1)
+    ; CHECK: %b1 = icmp eq ptr %private, null
+    %b1 = icmp eq ptr %c2, null 
+    ret i1 %b1
+}
+
+; Casting a private pointer to a global pointer.
+; CHECK: @kernel8(ptr %private)
+define i1 @kernel8(ptr %private) {
+    %c1 = addrspacecast ptr %private to ptr addrspace(4)
+    %c2 = call ptr addrspace(1) @llvm.spv.generic.cast.to.ptr.explicit(ptr addrspace(4) %c1)
+    ; CHECK: %b1 = icmp eq ptr addrspace(1) null, null
+    %b1 = icmp eq ptr addrspace(1) %c2, null
+    ret i1 %b1
+}
+
+; Casting a private pointer to a local pointer.
+; CHECK: @kernel9(ptr %private)
+define i1 @kernel9(ptr %private) {
+    %c1 = addrspacecast ptr %private to ptr addrspace(4)
+    %c2 = call ptr addrspace(3) @llvm.spv.generic.cast.to.ptr.explicit(ptr addrspace(4) %c1)
+    ; CHECK: %b1 = icmp eq ptr addrspace(3) null, null
+    %b1 = icmp eq ptr addrspace(3) %c2, null
+    ret i1 %b1
+}

>From d6653a99957457cfa539e7d39e2d40e93e68084e Mon Sep 17 00:00:00 2001
From: "Cai, Justin" <justin.cai at intel.com>
Date: Thu, 3 Jul 2025 15:01:46 +0000
Subject: [PATCH 2/2] add comments

---
 .../InferAddressSpaces/SPIRV/generic-cast-explicit.ll      | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/llvm/test/Transforms/InferAddressSpaces/SPIRV/generic-cast-explicit.ll b/llvm/test/Transforms/InferAddressSpaces/SPIRV/generic-cast-explicit.ll
index 2c8076df868a3..aa39797d74a10 100644
--- a/llvm/test/Transforms/InferAddressSpaces/SPIRV/generic-cast-explicit.ll
+++ b/llvm/test/Transforms/InferAddressSpaces/SPIRV/generic-cast-explicit.ll
@@ -25,6 +25,7 @@ define i1 @kernel2(ptr addrspace(1) %global) {
 }
 
 ; Casting a global pointer to a private pointer.
+; The uses of c2 will be replaced with null.
 ; CHECK: @kernel3(ptr addrspace(1) %global)
 define i1 @kernel3(ptr addrspace(1) %global) {
     %c1 = addrspacecast ptr addrspace(1) %global to ptr addrspace(4)
@@ -35,6 +36,7 @@ define i1 @kernel3(ptr addrspace(1) %global) {
 }
 
 ; Casting a local pointer to a local pointer.
+; The uses of c2 will be replaced with %local.
 ; CHECK: @kernel4(ptr addrspace(3) %local)
 define i1 @kernel4(ptr addrspace(3) %local) {
     %c1 = addrspacecast ptr addrspace(3) %local to ptr addrspace(4)
@@ -45,6 +47,7 @@ define i1 @kernel4(ptr addrspace(3) %local) {
 }
 
 ; Casting a local pointer to a global pointer.
+; The uses of c2 will be replaced with null.
 ; CHECK: @kernel5(ptr addrspace(3) %local)
 define i1 @kernel5(ptr addrspace(3) %local) {
     %c1 = addrspacecast ptr addrspace(3) %local to ptr addrspace(4)
@@ -55,6 +58,7 @@ define i1 @kernel5(ptr addrspace(3) %local) {
 }
 
 ; Casting a local pointer to a private pointer.
+; The uses of c2 will be replaced with null.
 ; CHECK: @kernel6(ptr addrspace(3) %local)
 define i1 @kernel6(ptr addrspace(3) %local) {
     %c1 = addrspacecast ptr addrspace(3) %local to ptr addrspace(4)
@@ -65,6 +69,7 @@ define i1 @kernel6(ptr addrspace(3) %local) {
 }
 
 ; Casting a private pointer to a private pointer.
+; The uses of c2 will be replaced with %private.
 ; CHECK: @kernel7(ptr %private)
 define i1 @kernel7(ptr %private) {
     %c1 = addrspacecast ptr %private to ptr addrspace(4)
@@ -75,6 +80,7 @@ define i1 @kernel7(ptr %private) {
 }
 
 ; Casting a private pointer to a global pointer.
+; The uses of c2 will be replaced with null.
 ; CHECK: @kernel8(ptr %private)
 define i1 @kernel8(ptr %private) {
     %c1 = addrspacecast ptr %private to ptr addrspace(4)
@@ -85,6 +91,7 @@ define i1 @kernel8(ptr %private) {
 }
 
 ; Casting a private pointer to a local pointer.
+; The uses of c2 will be replaced with null.
 ; CHECK: @kernel9(ptr %private)
 define i1 @kernel9(ptr %private) {
     %c1 = addrspacecast ptr %private to ptr addrspace(4)



More information about the llvm-commits mailing list