Skip to content

[clang][SPIRV] Add builtin for OpGenericCastToPtrExplicit and its SPIR-V friendly binding #137805

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

Naghasan
Copy link
Contributor

@Naghasan Naghasan commented Apr 29, 2025

The patch introduce __builtin_spirv_generic_cast_to_ptr_explicit which is lowered to the llvm.spv.generic.cast.to.ptr.explicit intrinsic.

The SPIR-V builtins are now split into 3 differents file: BuiltinsSPIRVCore.td,
BuiltinsSPIRVVK.td for Vulkan specific builtins, BuiltinsSPIRVCL.td for OpenCL specific builtins
and BuiltinsSPIRVCommon.td for common ones.

The patch also introduces a new header defining its SPIR-V friendly equivalent (__spirv_GenericCastToPtrExplicit_ToGlobal, __spirv_GenericCastToPtrExplicit_ToLocal and __spirv_GenericCastToPtrExplicit_ToPrivate). The functions are declared as aliases to the new builtin allowing C-like languages to have a definition to rely on as well as gaining proper front-end diagnostics.

The motivation for the header is to provide a stable binding for applications or library (such as SYCL) and allows non SPIR-V targets to provide an implementation (via libclc or similar to how it is done for gpuintrin.h).

@llvmbot llvmbot added clang Clang issues not falling into any other category backend:X86 clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:headers Headers provided by Clang, e.g. for intrinsics clang:codegen IR generation bugs: mangling, exceptions, etc. backend:SPIR-V labels Apr 29, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 29, 2025

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

@llvm/pr-subscribers-clang

Author: Victor Lomuller (Naghasan)

Changes

The patch introduce __builtin_spirv_generic_cast_to_ptr_explicit which is lowered to the llvm.spv.generic.cast.to.ptr.explicit intrinsic.

The patch also introduces a new header defining its SPIR-V friendly equivalent (__spirv_GenericCastToPtrExplicit_ToGlobal, __spirv_GenericCastToPtrExplicit_ToLocal and __spirv_GenericCastToPtrExplicit_ToPrivate). The functions are declared as aliases to the new builtin allowing C-like languages to have a definition to rely on as well as gaining proper front-end diagnostics.

The motivation for the header is to provide a stable binding for applications or library (such as SYCL) and allows non SPIR-V targets to provide an implementation (via libclc or similar to how it is done for gpuintrin.h).


Patch is 28.23 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/137805.diff

15 Files Affected:

  • (modified) clang/include/clang/Basic/BuiltinsSPIRV.td (+9)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+9-1)
  • (modified) clang/lib/AST/ASTContext.cpp (+5)
  • (modified) clang/lib/Basic/Targets/SPIR.cpp (+4-2)
  • (modified) clang/lib/Basic/Targets/SPIR.h (+1-5)
  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+3-3)
  • (modified) clang/lib/CodeGen/TargetBuiltins/SPIR.cpp (+14)
  • (modified) clang/lib/Headers/CMakeLists.txt (+16)
  • (added) clang/lib/Headers/__clang_spirv_builtins.h (+176)
  • (modified) clang/lib/Sema/SemaChecking.cpp (+5-1)
  • (modified) clang/lib/Sema/SemaDeclAttr.cpp (+3-1)
  • (modified) clang/lib/Sema/SemaSPIRV.cpp (+105)
  • (added) clang/test/CodeGenSPIRV/Builtins/generic_cast_to_ptr_explicit.c (+33)
  • (added) clang/test/Headers/spirv_functions.cpp (+25)
  • (added) clang/test/SemaSPIRV/BuiltIns/generic_cast_to_ptr_explicit.c (+25)
diff --git a/clang/include/clang/Basic/BuiltinsSPIRV.td b/clang/include/clang/Basic/BuiltinsSPIRV.td
index cc0c2f960f8d2..bbb2abba2e256 100644
--- a/clang/include/clang/Basic/BuiltinsSPIRV.td
+++ b/clang/include/clang/Basic/BuiltinsSPIRV.td
@@ -8,6 +8,12 @@
 
 include "clang/Basic/BuiltinsBase.td"
 
+class SPIRVBuiltin<string prototype, list<Attribute> Attr> : Builtin {
+  let Spellings = ["__builtin_spirv_"#NAME];
+  let Prototype = prototype;
+  let Attributes = !listconcat([NoThrow], Attr);
+}
+
 def SPIRVDistance : Builtin {
   let Spellings = ["__builtin_spirv_distance"];
   let Attributes = [NoThrow, Const];
@@ -37,3 +43,6 @@ def SPIRVFaceForward : Builtin {
   let Attributes = [NoThrow, Const, CustomTypeChecking];
   let Prototype = "void(...)";
 }
+
+def generic_cast_to_ptr_explicit
+    : SPIRVBuiltin<"void*(void*, int)", [NoThrow, Const, CustomTypeChecking]>;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 4c96142e28134..8f088d4d0d0f8 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4609,7 +4609,7 @@ def err_attribute_preferred_name_arg_invalid : Error<
   "argument %0 to 'preferred_name' attribute is not a typedef for "
   "a specialization of %1">;
 def err_attribute_builtin_alias : Error<
-  "%0 attribute can only be applied to a ARM, HLSL or RISC-V builtin">;
+  "%0 attribute can only be applied to a ARM, HLSL, SPIR-V or RISC-V builtin">;
 
 // called-once attribute diagnostics.
 def err_called_once_attribute_wrong_type : Error<
@@ -12740,6 +12740,14 @@ def err_bit_int_bad_size : Error<"%select{signed|unsigned}0 _BitInt must "
 def err_bit_int_max_size : Error<"%select{signed|unsigned}0 _BitInt of bit "
                                  "sizes greater than %1 not supported">;
 
+// SPIR-V builtins diagnostics
+def err_spirv_builtin_generic_cast_invalid_arg : Error<
+  "expecting a pointer argument to the generic address space">;
+def err_spirv_enum_not_int : Error<
+   "%0{storage class} argument for SPIR-V builtin is not a 32-bits integer">;
+def err_spirv_enum_not_valid : Error<
+   "invalid value for %select{storage class}0 argument">;
+
 // errors of expect.with.probability
 def err_probability_not_constant_float : Error<
    "probability argument to __builtin_expect_with_probability must be constant "
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index c95e733f30494..51438c22f52fe 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -10054,6 +10054,11 @@ bool ASTContext::canBuiltinBeRedeclared(const FunctionDecl *FD) const {
   if (LangOpts.HLSL && FD->getBuiltinID() != Builtin::NotBuiltin &&
       BuiltinInfo.hasCustomTypechecking(FD->getBuiltinID()))
     return true;
+  // Allow redecl custom type checking builtin for SPIR-V.
+  if (getTargetInfo().getTriple().isSPIROrSPIRV() &&
+      BuiltinInfo.isTSBuiltin(FD->getBuiltinID()) &&
+      BuiltinInfo.hasCustomTypechecking(FD->getBuiltinID()))
+    return true;
   return BuiltinInfo.canBeRedeclared(FD->getBuiltinID());
 }
 
diff --git a/clang/lib/Basic/Targets/SPIR.cpp b/clang/lib/Basic/Targets/SPIR.cpp
index 5b5f47f9647a2..d742ae8ff044d 100644
--- a/clang/lib/Basic/Targets/SPIR.cpp
+++ b/clang/lib/Basic/Targets/SPIR.cpp
@@ -35,8 +35,10 @@ static constexpr Builtin::Info BuiltinInfos[] = {
 static_assert(std::size(BuiltinInfos) == NumBuiltins);
 
 llvm::SmallVector<Builtin::InfosShard>
-SPIRVTargetInfo::getTargetBuiltins() const {
-  return {{&BuiltinStrings, BuiltinInfos}};
+BaseSPIRTargetInfo::getTargetBuiltins() const {
+  if (getTriple().isSPIRV())
+    return {{&BuiltinStrings, BuiltinInfos}};
+  return {};
 }
 
 void SPIRTargetInfo::getTargetDefines(const LangOptions &Opts,
diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h
index bf249e271a870..c6c31884db34d 100644
--- a/clang/lib/Basic/Targets/SPIR.h
+++ b/clang/lib/Basic/Targets/SPIR.h
@@ -164,9 +164,7 @@ class LLVM_LIBRARY_VISIBILITY BaseSPIRTargetInfo : public TargetInfo {
   // memcpy as per section 3 of the SPIR spec.
   bool useFP16ConversionIntrinsics() const override { return false; }
 
-  llvm::SmallVector<Builtin::InfosShard> getTargetBuiltins() const override {
-    return {};
-  }
+  llvm::SmallVector<Builtin::InfosShard> getTargetBuiltins() const override;
 
   std::string_view getClobbers() const override { return ""; }
 
@@ -321,8 +319,6 @@ class LLVM_LIBRARY_VISIBILITY SPIRVTargetInfo : public BaseSPIRVTargetInfo {
                     "v256:256-v512:512-v1024:1024-n8:16:32:64-G10");
   }
 
-  llvm::SmallVector<Builtin::InfosShard> getTargetBuiltins() const override;
-
   void getTargetDefines(const LangOptions &Opts,
                         MacroBuilder &Builder) const override;
 };
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 1e4e055e04afd..e526e0b62cf16 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -97,10 +97,10 @@ static Value *EmitTargetArchBuiltinExpr(CodeGenFunction *CGF,
   case llvm::Triple::riscv64:
     return CGF->EmitRISCVBuiltinExpr(BuiltinID, E, ReturnValue);
   case llvm::Triple::spirv:
-    return CGF->EmitSPIRVBuiltinExpr(BuiltinID, E);
+  case llvm::Triple::spirv32:
   case llvm::Triple::spirv64:
-    if (CGF->getTarget().getTriple().getOS() != llvm::Triple::OSType::AMDHSA)
-      return nullptr;
+  if (CGF->getTarget().getTriple().getOS() != llvm::Triple::OSType::AMDHSA)
+      return CGF->EmitSPIRVBuiltinExpr(BuiltinID, E);
     return CGF->EmitAMDGPUBuiltinExpr(BuiltinID, E);
   default:
     return nullptr;
diff --git a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
index 26f8eb1fd07f8..0687485cd3f80 100644
--- a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
@@ -83,6 +83,20 @@ Value *CodeGenFunction::EmitSPIRVBuiltinExpr(unsigned BuiltinID,
         /*ReturnType=*/N->getType(), Intrinsic::spv_faceforward,
         ArrayRef<Value *>{N, I, Ng}, /*FMFSource=*/nullptr, "spv.faceforward");
   }
+  case SPIRV::BI__builtin_spirv_generic_cast_to_ptr_explicit: {
+    Value *Ptr = EmitScalarExpr(E->getArg(0));
+    assert(E->getArg(0)->getType()->hasPointerRepresentation() &&
+           E->getArg(1)->getType()->hasIntegerRepresentation() &&
+           "GenericCastToPtrExplicit takes a pointer and an int");
+    llvm::Type *Res = getTypes().ConvertType(E->getType());
+    assert(Res->isPointerTy() &&
+           "GenericCastToPtrExplicit doesn't return a pointer");
+    llvm::CallInst *Call = Builder.CreateIntrinsic(
+        /*ReturnType=*/Res, Intrinsic::spv_generic_cast_to_ptr_explicit,
+        ArrayRef<Value *>{Ptr}, nullptr, "spv.generic_cast");
+    Call->addRetAttr(llvm::Attribute::AttrKind::NoUndef);
+    return Call;
+  }
   }
   return nullptr;
 }
diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt
index acf49e40c447e..556b076abbfbf 100644
--- a/clang/lib/Headers/CMakeLists.txt
+++ b/clang/lib/Headers/CMakeLists.txt
@@ -129,6 +129,10 @@ set(riscv_files
   sifive_vector.h
   )
 
+set(spirv_files
+  __clang_spirv_builtins.h
+  )
+
 set(systemz_files
   s390intrin.h
   vecintrin.h
@@ -316,6 +320,7 @@ set(files
   ${ppc_files}
   ${ppc_htm_files}
   ${riscv_files}
+  ${spirv_files}
   ${systemz_files}
   ${ve_files}
   ${x86_files}
@@ -526,6 +531,7 @@ add_dependencies("clang-resource-headers"
                  "ppc-resource-headers"
                  "ppc-htm-resource-headers"
                  "riscv-resource-headers"
+                 "spirv-resource-headers"
                  "systemz-resource-headers"
                  "ve-resource-headers"
                  "webassembly-resource-headers"
@@ -559,6 +565,7 @@ add_header_target("gpu-resource-headers" "${gpu_files}")
 
 # Other header groupings
 add_header_target("hlsl-resource-headers" ${hlsl_files})
+add_header_target("spirv-resource-headers" ${spirv_files})
 add_header_target("opencl-resource-headers" ${opencl_files})
 add_header_target("llvm-libc-resource-headers" ${llvm_libc_wrapper_files})
 add_header_target("openmp-resource-headers" ${openmp_wrapper_files})
@@ -764,6 +771,12 @@ install(
   ${EXCLUDE_HLSL}
   COMPONENT hlsl-resource-headers)
 
+install(
+  FILES ${spirv_files}
+  DESTINATION ${header_install_dir}
+  EXCLUDE_FROM_ALL
+  COMPONENT spirv-resource-headers)
+
 install(
   FILES ${opencl_files}
   DESTINATION ${header_install_dir}
@@ -833,6 +846,9 @@ if (NOT LLVM_ENABLE_IDE)
   add_llvm_install_targets(install-riscv-resource-headers
                            DEPENDS riscv-resource-headers
                            COMPONENT riscv-resource-headers)
+  add_llvm_install_targets(install-spirv-resource-headers
+                           DEPENDS spirv-resource-headers
+                           COMPONENT spirv-resource-headers)
   add_llvm_install_targets(install-systemz-resource-headers
                            DEPENDS systemz-resource-headers
                            COMPONENT systemz-resource-headers)
diff --git a/clang/lib/Headers/__clang_spirv_builtins.h b/clang/lib/Headers/__clang_spirv_builtins.h
new file mode 100644
index 0000000000000..0b23fc87b4511
--- /dev/null
+++ b/clang/lib/Headers/__clang_spirv_builtins.h
@@ -0,0 +1,176 @@
+/*===---- spirv_builtin_vars.h - SPIR-V built-in ---------------------------===
+ *
+ * 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
+ *
+ *===-----------------------------------------------------------------------===
+ */
+
+#ifndef __SPIRV_BUILTIN_VARS_H
+#define __SPIRV_BUILTIN_VARS_H
+
+#if __cplusplus >= 201103L
+#define __SPIRV_NOEXCEPT noexcept
+#else
+#define __SPIRV_NOEXCEPT
+#endif
+
+#define __SPIRV_overloadable __attribute__((overloadable))
+#define __SPIRV_convergent __attribute__((convergent))
+#define __SPIRV_inline __attribute__((always_inline))
+
+#define __global __attribute__((opencl_global))
+#define __local __attribute__((opencl_local))
+#define __private __attribute__((opencl_private))
+#define __constant __attribute__((opencl_constant))
+#ifdef __SYCL_DEVICE_ONLY__
+#define __generic
+#else
+#define __generic __attribute__((opencl_generic))
+#endif
+
+// Check if SPIR-V builtins are supported.
+// As the translator doesn't use the LLVM intrinsics (which would be emitted if
+// we use the SPIR-V builtins) we can't rely on the SPIRV32/SPIRV64 etc macros
+// to establish if we can use the builtin alias. We disable builtin altogether
+// if we do not intent to use the backend. So instead of use target macros, rely
+// on a __has_builtin test.
+#if (__has_builtin(__builtin_spirv_generic_cast_to_ptr_explicit))
+#define __SPIRV_BUILTIN_ALIAS(builtin)                                          \
+  __attribute__((clang_builtin_alias(builtin)))
+#else
+#define __SPIRV_BUILTIN_ALIAS(builtin)
+#endif
+
+// OpGenericCastToPtrExplicit
+
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__global void *__spirv_GenericCastToPtrExplicit_ToGlobal(__generic void *,
+                                                         int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__global const void *
+__spirv_GenericCastToPtrExplicit_ToGlobal(__generic const void *,
+                                          int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__global volatile void *
+__spirv_GenericCastToPtrExplicit_ToGlobal(__generic volatile void *,
+                                          int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__global const volatile void *
+__spirv_GenericCastToPtrExplicit_ToGlobal(__generic const volatile void *,
+                                          int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__local void *__spirv_GenericCastToPtrExplicit_ToLocal(__generic void *,
+                                                       int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__local const void *
+__spirv_GenericCastToPtrExplicit_ToLocal(__generic const void *,
+                                         int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__local volatile void *
+__spirv_GenericCastToPtrExplicit_ToLocal(__generic volatile void *,
+                                         int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__local const volatile void *
+__spirv_GenericCastToPtrExplicit_ToLocal(__generic const volatile void *,
+                                         int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__private void *__spirv_GenericCastToPtrExplicit_ToPrivate(__generic void *,
+                                                           int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__private const void *
+__spirv_GenericCastToPtrExplicit_ToPrivate(__generic const void *,
+                                           int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__private volatile void *
+__spirv_GenericCastToPtrExplicit_ToPrivate(__generic volatile void *,
+                                           int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__private const volatile void *
+__spirv_GenericCastToPtrExplicit_ToPrivate(__generic const volatile void *,
+                                           int) __SPIRV_NOEXCEPT;
+
+// OpGenericCastToPtr
+
+static __SPIRV_overloadable __SPIRV_inline __global void *
+__spirv_GenericCastToPtr_ToGlobal(__generic void *p, int) __SPIRV_NOEXCEPT {
+  return (__global void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __global const void *
+__spirv_GenericCastToPtr_ToGlobal(__generic const void *p, int) __SPIRV_NOEXCEPT {
+  return (__global const void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __global volatile void *
+__spirv_GenericCastToPtr_ToGlobal(__generic volatile void *p,
+                                  int) __SPIRV_NOEXCEPT {
+  return (__global volatile void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __global const volatile void *
+__spirv_GenericCastToPtr_ToGlobal(__generic const volatile void *p,
+                                  int) __SPIRV_NOEXCEPT {
+  return (__global const volatile void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __local void *
+__spirv_GenericCastToPtr_ToLocal(__generic void *p, int) __SPIRV_NOEXCEPT {
+  return (__local void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __local const void *
+__spirv_GenericCastToPtr_ToLocal(__generic const void *p, int) __SPIRV_NOEXCEPT {
+  return (__local const void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __local volatile void *
+__spirv_GenericCastToPtr_ToLocal(__generic volatile void *p,
+                                 int) __SPIRV_NOEXCEPT {
+  return (__local volatile void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __local const volatile void *
+__spirv_GenericCastToPtr_ToLocal(__generic const volatile void *p,
+                                 int) __SPIRV_NOEXCEPT {
+  return (__local const volatile void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __private void *
+__spirv_GenericCastToPtr_ToPrivate(__generic void *p, int) __SPIRV_NOEXCEPT {
+  return (__private void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __private const void *
+__spirv_GenericCastToPtr_ToPrivate(__generic const void *p,
+                                   int) __SPIRV_NOEXCEPT {
+  return (__private const void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __private volatile void *
+__spirv_GenericCastToPtr_ToPrivate(__generic volatile void *p,
+                                   int) __SPIRV_NOEXCEPT {
+  return (__private volatile void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __private const volatile void *
+__spirv_GenericCastToPtr_ToPrivate(__generic const volatile void *p,
+                                   int) __SPIRV_NOEXCEPT {
+  return (__private const volatile void *)p;
+}
+
+#undef __SPIRV_overloadable
+#undef __SPIRV_convergent
+#undef __SPIRV_inline
+
+#undef __global
+#undef __local
+#undef __constant
+#undef __generic
+
+#undef __SPIRV_BUILTIN_ALIAS
+#undef __SPIRV_NOEXCEPT
+
+#endif /* __SPIRV_BUILTIN_VARS_H */
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 2811fd3a04377..fb0f2d2c0d57b 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1983,7 +1983,11 @@ bool Sema::CheckTSBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID,
   case llvm::Triple::mips64el:
     return MIPS().CheckMipsBuiltinFunctionCall(TI, BuiltinID, TheCall);
   case llvm::Triple::spirv:
-    return SPIRV().CheckSPIRVBuiltinFunctionCall(BuiltinID, TheCall);
+  case llvm::Triple::spirv32:
+  case llvm::Triple::spirv64:
+    if (TI.getTriple().getOS() != llvm::Triple::OSType::AMDHSA)
+      return SPIRV().CheckSPIRVBuiltinFunctionCall(BuiltinID, TheCall);
+    return false;
   case llvm::Triple::systemz:
     return SystemZ().CheckSystemZBuiltinFunctionCall(BuiltinID, TheCall);
   case llvm::Triple::x86:
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 413999b95b998..28128d21e53cf 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -53,6 +53,7 @@
 #include "clang/Sema/SemaOpenCL.h"
 #include "clang/Sema/SemaOpenMP.h"
 #include "clang/Sema/SemaRISCV.h"
+#include "clang/Sema/SemaSPIRV.h"
 #include "clang/Sema/SemaSYCL.h"
 #include "clang/Sema/SemaSwift.h"
 #include "clang/Sema/SemaWasm.h"
@@ -5837,12 +5838,13 @@ static void handleBuiltinAliasAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   bool IsAArch64 = S.Context.getTargetInfo().getTriple().isAArch64();
   bool IsARM = S.Context.getTargetInfo().getTriple().isARM();
   bool IsRISCV = S.Context.getTargetInfo().getTriple().isRISCV();
+  bool IsSPIRV = S.Context.getTargetInfo().getTriple().isSPIRV();
   bool IsHLSL = S.Context.getLangOpts().HLSL;
   if ((IsAArch64 && !S.ARM().SveAliasValid(BuiltinID, AliasName)) ||
       (IsARM && !S.ARM().MveAliasValid(BuiltinID, AliasName) &&
        !S.ARM().CdeAliasValid(BuiltinID, AliasName)) ||
       (IsRISCV && !S.RISCV().isAliasValid(BuiltinID, AliasName)) ||
-      (!IsAArch64 && !IsARM && !IsRISCV && !IsHLSL)) {
+      (!IsAArch64 && !IsARM && !IsRISCV && !IsHLSL && !IsSPIRV)) {
     S.Diag(AL.getLoc(), diag::err_attribute_builtin_alias) << AL;
     return;
   }
diff --git a/clang/lib/Sema/SemaSPIRV.cpp b/clang/lib/Sema/SemaSPIRV.cpp
index 90888f1417a9d..9282d02bcb8f5 100644
--- a/clang/lib/Sema/SemaSPIRV.cpp
+++ b/clang/lib/Sema/SemaSPIRV.cpp
@@ -12,6 +12,18 @@
 #include "clang/Basic/TargetBuiltins.h"
 #include "clang/Sema/Sema.h"
 
+// SPIR-V enumerants. Enums have only the required entries, see SPIR-V specs for
+// values.
+// FIXME: either use the SPIRV-Headers or generate a custom header using the
+// grammar (like done with MLIR).
+namespace spirv {
+enum class StorageClass : int {
+  Workgroup = 4,
+  CrossWorkgroup = 5,
+  Function = 7
+};
+}
+
 namespace clang {
 
 SemaSPIRV::SemaSPIRV(Sema &S) : SemaBase(S) {}
@@ -33,6 +45,96 @@ static bool CheckAllArgsHaveSameType(Sema *S, CallExpr *TheCall) {
   return false;
 }
 
+static std::optional<int>
+processConstant32BitIntArgument(Sema &SemaRef, CallExpr *Call, int Argument) {
+  ExprResult Arg =
+      SemaRef.DefaultFunctionArrayLvalueConversion(Call->getArg(Argument));
+  if (Arg.isInvalid())
+    return true;
+  Call->setArg(Argument, Arg.get());
+
+  const Expr *IntArg = Arg.get();
+  SmallVector<PartialDiagnosticAt, 8> Notes;
+  Expr::EvalResult Eval;
+  Eval.Diag = &Notes;
+  if ((!IntArg->EvaluateA...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Apr 29, 2025

@llvm/pr-subscribers-clang-codegen

Author: Victor Lomuller (Naghasan)

Changes

The patch introduce __builtin_spirv_generic_cast_to_ptr_explicit which is lowered to the llvm.spv.generic.cast.to.ptr.explicit intrinsic.

The patch also introduces a new header defining its SPIR-V friendly equivalent (__spirv_GenericCastToPtrExplicit_ToGlobal, __spirv_GenericCastToPtrExplicit_ToLocal and __spirv_GenericCastToPtrExplicit_ToPrivate). The functions are declared as aliases to the new builtin allowing C-like languages to have a definition to rely on as well as gaining proper front-end diagnostics.

The motivation for the header is to provide a stable binding for applications or library (such as SYCL) and allows non SPIR-V targets to provide an implementation (via libclc or similar to how it is done for gpuintrin.h).


Patch is 28.23 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/137805.diff

15 Files Affected:

  • (modified) clang/include/clang/Basic/BuiltinsSPIRV.td (+9)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+9-1)
  • (modified) clang/lib/AST/ASTContext.cpp (+5)
  • (modified) clang/lib/Basic/Targets/SPIR.cpp (+4-2)
  • (modified) clang/lib/Basic/Targets/SPIR.h (+1-5)
  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+3-3)
  • (modified) clang/lib/CodeGen/TargetBuiltins/SPIR.cpp (+14)
  • (modified) clang/lib/Headers/CMakeLists.txt (+16)
  • (added) clang/lib/Headers/__clang_spirv_builtins.h (+176)
  • (modified) clang/lib/Sema/SemaChecking.cpp (+5-1)
  • (modified) clang/lib/Sema/SemaDeclAttr.cpp (+3-1)
  • (modified) clang/lib/Sema/SemaSPIRV.cpp (+105)
  • (added) clang/test/CodeGenSPIRV/Builtins/generic_cast_to_ptr_explicit.c (+33)
  • (added) clang/test/Headers/spirv_functions.cpp (+25)
  • (added) clang/test/SemaSPIRV/BuiltIns/generic_cast_to_ptr_explicit.c (+25)
diff --git a/clang/include/clang/Basic/BuiltinsSPIRV.td b/clang/include/clang/Basic/BuiltinsSPIRV.td
index cc0c2f960f8d2..bbb2abba2e256 100644
--- a/clang/include/clang/Basic/BuiltinsSPIRV.td
+++ b/clang/include/clang/Basic/BuiltinsSPIRV.td
@@ -8,6 +8,12 @@
 
 include "clang/Basic/BuiltinsBase.td"
 
+class SPIRVBuiltin<string prototype, list<Attribute> Attr> : Builtin {
+  let Spellings = ["__builtin_spirv_"#NAME];
+  let Prototype = prototype;
+  let Attributes = !listconcat([NoThrow], Attr);
+}
+
 def SPIRVDistance : Builtin {
   let Spellings = ["__builtin_spirv_distance"];
   let Attributes = [NoThrow, Const];
@@ -37,3 +43,6 @@ def SPIRVFaceForward : Builtin {
   let Attributes = [NoThrow, Const, CustomTypeChecking];
   let Prototype = "void(...)";
 }
+
+def generic_cast_to_ptr_explicit
+    : SPIRVBuiltin<"void*(void*, int)", [NoThrow, Const, CustomTypeChecking]>;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 4c96142e28134..8f088d4d0d0f8 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4609,7 +4609,7 @@ def err_attribute_preferred_name_arg_invalid : Error<
   "argument %0 to 'preferred_name' attribute is not a typedef for "
   "a specialization of %1">;
 def err_attribute_builtin_alias : Error<
-  "%0 attribute can only be applied to a ARM, HLSL or RISC-V builtin">;
+  "%0 attribute can only be applied to a ARM, HLSL, SPIR-V or RISC-V builtin">;
 
 // called-once attribute diagnostics.
 def err_called_once_attribute_wrong_type : Error<
@@ -12740,6 +12740,14 @@ def err_bit_int_bad_size : Error<"%select{signed|unsigned}0 _BitInt must "
 def err_bit_int_max_size : Error<"%select{signed|unsigned}0 _BitInt of bit "
                                  "sizes greater than %1 not supported">;
 
+// SPIR-V builtins diagnostics
+def err_spirv_builtin_generic_cast_invalid_arg : Error<
+  "expecting a pointer argument to the generic address space">;
+def err_spirv_enum_not_int : Error<
+   "%0{storage class} argument for SPIR-V builtin is not a 32-bits integer">;
+def err_spirv_enum_not_valid : Error<
+   "invalid value for %select{storage class}0 argument">;
+
 // errors of expect.with.probability
 def err_probability_not_constant_float : Error<
    "probability argument to __builtin_expect_with_probability must be constant "
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index c95e733f30494..51438c22f52fe 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -10054,6 +10054,11 @@ bool ASTContext::canBuiltinBeRedeclared(const FunctionDecl *FD) const {
   if (LangOpts.HLSL && FD->getBuiltinID() != Builtin::NotBuiltin &&
       BuiltinInfo.hasCustomTypechecking(FD->getBuiltinID()))
     return true;
+  // Allow redecl custom type checking builtin for SPIR-V.
+  if (getTargetInfo().getTriple().isSPIROrSPIRV() &&
+      BuiltinInfo.isTSBuiltin(FD->getBuiltinID()) &&
+      BuiltinInfo.hasCustomTypechecking(FD->getBuiltinID()))
+    return true;
   return BuiltinInfo.canBeRedeclared(FD->getBuiltinID());
 }
 
diff --git a/clang/lib/Basic/Targets/SPIR.cpp b/clang/lib/Basic/Targets/SPIR.cpp
index 5b5f47f9647a2..d742ae8ff044d 100644
--- a/clang/lib/Basic/Targets/SPIR.cpp
+++ b/clang/lib/Basic/Targets/SPIR.cpp
@@ -35,8 +35,10 @@ static constexpr Builtin::Info BuiltinInfos[] = {
 static_assert(std::size(BuiltinInfos) == NumBuiltins);
 
 llvm::SmallVector<Builtin::InfosShard>
-SPIRVTargetInfo::getTargetBuiltins() const {
-  return {{&BuiltinStrings, BuiltinInfos}};
+BaseSPIRTargetInfo::getTargetBuiltins() const {
+  if (getTriple().isSPIRV())
+    return {{&BuiltinStrings, BuiltinInfos}};
+  return {};
 }
 
 void SPIRTargetInfo::getTargetDefines(const LangOptions &Opts,
diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h
index bf249e271a870..c6c31884db34d 100644
--- a/clang/lib/Basic/Targets/SPIR.h
+++ b/clang/lib/Basic/Targets/SPIR.h
@@ -164,9 +164,7 @@ class LLVM_LIBRARY_VISIBILITY BaseSPIRTargetInfo : public TargetInfo {
   // memcpy as per section 3 of the SPIR spec.
   bool useFP16ConversionIntrinsics() const override { return false; }
 
-  llvm::SmallVector<Builtin::InfosShard> getTargetBuiltins() const override {
-    return {};
-  }
+  llvm::SmallVector<Builtin::InfosShard> getTargetBuiltins() const override;
 
   std::string_view getClobbers() const override { return ""; }
 
@@ -321,8 +319,6 @@ class LLVM_LIBRARY_VISIBILITY SPIRVTargetInfo : public BaseSPIRVTargetInfo {
                     "v256:256-v512:512-v1024:1024-n8:16:32:64-G10");
   }
 
-  llvm::SmallVector<Builtin::InfosShard> getTargetBuiltins() const override;
-
   void getTargetDefines(const LangOptions &Opts,
                         MacroBuilder &Builder) const override;
 };
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 1e4e055e04afd..e526e0b62cf16 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -97,10 +97,10 @@ static Value *EmitTargetArchBuiltinExpr(CodeGenFunction *CGF,
   case llvm::Triple::riscv64:
     return CGF->EmitRISCVBuiltinExpr(BuiltinID, E, ReturnValue);
   case llvm::Triple::spirv:
-    return CGF->EmitSPIRVBuiltinExpr(BuiltinID, E);
+  case llvm::Triple::spirv32:
   case llvm::Triple::spirv64:
-    if (CGF->getTarget().getTriple().getOS() != llvm::Triple::OSType::AMDHSA)
-      return nullptr;
+  if (CGF->getTarget().getTriple().getOS() != llvm::Triple::OSType::AMDHSA)
+      return CGF->EmitSPIRVBuiltinExpr(BuiltinID, E);
     return CGF->EmitAMDGPUBuiltinExpr(BuiltinID, E);
   default:
     return nullptr;
diff --git a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
index 26f8eb1fd07f8..0687485cd3f80 100644
--- a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
@@ -83,6 +83,20 @@ Value *CodeGenFunction::EmitSPIRVBuiltinExpr(unsigned BuiltinID,
         /*ReturnType=*/N->getType(), Intrinsic::spv_faceforward,
         ArrayRef<Value *>{N, I, Ng}, /*FMFSource=*/nullptr, "spv.faceforward");
   }
+  case SPIRV::BI__builtin_spirv_generic_cast_to_ptr_explicit: {
+    Value *Ptr = EmitScalarExpr(E->getArg(0));
+    assert(E->getArg(0)->getType()->hasPointerRepresentation() &&
+           E->getArg(1)->getType()->hasIntegerRepresentation() &&
+           "GenericCastToPtrExplicit takes a pointer and an int");
+    llvm::Type *Res = getTypes().ConvertType(E->getType());
+    assert(Res->isPointerTy() &&
+           "GenericCastToPtrExplicit doesn't return a pointer");
+    llvm::CallInst *Call = Builder.CreateIntrinsic(
+        /*ReturnType=*/Res, Intrinsic::spv_generic_cast_to_ptr_explicit,
+        ArrayRef<Value *>{Ptr}, nullptr, "spv.generic_cast");
+    Call->addRetAttr(llvm::Attribute::AttrKind::NoUndef);
+    return Call;
+  }
   }
   return nullptr;
 }
diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt
index acf49e40c447e..556b076abbfbf 100644
--- a/clang/lib/Headers/CMakeLists.txt
+++ b/clang/lib/Headers/CMakeLists.txt
@@ -129,6 +129,10 @@ set(riscv_files
   sifive_vector.h
   )
 
+set(spirv_files
+  __clang_spirv_builtins.h
+  )
+
 set(systemz_files
   s390intrin.h
   vecintrin.h
@@ -316,6 +320,7 @@ set(files
   ${ppc_files}
   ${ppc_htm_files}
   ${riscv_files}
+  ${spirv_files}
   ${systemz_files}
   ${ve_files}
   ${x86_files}
@@ -526,6 +531,7 @@ add_dependencies("clang-resource-headers"
                  "ppc-resource-headers"
                  "ppc-htm-resource-headers"
                  "riscv-resource-headers"
+                 "spirv-resource-headers"
                  "systemz-resource-headers"
                  "ve-resource-headers"
                  "webassembly-resource-headers"
@@ -559,6 +565,7 @@ add_header_target("gpu-resource-headers" "${gpu_files}")
 
 # Other header groupings
 add_header_target("hlsl-resource-headers" ${hlsl_files})
+add_header_target("spirv-resource-headers" ${spirv_files})
 add_header_target("opencl-resource-headers" ${opencl_files})
 add_header_target("llvm-libc-resource-headers" ${llvm_libc_wrapper_files})
 add_header_target("openmp-resource-headers" ${openmp_wrapper_files})
@@ -764,6 +771,12 @@ install(
   ${EXCLUDE_HLSL}
   COMPONENT hlsl-resource-headers)
 
+install(
+  FILES ${spirv_files}
+  DESTINATION ${header_install_dir}
+  EXCLUDE_FROM_ALL
+  COMPONENT spirv-resource-headers)
+
 install(
   FILES ${opencl_files}
   DESTINATION ${header_install_dir}
@@ -833,6 +846,9 @@ if (NOT LLVM_ENABLE_IDE)
   add_llvm_install_targets(install-riscv-resource-headers
                            DEPENDS riscv-resource-headers
                            COMPONENT riscv-resource-headers)
+  add_llvm_install_targets(install-spirv-resource-headers
+                           DEPENDS spirv-resource-headers
+                           COMPONENT spirv-resource-headers)
   add_llvm_install_targets(install-systemz-resource-headers
                            DEPENDS systemz-resource-headers
                            COMPONENT systemz-resource-headers)
diff --git a/clang/lib/Headers/__clang_spirv_builtins.h b/clang/lib/Headers/__clang_spirv_builtins.h
new file mode 100644
index 0000000000000..0b23fc87b4511
--- /dev/null
+++ b/clang/lib/Headers/__clang_spirv_builtins.h
@@ -0,0 +1,176 @@
+/*===---- spirv_builtin_vars.h - SPIR-V built-in ---------------------------===
+ *
+ * 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
+ *
+ *===-----------------------------------------------------------------------===
+ */
+
+#ifndef __SPIRV_BUILTIN_VARS_H
+#define __SPIRV_BUILTIN_VARS_H
+
+#if __cplusplus >= 201103L
+#define __SPIRV_NOEXCEPT noexcept
+#else
+#define __SPIRV_NOEXCEPT
+#endif
+
+#define __SPIRV_overloadable __attribute__((overloadable))
+#define __SPIRV_convergent __attribute__((convergent))
+#define __SPIRV_inline __attribute__((always_inline))
+
+#define __global __attribute__((opencl_global))
+#define __local __attribute__((opencl_local))
+#define __private __attribute__((opencl_private))
+#define __constant __attribute__((opencl_constant))
+#ifdef __SYCL_DEVICE_ONLY__
+#define __generic
+#else
+#define __generic __attribute__((opencl_generic))
+#endif
+
+// Check if SPIR-V builtins are supported.
+// As the translator doesn't use the LLVM intrinsics (which would be emitted if
+// we use the SPIR-V builtins) we can't rely on the SPIRV32/SPIRV64 etc macros
+// to establish if we can use the builtin alias. We disable builtin altogether
+// if we do not intent to use the backend. So instead of use target macros, rely
+// on a __has_builtin test.
+#if (__has_builtin(__builtin_spirv_generic_cast_to_ptr_explicit))
+#define __SPIRV_BUILTIN_ALIAS(builtin)                                          \
+  __attribute__((clang_builtin_alias(builtin)))
+#else
+#define __SPIRV_BUILTIN_ALIAS(builtin)
+#endif
+
+// OpGenericCastToPtrExplicit
+
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__global void *__spirv_GenericCastToPtrExplicit_ToGlobal(__generic void *,
+                                                         int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__global const void *
+__spirv_GenericCastToPtrExplicit_ToGlobal(__generic const void *,
+                                          int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__global volatile void *
+__spirv_GenericCastToPtrExplicit_ToGlobal(__generic volatile void *,
+                                          int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__global const volatile void *
+__spirv_GenericCastToPtrExplicit_ToGlobal(__generic const volatile void *,
+                                          int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__local void *__spirv_GenericCastToPtrExplicit_ToLocal(__generic void *,
+                                                       int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__local const void *
+__spirv_GenericCastToPtrExplicit_ToLocal(__generic const void *,
+                                         int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__local volatile void *
+__spirv_GenericCastToPtrExplicit_ToLocal(__generic volatile void *,
+                                         int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__local const volatile void *
+__spirv_GenericCastToPtrExplicit_ToLocal(__generic const volatile void *,
+                                         int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__private void *__spirv_GenericCastToPtrExplicit_ToPrivate(__generic void *,
+                                                           int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__private const void *
+__spirv_GenericCastToPtrExplicit_ToPrivate(__generic const void *,
+                                           int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__private volatile void *
+__spirv_GenericCastToPtrExplicit_ToPrivate(__generic volatile void *,
+                                           int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__private const volatile void *
+__spirv_GenericCastToPtrExplicit_ToPrivate(__generic const volatile void *,
+                                           int) __SPIRV_NOEXCEPT;
+
+// OpGenericCastToPtr
+
+static __SPIRV_overloadable __SPIRV_inline __global void *
+__spirv_GenericCastToPtr_ToGlobal(__generic void *p, int) __SPIRV_NOEXCEPT {
+  return (__global void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __global const void *
+__spirv_GenericCastToPtr_ToGlobal(__generic const void *p, int) __SPIRV_NOEXCEPT {
+  return (__global const void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __global volatile void *
+__spirv_GenericCastToPtr_ToGlobal(__generic volatile void *p,
+                                  int) __SPIRV_NOEXCEPT {
+  return (__global volatile void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __global const volatile void *
+__spirv_GenericCastToPtr_ToGlobal(__generic const volatile void *p,
+                                  int) __SPIRV_NOEXCEPT {
+  return (__global const volatile void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __local void *
+__spirv_GenericCastToPtr_ToLocal(__generic void *p, int) __SPIRV_NOEXCEPT {
+  return (__local void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __local const void *
+__spirv_GenericCastToPtr_ToLocal(__generic const void *p, int) __SPIRV_NOEXCEPT {
+  return (__local const void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __local volatile void *
+__spirv_GenericCastToPtr_ToLocal(__generic volatile void *p,
+                                 int) __SPIRV_NOEXCEPT {
+  return (__local volatile void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __local const volatile void *
+__spirv_GenericCastToPtr_ToLocal(__generic const volatile void *p,
+                                 int) __SPIRV_NOEXCEPT {
+  return (__local const volatile void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __private void *
+__spirv_GenericCastToPtr_ToPrivate(__generic void *p, int) __SPIRV_NOEXCEPT {
+  return (__private void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __private const void *
+__spirv_GenericCastToPtr_ToPrivate(__generic const void *p,
+                                   int) __SPIRV_NOEXCEPT {
+  return (__private const void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __private volatile void *
+__spirv_GenericCastToPtr_ToPrivate(__generic volatile void *p,
+                                   int) __SPIRV_NOEXCEPT {
+  return (__private volatile void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __private const volatile void *
+__spirv_GenericCastToPtr_ToPrivate(__generic const volatile void *p,
+                                   int) __SPIRV_NOEXCEPT {
+  return (__private const volatile void *)p;
+}
+
+#undef __SPIRV_overloadable
+#undef __SPIRV_convergent
+#undef __SPIRV_inline
+
+#undef __global
+#undef __local
+#undef __constant
+#undef __generic
+
+#undef __SPIRV_BUILTIN_ALIAS
+#undef __SPIRV_NOEXCEPT
+
+#endif /* __SPIRV_BUILTIN_VARS_H */
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 2811fd3a04377..fb0f2d2c0d57b 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1983,7 +1983,11 @@ bool Sema::CheckTSBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID,
   case llvm::Triple::mips64el:
     return MIPS().CheckMipsBuiltinFunctionCall(TI, BuiltinID, TheCall);
   case llvm::Triple::spirv:
-    return SPIRV().CheckSPIRVBuiltinFunctionCall(BuiltinID, TheCall);
+  case llvm::Triple::spirv32:
+  case llvm::Triple::spirv64:
+    if (TI.getTriple().getOS() != llvm::Triple::OSType::AMDHSA)
+      return SPIRV().CheckSPIRVBuiltinFunctionCall(BuiltinID, TheCall);
+    return false;
   case llvm::Triple::systemz:
     return SystemZ().CheckSystemZBuiltinFunctionCall(BuiltinID, TheCall);
   case llvm::Triple::x86:
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 413999b95b998..28128d21e53cf 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -53,6 +53,7 @@
 #include "clang/Sema/SemaOpenCL.h"
 #include "clang/Sema/SemaOpenMP.h"
 #include "clang/Sema/SemaRISCV.h"
+#include "clang/Sema/SemaSPIRV.h"
 #include "clang/Sema/SemaSYCL.h"
 #include "clang/Sema/SemaSwift.h"
 #include "clang/Sema/SemaWasm.h"
@@ -5837,12 +5838,13 @@ static void handleBuiltinAliasAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   bool IsAArch64 = S.Context.getTargetInfo().getTriple().isAArch64();
   bool IsARM = S.Context.getTargetInfo().getTriple().isARM();
   bool IsRISCV = S.Context.getTargetInfo().getTriple().isRISCV();
+  bool IsSPIRV = S.Context.getTargetInfo().getTriple().isSPIRV();
   bool IsHLSL = S.Context.getLangOpts().HLSL;
   if ((IsAArch64 && !S.ARM().SveAliasValid(BuiltinID, AliasName)) ||
       (IsARM && !S.ARM().MveAliasValid(BuiltinID, AliasName) &&
        !S.ARM().CdeAliasValid(BuiltinID, AliasName)) ||
       (IsRISCV && !S.RISCV().isAliasValid(BuiltinID, AliasName)) ||
-      (!IsAArch64 && !IsARM && !IsRISCV && !IsHLSL)) {
+      (!IsAArch64 && !IsARM && !IsRISCV && !IsHLSL && !IsSPIRV)) {
     S.Diag(AL.getLoc(), diag::err_attribute_builtin_alias) << AL;
     return;
   }
diff --git a/clang/lib/Sema/SemaSPIRV.cpp b/clang/lib/Sema/SemaSPIRV.cpp
index 90888f1417a9d..9282d02bcb8f5 100644
--- a/clang/lib/Sema/SemaSPIRV.cpp
+++ b/clang/lib/Sema/SemaSPIRV.cpp
@@ -12,6 +12,18 @@
 #include "clang/Basic/TargetBuiltins.h"
 #include "clang/Sema/Sema.h"
 
+// SPIR-V enumerants. Enums have only the required entries, see SPIR-V specs for
+// values.
+// FIXME: either use the SPIRV-Headers or generate a custom header using the
+// grammar (like done with MLIR).
+namespace spirv {
+enum class StorageClass : int {
+  Workgroup = 4,
+  CrossWorkgroup = 5,
+  Function = 7
+};
+}
+
 namespace clang {
 
 SemaSPIRV::SemaSPIRV(Sema &S) : SemaBase(S) {}
@@ -33,6 +45,96 @@ static bool CheckAllArgsHaveSameType(Sema *S, CallExpr *TheCall) {
   return false;
 }
 
+static std::optional<int>
+processConstant32BitIntArgument(Sema &SemaRef, CallExpr *Call, int Argument) {
+  ExprResult Arg =
+      SemaRef.DefaultFunctionArrayLvalueConversion(Call->getArg(Argument));
+  if (Arg.isInvalid())
+    return true;
+  Call->setArg(Argument, Arg.get());
+
+  const Expr *IntArg = Arg.get();
+  SmallVector<PartialDiagnosticAt, 8> Notes;
+  Expr::EvalResult Eval;
+  Eval.Diag = &Notes;
+  if ((!IntArg->EvaluateA...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Apr 29, 2025

@llvm/pr-subscribers-backend-x86

Author: Victor Lomuller (Naghasan)

Changes

The patch introduce __builtin_spirv_generic_cast_to_ptr_explicit which is lowered to the llvm.spv.generic.cast.to.ptr.explicit intrinsic.

The patch also introduces a new header defining its SPIR-V friendly equivalent (__spirv_GenericCastToPtrExplicit_ToGlobal, __spirv_GenericCastToPtrExplicit_ToLocal and __spirv_GenericCastToPtrExplicit_ToPrivate). The functions are declared as aliases to the new builtin allowing C-like languages to have a definition to rely on as well as gaining proper front-end diagnostics.

The motivation for the header is to provide a stable binding for applications or library (such as SYCL) and allows non SPIR-V targets to provide an implementation (via libclc or similar to how it is done for gpuintrin.h).


Patch is 28.23 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/137805.diff

15 Files Affected:

  • (modified) clang/include/clang/Basic/BuiltinsSPIRV.td (+9)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+9-1)
  • (modified) clang/lib/AST/ASTContext.cpp (+5)
  • (modified) clang/lib/Basic/Targets/SPIR.cpp (+4-2)
  • (modified) clang/lib/Basic/Targets/SPIR.h (+1-5)
  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+3-3)
  • (modified) clang/lib/CodeGen/TargetBuiltins/SPIR.cpp (+14)
  • (modified) clang/lib/Headers/CMakeLists.txt (+16)
  • (added) clang/lib/Headers/__clang_spirv_builtins.h (+176)
  • (modified) clang/lib/Sema/SemaChecking.cpp (+5-1)
  • (modified) clang/lib/Sema/SemaDeclAttr.cpp (+3-1)
  • (modified) clang/lib/Sema/SemaSPIRV.cpp (+105)
  • (added) clang/test/CodeGenSPIRV/Builtins/generic_cast_to_ptr_explicit.c (+33)
  • (added) clang/test/Headers/spirv_functions.cpp (+25)
  • (added) clang/test/SemaSPIRV/BuiltIns/generic_cast_to_ptr_explicit.c (+25)
diff --git a/clang/include/clang/Basic/BuiltinsSPIRV.td b/clang/include/clang/Basic/BuiltinsSPIRV.td
index cc0c2f960f8d2..bbb2abba2e256 100644
--- a/clang/include/clang/Basic/BuiltinsSPIRV.td
+++ b/clang/include/clang/Basic/BuiltinsSPIRV.td
@@ -8,6 +8,12 @@
 
 include "clang/Basic/BuiltinsBase.td"
 
+class SPIRVBuiltin<string prototype, list<Attribute> Attr> : Builtin {
+  let Spellings = ["__builtin_spirv_"#NAME];
+  let Prototype = prototype;
+  let Attributes = !listconcat([NoThrow], Attr);
+}
+
 def SPIRVDistance : Builtin {
   let Spellings = ["__builtin_spirv_distance"];
   let Attributes = [NoThrow, Const];
@@ -37,3 +43,6 @@ def SPIRVFaceForward : Builtin {
   let Attributes = [NoThrow, Const, CustomTypeChecking];
   let Prototype = "void(...)";
 }
+
+def generic_cast_to_ptr_explicit
+    : SPIRVBuiltin<"void*(void*, int)", [NoThrow, Const, CustomTypeChecking]>;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 4c96142e28134..8f088d4d0d0f8 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4609,7 +4609,7 @@ def err_attribute_preferred_name_arg_invalid : Error<
   "argument %0 to 'preferred_name' attribute is not a typedef for "
   "a specialization of %1">;
 def err_attribute_builtin_alias : Error<
-  "%0 attribute can only be applied to a ARM, HLSL or RISC-V builtin">;
+  "%0 attribute can only be applied to a ARM, HLSL, SPIR-V or RISC-V builtin">;
 
 // called-once attribute diagnostics.
 def err_called_once_attribute_wrong_type : Error<
@@ -12740,6 +12740,14 @@ def err_bit_int_bad_size : Error<"%select{signed|unsigned}0 _BitInt must "
 def err_bit_int_max_size : Error<"%select{signed|unsigned}0 _BitInt of bit "
                                  "sizes greater than %1 not supported">;
 
+// SPIR-V builtins diagnostics
+def err_spirv_builtin_generic_cast_invalid_arg : Error<
+  "expecting a pointer argument to the generic address space">;
+def err_spirv_enum_not_int : Error<
+   "%0{storage class} argument for SPIR-V builtin is not a 32-bits integer">;
+def err_spirv_enum_not_valid : Error<
+   "invalid value for %select{storage class}0 argument">;
+
 // errors of expect.with.probability
 def err_probability_not_constant_float : Error<
    "probability argument to __builtin_expect_with_probability must be constant "
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index c95e733f30494..51438c22f52fe 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -10054,6 +10054,11 @@ bool ASTContext::canBuiltinBeRedeclared(const FunctionDecl *FD) const {
   if (LangOpts.HLSL && FD->getBuiltinID() != Builtin::NotBuiltin &&
       BuiltinInfo.hasCustomTypechecking(FD->getBuiltinID()))
     return true;
+  // Allow redecl custom type checking builtin for SPIR-V.
+  if (getTargetInfo().getTriple().isSPIROrSPIRV() &&
+      BuiltinInfo.isTSBuiltin(FD->getBuiltinID()) &&
+      BuiltinInfo.hasCustomTypechecking(FD->getBuiltinID()))
+    return true;
   return BuiltinInfo.canBeRedeclared(FD->getBuiltinID());
 }
 
diff --git a/clang/lib/Basic/Targets/SPIR.cpp b/clang/lib/Basic/Targets/SPIR.cpp
index 5b5f47f9647a2..d742ae8ff044d 100644
--- a/clang/lib/Basic/Targets/SPIR.cpp
+++ b/clang/lib/Basic/Targets/SPIR.cpp
@@ -35,8 +35,10 @@ static constexpr Builtin::Info BuiltinInfos[] = {
 static_assert(std::size(BuiltinInfos) == NumBuiltins);
 
 llvm::SmallVector<Builtin::InfosShard>
-SPIRVTargetInfo::getTargetBuiltins() const {
-  return {{&BuiltinStrings, BuiltinInfos}};
+BaseSPIRTargetInfo::getTargetBuiltins() const {
+  if (getTriple().isSPIRV())
+    return {{&BuiltinStrings, BuiltinInfos}};
+  return {};
 }
 
 void SPIRTargetInfo::getTargetDefines(const LangOptions &Opts,
diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h
index bf249e271a870..c6c31884db34d 100644
--- a/clang/lib/Basic/Targets/SPIR.h
+++ b/clang/lib/Basic/Targets/SPIR.h
@@ -164,9 +164,7 @@ class LLVM_LIBRARY_VISIBILITY BaseSPIRTargetInfo : public TargetInfo {
   // memcpy as per section 3 of the SPIR spec.
   bool useFP16ConversionIntrinsics() const override { return false; }
 
-  llvm::SmallVector<Builtin::InfosShard> getTargetBuiltins() const override {
-    return {};
-  }
+  llvm::SmallVector<Builtin::InfosShard> getTargetBuiltins() const override;
 
   std::string_view getClobbers() const override { return ""; }
 
@@ -321,8 +319,6 @@ class LLVM_LIBRARY_VISIBILITY SPIRVTargetInfo : public BaseSPIRVTargetInfo {
                     "v256:256-v512:512-v1024:1024-n8:16:32:64-G10");
   }
 
-  llvm::SmallVector<Builtin::InfosShard> getTargetBuiltins() const override;
-
   void getTargetDefines(const LangOptions &Opts,
                         MacroBuilder &Builder) const override;
 };
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 1e4e055e04afd..e526e0b62cf16 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -97,10 +97,10 @@ static Value *EmitTargetArchBuiltinExpr(CodeGenFunction *CGF,
   case llvm::Triple::riscv64:
     return CGF->EmitRISCVBuiltinExpr(BuiltinID, E, ReturnValue);
   case llvm::Triple::spirv:
-    return CGF->EmitSPIRVBuiltinExpr(BuiltinID, E);
+  case llvm::Triple::spirv32:
   case llvm::Triple::spirv64:
-    if (CGF->getTarget().getTriple().getOS() != llvm::Triple::OSType::AMDHSA)
-      return nullptr;
+  if (CGF->getTarget().getTriple().getOS() != llvm::Triple::OSType::AMDHSA)
+      return CGF->EmitSPIRVBuiltinExpr(BuiltinID, E);
     return CGF->EmitAMDGPUBuiltinExpr(BuiltinID, E);
   default:
     return nullptr;
diff --git a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
index 26f8eb1fd07f8..0687485cd3f80 100644
--- a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
@@ -83,6 +83,20 @@ Value *CodeGenFunction::EmitSPIRVBuiltinExpr(unsigned BuiltinID,
         /*ReturnType=*/N->getType(), Intrinsic::spv_faceforward,
         ArrayRef<Value *>{N, I, Ng}, /*FMFSource=*/nullptr, "spv.faceforward");
   }
+  case SPIRV::BI__builtin_spirv_generic_cast_to_ptr_explicit: {
+    Value *Ptr = EmitScalarExpr(E->getArg(0));
+    assert(E->getArg(0)->getType()->hasPointerRepresentation() &&
+           E->getArg(1)->getType()->hasIntegerRepresentation() &&
+           "GenericCastToPtrExplicit takes a pointer and an int");
+    llvm::Type *Res = getTypes().ConvertType(E->getType());
+    assert(Res->isPointerTy() &&
+           "GenericCastToPtrExplicit doesn't return a pointer");
+    llvm::CallInst *Call = Builder.CreateIntrinsic(
+        /*ReturnType=*/Res, Intrinsic::spv_generic_cast_to_ptr_explicit,
+        ArrayRef<Value *>{Ptr}, nullptr, "spv.generic_cast");
+    Call->addRetAttr(llvm::Attribute::AttrKind::NoUndef);
+    return Call;
+  }
   }
   return nullptr;
 }
diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt
index acf49e40c447e..556b076abbfbf 100644
--- a/clang/lib/Headers/CMakeLists.txt
+++ b/clang/lib/Headers/CMakeLists.txt
@@ -129,6 +129,10 @@ set(riscv_files
   sifive_vector.h
   )
 
+set(spirv_files
+  __clang_spirv_builtins.h
+  )
+
 set(systemz_files
   s390intrin.h
   vecintrin.h
@@ -316,6 +320,7 @@ set(files
   ${ppc_files}
   ${ppc_htm_files}
   ${riscv_files}
+  ${spirv_files}
   ${systemz_files}
   ${ve_files}
   ${x86_files}
@@ -526,6 +531,7 @@ add_dependencies("clang-resource-headers"
                  "ppc-resource-headers"
                  "ppc-htm-resource-headers"
                  "riscv-resource-headers"
+                 "spirv-resource-headers"
                  "systemz-resource-headers"
                  "ve-resource-headers"
                  "webassembly-resource-headers"
@@ -559,6 +565,7 @@ add_header_target("gpu-resource-headers" "${gpu_files}")
 
 # Other header groupings
 add_header_target("hlsl-resource-headers" ${hlsl_files})
+add_header_target("spirv-resource-headers" ${spirv_files})
 add_header_target("opencl-resource-headers" ${opencl_files})
 add_header_target("llvm-libc-resource-headers" ${llvm_libc_wrapper_files})
 add_header_target("openmp-resource-headers" ${openmp_wrapper_files})
@@ -764,6 +771,12 @@ install(
   ${EXCLUDE_HLSL}
   COMPONENT hlsl-resource-headers)
 
+install(
+  FILES ${spirv_files}
+  DESTINATION ${header_install_dir}
+  EXCLUDE_FROM_ALL
+  COMPONENT spirv-resource-headers)
+
 install(
   FILES ${opencl_files}
   DESTINATION ${header_install_dir}
@@ -833,6 +846,9 @@ if (NOT LLVM_ENABLE_IDE)
   add_llvm_install_targets(install-riscv-resource-headers
                            DEPENDS riscv-resource-headers
                            COMPONENT riscv-resource-headers)
+  add_llvm_install_targets(install-spirv-resource-headers
+                           DEPENDS spirv-resource-headers
+                           COMPONENT spirv-resource-headers)
   add_llvm_install_targets(install-systemz-resource-headers
                            DEPENDS systemz-resource-headers
                            COMPONENT systemz-resource-headers)
diff --git a/clang/lib/Headers/__clang_spirv_builtins.h b/clang/lib/Headers/__clang_spirv_builtins.h
new file mode 100644
index 0000000000000..0b23fc87b4511
--- /dev/null
+++ b/clang/lib/Headers/__clang_spirv_builtins.h
@@ -0,0 +1,176 @@
+/*===---- spirv_builtin_vars.h - SPIR-V built-in ---------------------------===
+ *
+ * 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
+ *
+ *===-----------------------------------------------------------------------===
+ */
+
+#ifndef __SPIRV_BUILTIN_VARS_H
+#define __SPIRV_BUILTIN_VARS_H
+
+#if __cplusplus >= 201103L
+#define __SPIRV_NOEXCEPT noexcept
+#else
+#define __SPIRV_NOEXCEPT
+#endif
+
+#define __SPIRV_overloadable __attribute__((overloadable))
+#define __SPIRV_convergent __attribute__((convergent))
+#define __SPIRV_inline __attribute__((always_inline))
+
+#define __global __attribute__((opencl_global))
+#define __local __attribute__((opencl_local))
+#define __private __attribute__((opencl_private))
+#define __constant __attribute__((opencl_constant))
+#ifdef __SYCL_DEVICE_ONLY__
+#define __generic
+#else
+#define __generic __attribute__((opencl_generic))
+#endif
+
+// Check if SPIR-V builtins are supported.
+// As the translator doesn't use the LLVM intrinsics (which would be emitted if
+// we use the SPIR-V builtins) we can't rely on the SPIRV32/SPIRV64 etc macros
+// to establish if we can use the builtin alias. We disable builtin altogether
+// if we do not intent to use the backend. So instead of use target macros, rely
+// on a __has_builtin test.
+#if (__has_builtin(__builtin_spirv_generic_cast_to_ptr_explicit))
+#define __SPIRV_BUILTIN_ALIAS(builtin)                                          \
+  __attribute__((clang_builtin_alias(builtin)))
+#else
+#define __SPIRV_BUILTIN_ALIAS(builtin)
+#endif
+
+// OpGenericCastToPtrExplicit
+
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__global void *__spirv_GenericCastToPtrExplicit_ToGlobal(__generic void *,
+                                                         int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__global const void *
+__spirv_GenericCastToPtrExplicit_ToGlobal(__generic const void *,
+                                          int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__global volatile void *
+__spirv_GenericCastToPtrExplicit_ToGlobal(__generic volatile void *,
+                                          int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__global const volatile void *
+__spirv_GenericCastToPtrExplicit_ToGlobal(__generic const volatile void *,
+                                          int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__local void *__spirv_GenericCastToPtrExplicit_ToLocal(__generic void *,
+                                                       int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__local const void *
+__spirv_GenericCastToPtrExplicit_ToLocal(__generic const void *,
+                                         int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__local volatile void *
+__spirv_GenericCastToPtrExplicit_ToLocal(__generic volatile void *,
+                                         int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__local const volatile void *
+__spirv_GenericCastToPtrExplicit_ToLocal(__generic const volatile void *,
+                                         int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__private void *__spirv_GenericCastToPtrExplicit_ToPrivate(__generic void *,
+                                                           int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__private const void *
+__spirv_GenericCastToPtrExplicit_ToPrivate(__generic const void *,
+                                           int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__private volatile void *
+__spirv_GenericCastToPtrExplicit_ToPrivate(__generic volatile void *,
+                                           int) __SPIRV_NOEXCEPT;
+extern __SPIRV_overloadable
+__SPIRV_BUILTIN_ALIAS(__builtin_spirv_generic_cast_to_ptr_explicit)
+__private const volatile void *
+__spirv_GenericCastToPtrExplicit_ToPrivate(__generic const volatile void *,
+                                           int) __SPIRV_NOEXCEPT;
+
+// OpGenericCastToPtr
+
+static __SPIRV_overloadable __SPIRV_inline __global void *
+__spirv_GenericCastToPtr_ToGlobal(__generic void *p, int) __SPIRV_NOEXCEPT {
+  return (__global void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __global const void *
+__spirv_GenericCastToPtr_ToGlobal(__generic const void *p, int) __SPIRV_NOEXCEPT {
+  return (__global const void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __global volatile void *
+__spirv_GenericCastToPtr_ToGlobal(__generic volatile void *p,
+                                  int) __SPIRV_NOEXCEPT {
+  return (__global volatile void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __global const volatile void *
+__spirv_GenericCastToPtr_ToGlobal(__generic const volatile void *p,
+                                  int) __SPIRV_NOEXCEPT {
+  return (__global const volatile void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __local void *
+__spirv_GenericCastToPtr_ToLocal(__generic void *p, int) __SPIRV_NOEXCEPT {
+  return (__local void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __local const void *
+__spirv_GenericCastToPtr_ToLocal(__generic const void *p, int) __SPIRV_NOEXCEPT {
+  return (__local const void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __local volatile void *
+__spirv_GenericCastToPtr_ToLocal(__generic volatile void *p,
+                                 int) __SPIRV_NOEXCEPT {
+  return (__local volatile void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __local const volatile void *
+__spirv_GenericCastToPtr_ToLocal(__generic const volatile void *p,
+                                 int) __SPIRV_NOEXCEPT {
+  return (__local const volatile void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __private void *
+__spirv_GenericCastToPtr_ToPrivate(__generic void *p, int) __SPIRV_NOEXCEPT {
+  return (__private void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __private const void *
+__spirv_GenericCastToPtr_ToPrivate(__generic const void *p,
+                                   int) __SPIRV_NOEXCEPT {
+  return (__private const void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __private volatile void *
+__spirv_GenericCastToPtr_ToPrivate(__generic volatile void *p,
+                                   int) __SPIRV_NOEXCEPT {
+  return (__private volatile void *)p;
+}
+static __SPIRV_overloadable __SPIRV_inline __private const volatile void *
+__spirv_GenericCastToPtr_ToPrivate(__generic const volatile void *p,
+                                   int) __SPIRV_NOEXCEPT {
+  return (__private const volatile void *)p;
+}
+
+#undef __SPIRV_overloadable
+#undef __SPIRV_convergent
+#undef __SPIRV_inline
+
+#undef __global
+#undef __local
+#undef __constant
+#undef __generic
+
+#undef __SPIRV_BUILTIN_ALIAS
+#undef __SPIRV_NOEXCEPT
+
+#endif /* __SPIRV_BUILTIN_VARS_H */
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 2811fd3a04377..fb0f2d2c0d57b 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1983,7 +1983,11 @@ bool Sema::CheckTSBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID,
   case llvm::Triple::mips64el:
     return MIPS().CheckMipsBuiltinFunctionCall(TI, BuiltinID, TheCall);
   case llvm::Triple::spirv:
-    return SPIRV().CheckSPIRVBuiltinFunctionCall(BuiltinID, TheCall);
+  case llvm::Triple::spirv32:
+  case llvm::Triple::spirv64:
+    if (TI.getTriple().getOS() != llvm::Triple::OSType::AMDHSA)
+      return SPIRV().CheckSPIRVBuiltinFunctionCall(BuiltinID, TheCall);
+    return false;
   case llvm::Triple::systemz:
     return SystemZ().CheckSystemZBuiltinFunctionCall(BuiltinID, TheCall);
   case llvm::Triple::x86:
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 413999b95b998..28128d21e53cf 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -53,6 +53,7 @@
 #include "clang/Sema/SemaOpenCL.h"
 #include "clang/Sema/SemaOpenMP.h"
 #include "clang/Sema/SemaRISCV.h"
+#include "clang/Sema/SemaSPIRV.h"
 #include "clang/Sema/SemaSYCL.h"
 #include "clang/Sema/SemaSwift.h"
 #include "clang/Sema/SemaWasm.h"
@@ -5837,12 +5838,13 @@ static void handleBuiltinAliasAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   bool IsAArch64 = S.Context.getTargetInfo().getTriple().isAArch64();
   bool IsARM = S.Context.getTargetInfo().getTriple().isARM();
   bool IsRISCV = S.Context.getTargetInfo().getTriple().isRISCV();
+  bool IsSPIRV = S.Context.getTargetInfo().getTriple().isSPIRV();
   bool IsHLSL = S.Context.getLangOpts().HLSL;
   if ((IsAArch64 && !S.ARM().SveAliasValid(BuiltinID, AliasName)) ||
       (IsARM && !S.ARM().MveAliasValid(BuiltinID, AliasName) &&
        !S.ARM().CdeAliasValid(BuiltinID, AliasName)) ||
       (IsRISCV && !S.RISCV().isAliasValid(BuiltinID, AliasName)) ||
-      (!IsAArch64 && !IsARM && !IsRISCV && !IsHLSL)) {
+      (!IsAArch64 && !IsARM && !IsRISCV && !IsHLSL && !IsSPIRV)) {
     S.Diag(AL.getLoc(), diag::err_attribute_builtin_alias) << AL;
     return;
   }
diff --git a/clang/lib/Sema/SemaSPIRV.cpp b/clang/lib/Sema/SemaSPIRV.cpp
index 90888f1417a9d..9282d02bcb8f5 100644
--- a/clang/lib/Sema/SemaSPIRV.cpp
+++ b/clang/lib/Sema/SemaSPIRV.cpp
@@ -12,6 +12,18 @@
 #include "clang/Basic/TargetBuiltins.h"
 #include "clang/Sema/Sema.h"
 
+// SPIR-V enumerants. Enums have only the required entries, see SPIR-V specs for
+// values.
+// FIXME: either use the SPIRV-Headers or generate a custom header using the
+// grammar (like done with MLIR).
+namespace spirv {
+enum class StorageClass : int {
+  Workgroup = 4,
+  CrossWorkgroup = 5,
+  Function = 7
+};
+}
+
 namespace clang {
 
 SemaSPIRV::SemaSPIRV(Sema &S) : SemaBase(S) {}
@@ -33,6 +45,96 @@ static bool CheckAllArgsHaveSameType(Sema *S, CallExpr *TheCall) {
   return false;
 }
 
+static std::optional<int>
+processConstant32BitIntArgument(Sema &SemaRef, CallExpr *Call, int Argument) {
+  ExprResult Arg =
+      SemaRef.DefaultFunctionArrayLvalueConversion(Call->getArg(Argument));
+  if (Arg.isInvalid())
+    return true;
+  Call->setArg(Argument, Arg.get());
+
+  const Expr *IntArg = Arg.get();
+  SmallVector<PartialDiagnosticAt, 8> Notes;
+  Expr::EvalResult Eval;
+  Eval.Diag = &Notes;
+  if ((!IntArg->EvaluateA...
[truncated]

Copy link

github-actions bot commented Apr 29, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@Naghasan
Copy link
Contributor Author

Sorry I can't assign reviewers, @JonChesterfield @jhuber6 as you are involved in gpuintrin.h, I'd welcome feedbacks here (feel free to ping other relevant persons)

For the SPIR-V side of thing @VyacheslavLevytskyy @farzonl I appreciate your feedbacks as well :)

FYI @tahonermann

@Naghasan Naghasan force-pushed the generic_cast_to_ptr_explicit_builtin branch from c01ab8c to 90725e8 Compare April 29, 2025 13:20
return {{&BuiltinStrings, BuiltinInfos}};
BaseSPIRTargetInfo::getTargetBuiltins() const {
if (getTriple().isSPIRV())
return {{&BuiltinStrings, BuiltinInfos}};
Copy link
Member

Choose a reason for hiding this comment

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

I don't understand why this change (in SPIR.cpp and SPIR.h) is necessesary. The existing code accomplished the same thing with method overriding. The original code also had some defensive programming safety to me. We went from a compile time enforcement of SPIRVTargetInfo== BuiltinsSPIRV.td to now a runtime enforcement. The problem with that is the next guy that comes along and makes a change might assume the builtins so far are SPIRV and thats why you are checking the triple, then add a new SPIR flavor builtin that would not be valid for SPIRV.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, that's a left over of my initial prototype in intel/llvm sorry. For historical reasons spir / spir64 is also used there, so had to hack a bit to allow proper testing there. The change should be moving it to BaseSPIRVTargetInfo to only enable spirv, spirv32 and spirv64. I updated the PR.

Copy link
Member

@farzonl farzonl Apr 29, 2025

Choose a reason for hiding this comment

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

Ok we might have a hierarchy problem then how do we say a builtin is valid for spirv but not spirv32 or spriv64 and vice versa. How do we say which ones are valid across all targets? See #137805 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had a few thought about this, but there is a bit more to it:

  1. Vulkan vs OpenCL environment (and for which target version)
  2. Enabled vs disabled extensions
  3. SPIR-V version

each will have an impact on the set of enabled builtins. A split between common, Vk env (spirv) and OpenCL env (spirv32 / spirv64) builtins is probably a good step towards answering (1) by construction. What I don't like though is you'll end up with an "unknown builtin" rather than a clear message.
For the rest, I wanted to create an RFC to suggest the use of target features as there is plenty of existing infrastructure for this job.

Copy link
Member

@farzonl farzonl Apr 29, 2025

Choose a reason for hiding this comment

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

What if we had a BuiltinsSPIRVCore.td BuiltinsSPIRVVK.td and a BuiltinsSPIRVCL.td The stuff shared between CL and VK could live in Core and the VK and CL builtins will include the Core one. That should let us have seperate SPIRVTargetInfo and SPIRV64TargetInfo/SPIRV32TargetInfo. You won't have an unknown builtin if we do it this way because we would never expose the builtins that aren't valid for the target.

It could complicate codegen though forcing seperate emitters for spriv and spirv32/64 so not a silver bullet.

Copy link
Member

Choose a reason for hiding this comment

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

I'll consider this issue resolved though if you plan to address it via an RFC.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What if we had a BuiltinsSPIRVCore.td BuiltinsSPIRVVK.td and a BuiltinsSPIRVCL.td

I was looking into doing this.

You won't have an unknown builtin if we do it this way because we would never expose the builtins that aren't valid for the target.

You will if you use a VK one when targeting spirv64 rather than a clear message. But it is nitpicking and I only have a weak preference on the matter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@farzonl Apply the suggestion, although you can't really split into 2 distinct set cleanly (common + vk and common + cl) due to the ID assignment (part is done dynamically). So I went for a unique set and diagnose when vk builtins are used with spirv32 or spirv32 or when cl builtins are used with spirv.

SPIRVTargetInfo::getTargetBuiltins() const {
return {{&BuiltinStrings, BuiltinInfos}};
BaseSPIRTargetInfo::getTargetBuiltins() const {
if (getTriple().isSPIRV())
Copy link
Member

Choose a reason for hiding this comment

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

if you are using this to extend the builtins to spirv32/spirv64 I have concerns that not all builtins will be valid for those targets. For example __builtin_spirv_reflect is only valid when targeting spirv because thats the target that enables GLSL extentions. It is not valid for spirv32/64 because there is no equivalent opencl extention. To expose across all targets without exposing builtins that break could be tricky and might require special codegen to emulate the behavior of other targets. It might be simpler to just do a new tablegen file for spirv32/spriv64.

…R-V friendly binding

The patch introduce __builtin_spirv_generic_cast_to_ptr_explicit which is lowered to
the llvm.spv.generic.cast.to.ptr.explicit intrinsic.

The patch also introduces a new header defining its SPIR-V friendly equivalent
(__spirv_GenericCastToPtrExplicit_ToGlobal, __spirv_GenericCastToPtrExplicit_ToLocal
and __spirv_GenericCastToPtrExplicit_ToPrivate). The functions are declared as aliases
to the new builtin allowing C-like languages to have a definition to rely on as well as
gaining proper front-end diagnostics.
@Naghasan Naghasan force-pushed the generic_cast_to_ptr_explicit_builtin branch from 90725e8 to 020a804 Compare April 29, 2025 13:49
bool IsHLSL = S.Context.getLangOpts().HLSL;
if ((IsAArch64 && !S.ARM().SveAliasValid(BuiltinID, AliasName)) ||
(IsARM && !S.ARM().MveAliasValid(BuiltinID, AliasName) &&
!S.ARM().CdeAliasValid(BuiltinID, AliasName)) ||
(IsRISCV && !S.RISCV().isAliasValid(BuiltinID, AliasName)) ||
(!IsAArch64 && !IsARM && !IsRISCV && !IsHLSL)) {
(!IsAArch64 && !IsARM && !IsRISCV && !IsHLSL && !IsSPIRV)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not affecting HLSL in any way here.

The SPIR-V builtins are now split into 3 differents file: BuiltinsSPIRVCore.td,
BuiltinsSPIRVVK.td for Vulkan specific builtins, BuiltinsSPIRVCL.td for OpenCL specific builtins
and BuiltinsSPIRVCommon.td for common ones.
@Naghasan Naghasan force-pushed the generic_cast_to_ptr_explicit_builtin branch from 05e0d73 to 1615b6b Compare April 30, 2025 09:47
Copy link
Contributor

@VyacheslavLevytskyy VyacheslavLevytskyy left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Member

@farzonl farzonl left a comment

Choose a reason for hiding this comment

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

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:SPIR-V backend:X86 clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:headers Headers provided by Clang, e.g. for intrinsics clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants