https://github.com/aobolensk updated 
https://github.com/llvm/llvm-project/pull/228559

>From 33f1e50a78020ddd948afe954eaf9842057f7e33 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <[email protected]>
Date: Fri, 2 Oct 2026 21:30:41 +0200
Subject: [PATCH 1/2] [CIR][SPIR-V] Emit non-generic null pointers via generic
 null cast

Match classic codegen so targets with nonzero null in non-generic address 
spaces can lower null correctly.
---
 clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp |  5 ++
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp   | 10 ++-
 clang/lib/CIR/CodeGen/CIRGenModule.h         |  4 +
 clang/lib/CIR/CodeGen/TargetInfo.cpp         |  7 ++
 clang/lib/CIR/CodeGen/TargetInfo.h           |  5 ++
 clang/lib/CIR/CodeGen/Targets/SPIRV.cpp      | 31 +++++++
 clang/test/CIR/CodeGenOpenCL/null-pointer.cl | 93 ++++++++++++++++++++
 7 files changed, 151 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CIR/CodeGenOpenCL/null-pointer.cl

diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index 5ca19bab96119..d749c85462064 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -1597,6 +1597,11 @@ mlir::Value CIRGenModule::emitNullConstant(QualType t, 
mlir::Location loc) {
   return builder.getConstant(loc, emitNullConstantAttr(t));
 }
 
+mlir::Value CIRGenModule::getNullPointer(cir::PointerType ptrTy, QualType qt,
+                                         mlir::Location loc) {
+  return getTargetCIRGenInfo().getNullPointer(*this, ptrTy, qt, loc);
+}
+
 mlir::TypedAttr CIRGenModule::emitNullConstantAttr(QualType t) {
   if (t->getAs<PointerType>())
     return builder.getConstNullPtrAttr(getTypes().convertTypeForMem(t));
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 6cae1200e6e23..ec13233dd217e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -2645,8 +2645,9 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr 
*ce) {
       // eliminate the useless instructions emitted during translating E.
       if (result.HasSideEffects)
         Visit(subExpr);
-      return cgf.cgm.emitNullConstant(destTy,
-                                      cgf.getLoc(subExpr->getExprLoc()));
+      return cgf.cgm.getNullPointer(
+          mlir::cast<cir::PointerType>(convertType(destTy)), destTy,
+          cgf.getLoc(subExpr->getExprLoc()));
     }
     return cgf.performAddrSpaceCast(Visit(subExpr), convertType(destTy));
   }
@@ -2716,8 +2717,9 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr 
*ce) {
 
     // Note that DestTy is used as the MLIR type instead of a custom
     // nullptr type.
-    mlir::Type ty = cgf.convertType(destTy);
-    return builder.getNullPtr(ty, cgf.getLoc(subExpr->getExprLoc()));
+    auto ty = mlir::cast<cir::PointerType>(cgf.convertType(destTy));
+    return cgf.cgm.getNullPointer(ty, destTy,
+                                  cgf.getLoc(subExpr->getExprLoc()));
   }
 
   case CK_NullToMemberPointer: {
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h 
b/clang/lib/CIR/CodeGen/CIRGenModule.h
index efe0e5525dc1d..d17dcb472f307 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -731,6 +731,10 @@ class CIRGenModule : public CIRGenTypeCache {
 
   mlir::TypedAttr emitNullConstantAttr(QualType t);
 
+  /// Get target specific null pointer.
+  mlir::Value getNullPointer(cir::PointerType ptrTy, QualType qt,
+                             mlir::Location loc);
+
   /// Return a null constant appropriate for zero-initializing a base class 
with
   /// the given type. This is usually, but not always, an LLVM null constant.
   mlir::TypedAttr emitNullConstantForBase(const CXXRecordDecl *record);
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.cpp 
b/clang/lib/CIR/CodeGen/TargetInfo.cpp
index cede4941b4473..7a41cbeecc7e7 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CIR/CodeGen/TargetInfo.cpp
@@ -214,6 +214,13 @@ cir::CallingConv 
TargetCIRGenInfo::getDeviceKernelCallingConv() const {
   return cir::CallingConv::C;
 }
 
+mlir::Value TargetCIRGenInfo::getNullPointer(CIRGenModule &cgm,
+                                             cir::PointerType ptrTy,
+                                             QualType qt,
+                                             mlir::Location loc) const {
+  return cgm.getBuilder().getNullPtr(ptrTy, loc);
+}
+
 clang::LangAS
 TargetCIRGenInfo::getGlobalVarAddressSpace(CIRGenModule &cgm,
                                            const clang::VarDecl *d) const {
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.h 
b/clang/lib/CIR/CodeGen/TargetInfo.h
index 5f380026e025d..5313a27c5e2f9 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.h
+++ b/clang/lib/CIR/CodeGen/TargetInfo.h
@@ -80,6 +80,11 @@ class TargetCIRGenInfo {
                                           cir::LangAddressSpace::Default);
   }
 
+  /// Get the CIR value of a null pointer of type \p ptrTy, where \p qt is the
+  /// source pointer type.
+  virtual mlir::Value getNullPointer(CIRGenModule &cgm, cir::PointerType ptrTy,
+                                     QualType qt, mlir::Location loc) const;
+
   virtual mlir::Type getCUDADeviceBuiltinSurfaceDeviceType() const {
     return nullptr;
   }
diff --git a/clang/lib/CIR/CodeGen/Targets/SPIRV.cpp 
b/clang/lib/CIR/CodeGen/Targets/SPIRV.cpp
index 4d19f122799e3..8129f0ddb752e 100644
--- a/clang/lib/CIR/CodeGen/Targets/SPIRV.cpp
+++ b/clang/lib/CIR/CodeGen/Targets/SPIRV.cpp
@@ -54,10 +54,41 @@ class CommonSPIRTargetCIRGenInfo : public TargetCIRGenInfo {
       ft = getABIInfo().cgt.getASTContext().adjustFunctionType(
           ft, ft->getExtInfo().withCallingConv(CC_DeviceKernel));
   }
+
+  mlir::Value getNullPointer(CIRGenModule &cgm, cir::PointerType ptrTy,
+                             QualType qt, mlir::Location loc) const override;
 };
 
 } // namespace
 
+// The bit pattern of null in non-generic AS is unspecified for SPIR(-V), so
+// materialize it via an address space cast from null in generic AS.
+mlir::Value
+CommonSPIRTargetCIRGenInfo::getNullPointer(CIRGenModule &cgm,
+                                           cir::PointerType ptrTy, QualType qt,
+                                           mlir::Location loc) const {
+  LangAS as = qt->getUnqualifiedDesugaredType()->isNullPtrType()
+                  ? LangAS::Default
+                  : qt->getPointeeType().getAddressSpace();
+  unsigned asAsInt = static_cast<unsigned>(as);
+  unsigned firstTargetASAsInt =
+      static_cast<unsigned>(LangAS::FirstTargetAddressSpace);
+  unsigned codeSectionINTELAS = firstTargetASAsInt + 9;
+  // As per SPV_INTEL_function_pointers, it is illegal to addrspacecast
+  // function pointers to/from the generic AS.
+  bool isFunctionPtrAS =
+      cgm.getTriple().isSPIRV() && asAsInt == codeSectionINTELAS;
+  CIRGenBuilderTy &builder = cgm.getBuilder();
+  if (as == LangAS::Default || as == LangAS::opencl_generic ||
+      as == LangAS::opencl_constant || isFunctionPtrAS)
+    return builder.getNullPtr(ptrTy, loc);
+
+  cir::PointerType genericPtrTy =
+      builder.getPointerTo(ptrTy.getPointee(), LangAS::opencl_generic);
+  return builder.createAddrSpaceCast(loc, builder.getNullPtr(genericPtrTy, 
loc),
+                                     ptrTy);
+}
+
 std::unique_ptr<TargetCIRGenInfo>
 clang::CIRGen::createCommonSPIRTargetCIRGenInfo(CIRGenTypes &cgt) {
   return std::make_unique<CommonSPIRTargetCIRGenInfo>(cgt);
diff --git a/clang/test/CIR/CodeGenOpenCL/null-pointer.cl 
b/clang/test/CIR/CodeGenOpenCL/null-pointer.cl
new file mode 100644
index 0000000000000..ed2769727e1b8
--- /dev/null
+++ b/clang/test/CIR/CodeGenOpenCL/null-pointer.cl
@@ -0,0 +1,93 @@
+// RUN: %clang_cc1 -triple spirv64-unknown-unknown -cl-std=CL2.0 -fclangir \
+// RUN:   -emit-cir -mmlir --mlir-print-ir-before=cir-target-lowering \
+// RUN:   %s -o %t.cir 2> %t.pre.cir
+// RUN: FileCheck %s --check-prefix=CIR --input-file=%t.pre.cir
+// RUN: %clang_cc1 -triple spirv64-unknown-unknown -cl-std=CL2.0 -fclangir \
+// RUN:   -emit-llvm -O0 %s -o %t-cir.ll
+// RUN: FileCheck %s --check-prefix=LLVM --input-file=%t-cir.ll
+// RUN: %clang_cc1 -triple spirv64-unknown-unknown -cl-std=CL2.0 \
+// RUN:   -emit-llvm -O0 %s -o %t.ll
+// RUN: FileCheck %s --check-prefix=LLVM --input-file=%t.ll
+
+#define NULL ((void *)0)
+
+void test_storage(private char **arg_private, local char **arg_local,
+                  global char **arg_global, constant char **arg_constant,
+                  generic char **arg_generic) {
+  *arg_private = 0;
+  *arg_local = 0;
+  *arg_global = 0;
+  *arg_constant = 0;
+  *arg_generic = 0;
+}
+
+// CIR-LABEL: cir.func {{.*}}@test_storage
+// CIR: %[[N:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!s8i, 
lang_address_space(offload_generic)>
+// CIR: cir.cast address_space %[[N]] : !cir.ptr<!s8i, 
lang_address_space(offload_generic)> -> !cir.ptr<!s8i, 
lang_address_space(offload_private)>
+// CIR: %[[N:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!s8i, 
lang_address_space(offload_generic)>
+// CIR: cir.cast address_space %[[N]] : !cir.ptr<!s8i, 
lang_address_space(offload_generic)> -> !cir.ptr<!s8i, 
lang_address_space(offload_local)>
+// CIR: %[[N:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!s8i, 
lang_address_space(offload_generic)>
+// CIR: cir.cast address_space %[[N]] : !cir.ptr<!s8i, 
lang_address_space(offload_generic)> -> !cir.ptr<!s8i, 
lang_address_space(offload_global)>
+// CIR: cir.const #cir.ptr<null> : !cir.ptr<!s8i, 
lang_address_space(offload_constant)>
+// CIR-NOT: cir.cast address_space
+// CIR: cir.const #cir.ptr<null> : !cir.ptr<!s8i, 
lang_address_space(offload_generic)>
+// CIR-NOT: cir.cast address_space
+// CIR: cir.return
+
+// LLVM-LABEL: define {{.*}}void @test_storage
+// LLVM: store ptr addrspacecast (ptr addrspace(4) null to ptr), ptr 
addrspace(4) %{{.*}}
+// LLVM: store ptr addrspace(3) addrspacecast (ptr addrspace(4) null to ptr 
addrspace(3)), ptr addrspace(4) %{{.*}}
+// LLVM: store ptr addrspace(1) addrspacecast (ptr addrspace(4) null to ptr 
addrspace(1)), ptr addrspace(4) %{{.*}}
+// LLVM: store ptr addrspace(2) null, ptr addrspace(4) %{{.*}}
+// LLVM: store ptr addrspace(4) null, ptr addrspace(4) %{{.*}}
+
+void test_init(void) {
+  private char *p1 = 0;
+  local char *p2 = (local char *)0;
+  global char *p3 = NULL;
+  constant char *p4 = 0;
+  generic char *p5 = NULL;
+}
+
+// LLVM-LABEL: define {{.*}}void @test_init
+// LLVM: store ptr addrspacecast (ptr addrspace(4) null to ptr), ptr %{{.*}}
+// LLVM: store ptr addrspace(3) addrspacecast (ptr addrspace(4) null to ptr 
addrspace(3)), ptr %{{.*}}
+// LLVM: store ptr addrspace(1) addrspacecast (ptr addrspace(4) null to ptr 
addrspace(1)), ptr %{{.*}}
+// LLVM: store ptr addrspace(2) null, ptr %{{.*}}
+// LLVM: store ptr addrspace(4) null, ptr %{{.*}}
+
+private int *test_return_private(void) { return 0; }
+// LLVM-LABEL: define {{.*}}ptr @test_return_private
+// LLVM: ptr addrspacecast (ptr addrspace(4) null to ptr)
+
+local int *test_return_local(void) { return (local int *)0; }
+// LLVM-LABEL: define {{.*}}ptr addrspace(3) @test_return_local
+// LLVM: ptr addrspace(3) addrspacecast (ptr addrspace(4) null to ptr 
addrspace(3))
+
+global int *test_return_global(void) { return NULL; }
+// LLVM-LABEL: define {{.*}}ptr addrspace(1) @test_return_global
+// LLVM: ptr addrspace(1) addrspacecast (ptr addrspace(4) null to ptr 
addrspace(1))
+
+constant int *test_return_constant(void) { return 0; }
+// LLVM-LABEL: define {{.*}}ptr addrspace(2) @test_return_constant
+// LLVM: ptr addrspace(2) null
+
+generic int *test_return_generic(void) { return 0; }
+// LLVM-LABEL: define {{.*}}ptr addrspace(4) @test_return_generic
+// LLVM: ptr addrspace(4) null
+
+local int *test_return_as_conversion(void) {
+  return (local int *)(generic int *)(global int *)0;
+}
+// CIR-LABEL: cir.func {{.*}}@test_return_as_conversion
+// CIR: %[[N:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, 
lang_address_space(offload_generic)>
+// CIR: cir.cast address_space %[[N]] : !cir.ptr<!s32i, 
lang_address_space(offload_generic)> -> !cir.ptr<!s32i, 
lang_address_space(offload_local)>
+
+// LLVM-LABEL: define {{.*}}ptr addrspace(3) @test_return_as_conversion
+// LLVM: ptr addrspace(3) addrspacecast (ptr addrspace(4) null to ptr 
addrspace(3))
+
+// SPV_INTEL_function_pointers forbids casting function pointers to generic.
+typedef __attribute__((address_space(9))) void *FnPtrTy;
+FnPtrTy test_return_fnptr(void) { return 0; }
+// LLVM-LABEL: define {{.*}}ptr addrspace(9) @test_return_fnptr
+// LLVM: ptr addrspace(9) null

>From 7c3a55a51a40ffebb6431796b3295aa4abb4b993 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <[email protected]>
Date: Mon, 5 Oct 2026 19:15:31 +0200
Subject: [PATCH 2/2] comments

---
 clang/include/clang/CodeGenUtils/CodeGenUtils.h |  5 +++++
 clang/lib/CIR/CodeGen/Targets/SPIRV.cpp         | 15 ++-------------
 clang/lib/CodeGen/Targets/SPIR.cpp              | 15 ++-------------
 clang/lib/CodeGenUtils/CodeGenUtils.cpp         | 15 +++++++++++++++
 4 files changed, 24 insertions(+), 26 deletions(-)

diff --git a/clang/include/clang/CodeGenUtils/CodeGenUtils.h 
b/clang/include/clang/CodeGenUtils/CodeGenUtils.h
index b2cc416330eb3..8d77264e2b4ef 100644
--- a/clang/include/clang/CodeGenUtils/CodeGenUtils.h
+++ b/clang/include/clang/CodeGenUtils/CodeGenUtils.h
@@ -54,6 +54,11 @@ LangAS getGlobalConstantAddressSpace(const LangOptions 
&LangOpts,
 
 bool isInitializerOfDynamicClass(const CXXCtorInitializer *BaseInit);
 
+/// Return true if a SPIR(-V) null pointer of type \p QT must be materialized
+/// as an addrspacecast from a generic null pointer, as the null bit pattern in
+/// non-generic address spaces is unspecified.
+bool spirNullPointerNeedsGenericCast(QualType QT, const llvm::Triple &Triple);
+
 /// Check that a call to a target-specific builtin has the required target
 /// features enabled in the caller, emitting an error diagnostic if not.
 /// \p caller is the FunctionDecl of the enclosing function (may be null).
diff --git a/clang/lib/CIR/CodeGen/Targets/SPIRV.cpp 
b/clang/lib/CIR/CodeGen/Targets/SPIRV.cpp
index 8129f0ddb752e..064aee64aaa4e 100644
--- a/clang/lib/CIR/CodeGen/Targets/SPIRV.cpp
+++ b/clang/lib/CIR/CodeGen/Targets/SPIRV.cpp
@@ -16,6 +16,7 @@
 #include "clang/AST/Attr.h"
 #include "clang/AST/Decl.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CodeGenUtils/CodeGenUtils.h"
 
 using namespace clang;
 using namespace clang::CIRGen;
@@ -67,20 +68,8 @@ mlir::Value
 CommonSPIRTargetCIRGenInfo::getNullPointer(CIRGenModule &cgm,
                                            cir::PointerType ptrTy, QualType qt,
                                            mlir::Location loc) const {
-  LangAS as = qt->getUnqualifiedDesugaredType()->isNullPtrType()
-                  ? LangAS::Default
-                  : qt->getPointeeType().getAddressSpace();
-  unsigned asAsInt = static_cast<unsigned>(as);
-  unsigned firstTargetASAsInt =
-      static_cast<unsigned>(LangAS::FirstTargetAddressSpace);
-  unsigned codeSectionINTELAS = firstTargetASAsInt + 9;
-  // As per SPV_INTEL_function_pointers, it is illegal to addrspacecast
-  // function pointers to/from the generic AS.
-  bool isFunctionPtrAS =
-      cgm.getTriple().isSPIRV() && asAsInt == codeSectionINTELAS;
   CIRGenBuilderTy &builder = cgm.getBuilder();
-  if (as == LangAS::Default || as == LangAS::opencl_generic ||
-      as == LangAS::opencl_constant || isFunctionPtrAS)
+  if (!CodeGenUtils::spirNullPointerNeedsGenericCast(qt, cgm.getTriple()))
     return builder.getNullPtr(ptrTy, loc);
 
   cir::PointerType genericPtrTy =
diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp 
b/clang/lib/CodeGen/Targets/SPIR.cpp
index 4a7c84ec26b2b..244dd10d1f7ca 100644
--- a/clang/lib/CodeGen/Targets/SPIR.cpp
+++ b/clang/lib/CodeGen/Targets/SPIR.cpp
@@ -11,6 +11,7 @@
 #include "TargetInfo.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/Basic/LangOptions.h"
+#include "clang/CodeGenUtils/CodeGenUtils.h"
 #include "llvm/IR/DerivedTypes.h"
 #include "llvm/IR/LLVMContext.h"
 
@@ -491,19 +492,7 @@ llvm::Constant *
 CommonSPIRTargetCodeGenInfo::getNullPointer(const CodeGen::CodeGenModule &CGM,
                                             llvm::PointerType *PT,
                                             QualType QT) const {
-  LangAS AS = QT->getUnqualifiedDesugaredType()->isNullPtrType()
-                  ? LangAS::Default
-                  : QT->getPointeeType().getAddressSpace();
-  unsigned ASAsInt = static_cast<unsigned>(AS);
-  unsigned FirstTargetASAsInt =
-      static_cast<unsigned>(LangAS::FirstTargetAddressSpace);
-  unsigned CodeSectionINTELAS = FirstTargetASAsInt + 9;
-  // As per SPV_INTEL_function_pointers, it is illegal to addrspacecast
-  // function pointers to/from the generic AS.
-  bool IsFunctionPtrAS =
-      CGM.getTriple().isSPIRV() && ASAsInt == CodeSectionINTELAS;
-  if (AS == LangAS::Default || AS == LangAS::opencl_generic ||
-      AS == LangAS::opencl_constant || IsFunctionPtrAS)
+  if (!CodeGenUtils::spirNullPointerNeedsGenericCast(QT, CGM.getTriple()))
     return llvm::ConstantPointerNull::get(PT);
 
   auto &Ctx = CGM.getContext();
diff --git a/clang/lib/CodeGenUtils/CodeGenUtils.cpp 
b/clang/lib/CodeGenUtils/CodeGenUtils.cpp
index 11afe31ae89b0..fc1ceb006ec05 100644
--- a/clang/lib/CodeGenUtils/CodeGenUtils.cpp
+++ b/clang/lib/CodeGenUtils/CodeGenUtils.cpp
@@ -134,6 +134,21 @@ LangAS getGlobalConstantAddressSpace(const LangOptions 
&LangOpts,
   return LangAS::Default;
 }
 
+bool spirNullPointerNeedsGenericCast(QualType QT, const llvm::Triple &Triple) {
+  // LLVM address space of the SPIR-V CodeSectionINTEL storage class.
+  constexpr unsigned SPIRVCodeSectionINTELAddrSpace = 9;
+  LangAS AS = QT->getUnqualifiedDesugaredType()->isNullPtrType()
+                  ? LangAS::Default
+                  : QT->getPointeeType().getAddressSpace();
+  if (AS == LangAS::Default || AS == LangAS::opencl_generic ||
+      AS == LangAS::opencl_constant)
+    return false;
+  // As per SPV_INTEL_function_pointers, it is illegal to addrspacecast
+  // function pointers to/from the generic AS.
+  return !(Triple.isSPIRV() && isTargetAddressSpace(AS) &&
+           toTargetAddressSpace(AS) == SPIRVCodeSectionINTELAddrSpace);
+}
+
 bool isInitializerOfDynamicClass(const CXXCtorInitializer *BaseInit) {
   const Type *BaseType = BaseInit->getBaseClass();
   return BaseType->castAsCXXRecordDecl()->isDynamicClass();

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to