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

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).
This commit is contained in:
Victor Lomuller
2025-05-29 14:19:40 +01:00
committed by GitHub
parent 7efb79b705
commit c474f8f240
24 changed files with 587 additions and 59 deletions

View File

@@ -1,39 +0,0 @@
//===--- BuiltinsSPIRV.td - SPIRV Builtin function database ---------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
include "clang/Basic/BuiltinsBase.td"
def SPIRVDistance : Builtin {
let Spellings = ["__builtin_spirv_distance"];
let Attributes = [NoThrow, Const];
let Prototype = "void(...)";
}
def SPIRVLength : Builtin {
let Spellings = ["__builtin_spirv_length"];
let Attributes = [NoThrow, Const];
let Prototype = "void(...)";
}
def SPIRVReflect : Builtin {
let Spellings = ["__builtin_spirv_reflect"];
let Attributes = [NoThrow, Const];
let Prototype = "void(...)";
}
def SPIRVSmoothStep : Builtin {
let Spellings = ["__builtin_spirv_smoothstep"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Prototype = "void(...)";
}
def SPIRVFaceForward : Builtin {
let Spellings = ["__builtin_spirv_faceforward"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Prototype = "void(...)";
}

View File

@@ -0,0 +1,15 @@
//===--- BuiltinsSPIRVBase.td - SPIRV Builtin function database -*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
include "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);
}

View File

@@ -0,0 +1,12 @@
//===--- BuiltinsSPIRVCL.td - SPIRV Builtin function database ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
include "clang/Basic/BuiltinsSPIRVBase.td"
def generic_cast_to_ptr_explicit
: SPIRVBuiltin<"void*(void*, int)", [NoThrow, Const, CustomTypeChecking]>;

View File

@@ -0,0 +1,13 @@
//===- BuiltinsSPIRVCommon.td - SPIRV Builtin function database -*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
include "clang/Basic/BuiltinsSPIRVBase.td"
def distance : SPIRVBuiltin<"void(...)", [NoThrow, Const]>;
def length : SPIRVBuiltin<"void(...)", [NoThrow, Const]>;
def smoothstep : SPIRVBuiltin<"void(...)", [NoThrow, Const, CustomTypeChecking]>;

View File

@@ -0,0 +1,13 @@
//===--- BuiltinsSPIRVVK.td - SPIRV Builtin function database ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
include "clang/Basic/BuiltinsSPIRVBase.td"
def reflect : SPIRVBuiltin<"void(...)", [NoThrow, Const]>;
def faceforward : SPIRVBuiltin<"void(...)", [NoThrow, Const, CustomTypeChecking]>;

View File

@@ -109,9 +109,17 @@ clang_tablegen(BuiltinsRISCV.inc -gen-clang-builtins
SOURCE BuiltinsRISCV.td
TARGET ClangBuiltinsRISCV)
clang_tablegen(BuiltinsSPIRV.inc -gen-clang-builtins
SOURCE BuiltinsSPIRV.td
TARGET ClangBuiltinsSPIRV)
clang_tablegen(BuiltinsSPIRVCommon.inc -gen-clang-builtins
SOURCE BuiltinsSPIRVCommon.td
TARGET ClangBuiltinsSPIRVCommon)
clang_tablegen(BuiltinsSPIRVVK.inc -gen-clang-builtins
SOURCE BuiltinsSPIRVVK.td
TARGET ClangBuiltinsSPIRVVK)
clang_tablegen(BuiltinsSPIRVCL.inc -gen-clang-builtins
SOURCE BuiltinsSPIRVCL.td
TARGET ClangBuiltinsSPIRVCL)
clang_tablegen(BuiltinsX86.inc -gen-clang-builtins
SOURCE BuiltinsX86.td

View File

@@ -4679,7 +4679,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<
@@ -12878,6 +12878,16 @@ 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_invalid_target : Error<
"builtin requires %select{spirv|spirv32 or spirv64}0 target">;
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 "

View File

@@ -157,7 +157,17 @@ namespace clang {
enum {
LastTIBuiltin = clang::Builtin::FirstTSBuiltin - 1,
#define GET_BUILTIN_ENUMERATORS
#include "clang/Basic/BuiltinsSPIRV.inc"
#include "clang/Basic/BuiltinsSPIRVCommon.inc"
#undef GET_BUILTIN_ENUMERATORS
FirstVKBuiltin,
LastCoreBuiltin = FirstVKBuiltin - 1,
#define GET_BUILTIN_ENUMERATORS
#include "clang/Basic/BuiltinsSPIRVVK.inc"
#undef GET_BUILTIN_ENUMERATORS
FirstCLBuiltin,
LastVKBuiltin = FirstCLBuiltin - 1,
#define GET_BUILTIN_ENUMERATORS
#include "clang/Basic/BuiltinsSPIRVCL.inc"
#undef GET_BUILTIN_ENUMERATORS
LastTSBuiltin
};

View File

@@ -21,7 +21,8 @@ class SemaSPIRV : public SemaBase {
public:
SemaSPIRV(Sema &S);
bool CheckSPIRVBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
bool CheckSPIRVBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID,
CallExpr *TheCall);
};
} // namespace clang

View File

@@ -10165,6 +10165,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());
}

View File

@@ -24,19 +24,48 @@ static constexpr int NumBuiltins =
clang::SPIRV::LastTSBuiltin - Builtin::FirstTSBuiltin;
#define GET_BUILTIN_STR_TABLE
#include "clang/Basic/BuiltinsSPIRV.inc"
#include "clang/Basic/BuiltinsSPIRVCommon.inc"
#undef GET_BUILTIN_STR_TABLE
static constexpr Builtin::Info BuiltinInfos[] = {
#define GET_BUILTIN_INFOS
#include "clang/Basic/BuiltinsSPIRV.inc"
#include "clang/Basic/BuiltinsSPIRVCommon.inc"
#undef GET_BUILTIN_INFOS
};
static_assert(std::size(BuiltinInfos) == NumBuiltins);
namespace CL {
#define GET_BUILTIN_STR_TABLE
#include "clang/Basic/BuiltinsSPIRVCL.inc"
#undef GET_BUILTIN_STR_TABLE
static constexpr Builtin::Info BuiltinInfos[] = {
#define GET_BUILTIN_INFOS
#include "clang/Basic/BuiltinsSPIRVCL.inc"
#undef GET_BUILTIN_INFOS
};
} // namespace CL
namespace VK {
#define GET_BUILTIN_STR_TABLE
#include "clang/Basic/BuiltinsSPIRVVK.inc"
#undef GET_BUILTIN_STR_TABLE
static constexpr Builtin::Info BuiltinInfos[] = {
#define GET_BUILTIN_INFOS
#include "clang/Basic/BuiltinsSPIRVVK.inc"
#undef GET_BUILTIN_INFOS
};
} // namespace VK
static_assert(std::size(BuiltinInfos) + std::size(CL::BuiltinInfos) +
std::size(VK::BuiltinInfos) ==
NumBuiltins);
llvm::SmallVector<Builtin::InfosShard>
SPIRVTargetInfo::getTargetBuiltins() const {
return {{&BuiltinStrings, BuiltinInfos}};
BaseSPIRVTargetInfo::getTargetBuiltins() const {
return {{&BuiltinStrings, BuiltinInfos},
{&VK::BuiltinStrings, VK::BuiltinInfos},
{&CL::BuiltinStrings, CL::BuiltinInfos}};
}
void SPIRTargetInfo::getTargetDefines(const LangOptions &Opts,

View File

@@ -293,6 +293,8 @@ public:
assert(Triple.isSPIRV() && "Invalid architecture for SPIR-V.");
}
llvm::SmallVector<Builtin::InfosShard> getTargetBuiltins() const override;
bool hasFeature(StringRef Feature) const override {
return Feature == "spirv";
}
@@ -321,8 +323,6 @@ public:
"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;
};

View File

@@ -122,12 +122,13 @@ static Value *EmitTargetArchBuiltinExpr(CodeGenFunction *CGF,
case llvm::Triple::riscv32:
case llvm::Triple::riscv64:
return CGF->EmitRISCVBuiltinExpr(BuiltinID, E, ReturnValue);
case llvm::Triple::spirv32:
case llvm::Triple::spirv64:
if (CGF->getTarget().getTriple().getOS() == llvm::Triple::OSType::AMDHSA)
return CGF->EmitAMDGPUBuiltinExpr(BuiltinID, E);
[[fallthrough]];
case llvm::Triple::spirv:
return CGF->EmitSPIRVBuiltinExpr(BuiltinID, E);
case llvm::Triple::spirv64:
if (CGF->getTarget().getTriple().getOS() != llvm::Triple::OSType::AMDHSA)
return nullptr;
return CGF->EmitAMDGPUBuiltinExpr(BuiltinID, E);
default:
return nullptr;
}

View File

@@ -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;
}

View File

@@ -132,6 +132,10 @@ set(riscv_files
andes_vector.h
)
set(spirv_files
__clang_spirv_builtins.h
)
set(systemz_files
s390intrin.h
vecintrin.h
@@ -319,6 +323,7 @@ set(files
${ppc_files}
${ppc_htm_files}
${riscv_files}
${spirv_files}
${systemz_files}
${ve_files}
${x86_files}
@@ -529,6 +534,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"
@@ -562,6 +568,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})
@@ -767,6 +774,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}
@@ -836,6 +849,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)

View File

@@ -0,0 +1,179 @@
/*===---- 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 */

View File

@@ -2033,7 +2033,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(TI, BuiltinID, TheCall);
return false;
case llvm::Triple::systemz:
return SystemZ().CheckSystemZBuiltinFunctionCall(BuiltinID, TheCall);
case llvm::Triple::x86:

View File

@@ -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"
@@ -5863,12 +5864,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;
}

View File

@@ -10,8 +10,21 @@
#include "clang/Sema/SemaSPIRV.h"
#include "clang/Basic/TargetBuiltins.h"
#include "clang/Basic/TargetInfo.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,8 +46,114 @@ static bool CheckAllArgsHaveSameType(Sema *S, CallExpr *TheCall) {
return false;
}
bool SemaSPIRV::CheckSPIRVBuiltinFunctionCall(unsigned BuiltinID,
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->EvaluateAsConstantExpr(Eval, SemaRef.getASTContext())) ||
!Eval.Val.isInt() || Eval.Val.getInt().getBitWidth() > 32) {
SemaRef.Diag(IntArg->getBeginLoc(), diag::err_spirv_enum_not_int)
<< 0 << IntArg->getSourceRange();
for (const PartialDiagnosticAt &PDiag : Notes)
SemaRef.Diag(PDiag.first, PDiag.second);
return true;
}
return {Eval.Val.getInt().getZExtValue()};
}
static bool checkGenericCastToPtr(Sema &SemaRef, CallExpr *Call) {
if (SemaRef.checkArgCount(Call, 2))
return true;
{
ExprResult Arg =
SemaRef.DefaultFunctionArrayLvalueConversion(Call->getArg(0));
if (Arg.isInvalid())
return true;
Call->setArg(0, Arg.get());
QualType Ty = Arg.get()->getType();
const auto *PtrTy = Ty->getAs<PointerType>();
auto AddressSpaceNotInGeneric = [&](LangAS AS) {
if (SemaRef.LangOpts.OpenCL)
return AS != LangAS::opencl_generic;
return AS != LangAS::Default;
};
if (!PtrTy ||
AddressSpaceNotInGeneric(PtrTy->getPointeeType().getAddressSpace())) {
SemaRef.Diag(Arg.get()->getBeginLoc(),
diag::err_spirv_builtin_generic_cast_invalid_arg)
<< Call->getSourceRange();
return true;
}
}
spirv::StorageClass StorageClass;
if (std::optional<int> SCInt =
processConstant32BitIntArgument(SemaRef, Call, 1);
SCInt.has_value()) {
StorageClass = static_cast<spirv::StorageClass>(SCInt.value());
if (StorageClass != spirv::StorageClass::CrossWorkgroup &&
StorageClass != spirv::StorageClass::Workgroup &&
StorageClass != spirv::StorageClass::Function) {
SemaRef.Diag(Call->getArg(1)->getBeginLoc(),
diag::err_spirv_enum_not_valid)
<< 0 << Call->getArg(1)->getSourceRange();
return true;
}
} else {
return true;
}
auto RT = Call->getArg(0)->getType();
RT = RT->getPointeeType();
auto Qual = RT.getQualifiers();
LangAS AddrSpace;
switch (static_cast<spirv::StorageClass>(StorageClass)) {
case spirv::StorageClass::CrossWorkgroup:
AddrSpace =
SemaRef.LangOpts.isSYCL() ? LangAS::sycl_global : LangAS::opencl_global;
break;
case spirv::StorageClass::Workgroup:
AddrSpace =
SemaRef.LangOpts.isSYCL() ? LangAS::sycl_local : LangAS::opencl_local;
break;
case spirv::StorageClass::Function:
AddrSpace = SemaRef.LangOpts.isSYCL() ? LangAS::sycl_private
: LangAS::opencl_private;
break;
default:
llvm_unreachable("Invalid builtin function");
}
Qual.setAddressSpace(AddrSpace);
Call->setType(SemaRef.getASTContext().getPointerType(
SemaRef.getASTContext().getQualifiedType(RT.getUnqualifiedType(), Qual)));
return false;
}
bool SemaSPIRV::CheckSPIRVBuiltinFunctionCall(const TargetInfo &TI,
unsigned BuiltinID,
CallExpr *TheCall) {
if (BuiltinID >= SPIRV::FirstVKBuiltin && BuiltinID <= SPIRV::LastVKBuiltin &&
TI.getTriple().getArch() != llvm::Triple::spirv) {
SemaRef.Diag(TheCall->getBeginLoc(), diag::err_spirv_invalid_target) << 0;
return true;
}
if (BuiltinID >= SPIRV::FirstCLBuiltin && BuiltinID <= SPIRV::LastTSBuiltin &&
TI.getTriple().getArch() != llvm::Triple::spirv32 &&
TI.getTriple().getArch() != llvm::Triple::spirv64) {
SemaRef.Diag(TheCall->getBeginLoc(), diag::err_spirv_invalid_target) << 1;
return true;
}
switch (BuiltinID) {
case SPIRV::BI__builtin_spirv_distance: {
if (SemaRef.checkArgCount(TheCall, 2))
@@ -160,6 +279,9 @@ bool SemaSPIRV::CheckSPIRVBuiltinFunctionCall(unsigned BuiltinID,
TheCall->setType(RetTy);
break;
}
case SPIRV::BI__builtin_spirv_generic_cast_to_ptr_explicit: {
return checkGenericCastToPtr(SemaRef, TheCall);
}
}
return false;
}

View File

@@ -0,0 +1,33 @@
// RUN: %clang_cc1 -O1 -triple spirv64 -fsycl-is-device %s -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -O1 -triple spirv64 -cl-std=CL3.0 -x cl %s -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -O1 -triple spirv32 -cl-std=CL3.0 -x cl %s -emit-llvm -o - | FileCheck %s
// CHECK: spir_func noundef ptr @test_cast_to_private(
// CHECK-SAME: ptr addrspace(4) noundef readnone [[P:%.*]]
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[SPV_CAST:%.*]] = tail call noundef ptr @llvm.spv.generic.cast.to.ptr.explicit.p0(ptr addrspace(4) %p)
// CHECK-NEXT: ret ptr [[SPV_CAST]]
//
__attribute__((opencl_private)) int* test_cast_to_private(int* p) {
return __builtin_spirv_generic_cast_to_ptr_explicit(p, 7);
}
// CHECK: spir_func noundef ptr addrspace(1) @test_cast_to_global(
// CHECK-SAME: ptr addrspace(4) noundef readnone [[P:%.*]]
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[SPV_CAST:%.*]] = tail call noundef ptr addrspace(1) @llvm.spv.generic.cast.to.ptr.explicit.p1(ptr addrspace(4) %p)
// CHECK-NEXT: ret ptr addrspace(1) [[SPV_CAST]]
//
__attribute__((opencl_global)) int* test_cast_to_global(int* p) {
return __builtin_spirv_generic_cast_to_ptr_explicit(p, 5);
}
// CHECK: spir_func noundef ptr addrspace(3) @test_cast_to_local(
// CHECK-SAME: ptr addrspace(4) noundef readnone [[P:%.*]]
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[SPV_CAST:%.*]] = tail call noundef ptr addrspace(3) @llvm.spv.generic.cast.to.ptr.explicit.p3(ptr addrspace(4) %p)
// CHECK-NEXT: ret ptr addrspace(3) [[SPV_CAST]]
//
__attribute__((opencl_local)) int* test_cast_to_local(int* p) {
return __builtin_spirv_generic_cast_to_ptr_explicit(p, 4);
}

View File

@@ -0,0 +1,25 @@
// RUN: %clang_cc1 -Wno-unused-value -O0 -internal-isystem %S/../../lib/Headers -include __clang_spirv_builtins.h -triple spirv64 -emit-llvm %s -fsycl-is-device -o - | FileCheck %s -check-prefixes=SPV
// RUN: %clang_cc1 -Wno-unused-value -O0 -internal-isystem %S/../../lib/Headers -include __clang_spirv_builtins.h -triple nvptx64 -emit-llvm %s -fsycl-is-device -o - | FileCheck %s -check-prefixes=NV
// SPV: void @_Z9test_castPi
// SPV: call noundef ptr addrspace(1) @llvm.spv.generic.cast.to.ptr.explicit.p1
// SPV: call noundef ptr addrspace(3) @llvm.spv.generic.cast.to.ptr.explicit.p3
// SPV: call noundef ptr @llvm.spv.generic.cast.to.ptr.explicit.p0
// SPV: addrspacecast ptr addrspace(4) %{{.*}} to ptr addrspace(1)
// SPV: addrspacecast ptr addrspace(4) %{{.*}} to ptr addrspace(3)
// SPV: addrspacecast ptr addrspace(4) %{{.*}} to ptr
// NV: void @_Z9test_castPi
// NV: call noundef ptr addrspace(1) @_Z41__spirv_GenericCastToPtrExplicit_ToGlobalPvi
// NV: call noundef ptr addrspace(3) @_Z40__spirv_GenericCastToPtrExplicit_ToLocalPvi
// NV: call noundef ptr @_Z42__spirv_GenericCastToPtrExplicit_ToPrivatePvi
// NV: addrspacecast ptr %{{.*}} to ptr addrspace(1)
// NV: addrspacecast ptr %{{.*}} to ptr addrspace(3)
void test_cast(int* p) {
__spirv_GenericCastToPtrExplicit_ToGlobal(p, 5);
__spirv_GenericCastToPtrExplicit_ToLocal(p, 4);
__spirv_GenericCastToPtrExplicit_ToPrivate(p, 7);
__spirv_GenericCastToPtr_ToGlobal(p, 5);
__spirv_GenericCastToPtr_ToLocal(p, 4);
__spirv_GenericCastToPtr_ToPrivate(p, 7);
}

View File

@@ -0,0 +1,25 @@
// RUN: %clang_cc1 -O1 -triple spirv64 -fsycl-is-device -verify %s -o -
// RUN: %clang_cc1 -O1 -triple spirv64 -verify %s -cl-std=CL3.0 -x cl -o -
// RUN: %clang_cc1 -O1 -triple spirv32 -verify %s -cl-std=CL3.0 -x cl -o -
void test_missing_arguments(int* p) {
__builtin_spirv_generic_cast_to_ptr_explicit(p);
// expected-error@-1 {{too few arguments to function call, expected 2, have 1}}
__builtin_spirv_generic_cast_to_ptr_explicit(p, 7, p);
// expected-error@-1 {{too many arguments to function call, expected 2, have 3}}
}
void test_wrong_flag_value(int* p) {
__builtin_spirv_generic_cast_to_ptr_explicit(p, 14);
// expected-error@-1 {{invalid value for storage class argument}}
}
void test_wrong_address_space(__attribute__((opencl_local)) int* p) {
__builtin_spirv_generic_cast_to_ptr_explicit(p, 14);
// expected-error@-1 {{expecting a pointer argument to the generic address space}}
}
void test_not_a_pointer(int p) {
__builtin_spirv_generic_cast_to_ptr_explicit(p, 14);
// expected-error@-1 {{expecting a pointer argument to the generic address space}}
}

View File

@@ -0,0 +1,15 @@
// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -Wno-unused-value -verify=invalid %s -o -
// RUN: %clang_cc1 -triple spirv32 -verify=valid -Wno-unused-value %s -cl-std=CL3.0 -x cl -o -
// RUN: %clang_cc1 -triple spirv64 -verify=valid -Wno-unused-value %s -cl-std=CL3.0 -x cl -o -
typedef float float2 __attribute__((ext_vector_type(2)));
// valid-no-diagnostics
void invalid_builtin_for_target(int* p) {
__builtin_spirv_generic_cast_to_ptr_explicit(p, 7);
// invalid-error@-1 {{builtin requires spirv32 or spirv64 target}}
}
// no error
float valid_builtin(float2 X) { return __builtin_spirv_length(X); }

View File

@@ -0,0 +1,15 @@
// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -Wno-unused-value -verify=valid %s -o -
// RUN: %clang_cc1 -triple spirv32 -verify=invalid -Wno-unused-value %s -cl-std=CL3.0 -x cl -o -
// RUN: %clang_cc1 -triple spirv64 -verify=invalid -Wno-unused-value %s -cl-std=CL3.0 -x cl -o -
typedef float float2 __attribute__((ext_vector_type(2)));
// valid-no-diagnostics
void call(float2 X, float2 Y) {
__builtin_spirv_reflect(X, Y);
// invalid-error@-1 {{builtin requires spirv target}}
}
// no error
float valid_builtin(float2 X) { return __builtin_spirv_length(X); }