https://github.com/Naghasan updated https://github.com/llvm/llvm-project/pull/137805
>From 020a804188b13ef881dcf1cbd81a5e11e4803d62 Mon Sep 17 00:00:00 2001 From: Victor Lomuller <vic...@codeplay.com> Date: Mon, 28 Apr 2025 16:20:09 +0100 Subject: [PATCH] [clang][SPIRV] Add builtin for OpGenericCastToPtrExplicit and its SPIR-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. --- clang/include/clang/Basic/BuiltinsSPIRV.td | 9 + .../clang/Basic/DiagnosticSemaKinds.td | 10 +- clang/lib/AST/ASTContext.cpp | 5 + clang/lib/Basic/Targets/SPIR.cpp | 2 +- clang/lib/Basic/Targets/SPIR.h | 4 +- clang/lib/CodeGen/CGBuiltin.cpp | 4 +- clang/lib/CodeGen/TargetBuiltins/SPIR.cpp | 14 ++ clang/lib/Headers/CMakeLists.txt | 16 ++ clang/lib/Headers/__clang_spirv_builtins.h | 179 ++++++++++++++++++ clang/lib/Sema/SemaChecking.cpp | 6 +- clang/lib/Sema/SemaDeclAttr.cpp | 4 +- clang/lib/Sema/SemaSPIRV.cpp | 108 +++++++++++ .../Builtins/generic_cast_to_ptr_explicit.c | 33 ++++ clang/test/Headers/spirv_functions.cpp | 25 +++ .../BuiltIns/generic_cast_to_ptr_explicit.c | 25 +++ 15 files changed, 436 insertions(+), 8 deletions(-) create mode 100644 clang/lib/Headers/__clang_spirv_builtins.h create mode 100644 clang/test/CodeGenSPIRV/Builtins/generic_cast_to_ptr_explicit.c create mode 100644 clang/test/Headers/spirv_functions.cpp create mode 100644 clang/test/SemaSPIRV/BuiltIns/generic_cast_to_ptr_explicit.c 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..fe13e4ee25a36 100644 --- a/clang/lib/Basic/Targets/SPIR.cpp +++ b/clang/lib/Basic/Targets/SPIR.cpp @@ -35,7 +35,7 @@ static constexpr Builtin::Info BuiltinInfos[] = { static_assert(std::size(BuiltinInfos) == NumBuiltins); llvm::SmallVector<Builtin::InfosShard> -SPIRVTargetInfo::getTargetBuiltins() const { +BaseSPIRVTargetInfo::getTargetBuiltins() const { return {{&BuiltinStrings, BuiltinInfos}}; } diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h index bf249e271a870..0176bf223a3aa 100644 --- a/clang/lib/Basic/Targets/SPIR.h +++ b/clang/lib/Basic/Targets/SPIR.h @@ -293,6 +293,8 @@ class LLVM_LIBRARY_VISIBILITY BaseSPIRVTargetInfo : public BaseSPIRTargetInfo { 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 @@ 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..85309186b2e9a 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; + 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..e344ed52571a7 --- /dev/null +++ b/clang/lib/Headers/__clang_spirv_builtins.h @@ -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 */ 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..d5b37e0b8aff6 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,99 @@ 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->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(unsigned BuiltinID, CallExpr *TheCall) { switch (BuiltinID) { @@ -160,6 +265,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; } diff --git a/clang/test/CodeGenSPIRV/Builtins/generic_cast_to_ptr_explicit.c b/clang/test/CodeGenSPIRV/Builtins/generic_cast_to_ptr_explicit.c new file mode 100644 index 0000000000000..8cfe650f4db10 --- /dev/null +++ b/clang/test/CodeGenSPIRV/Builtins/generic_cast_to_ptr_explicit.c @@ -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); +} diff --git a/clang/test/Headers/spirv_functions.cpp b/clang/test/Headers/spirv_functions.cpp new file mode 100644 index 0000000000000..ff036b75faf02 --- /dev/null +++ b/clang/test/Headers/spirv_functions.cpp @@ -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); +} diff --git a/clang/test/SemaSPIRV/BuiltIns/generic_cast_to_ptr_explicit.c b/clang/test/SemaSPIRV/BuiltIns/generic_cast_to_ptr_explicit.c new file mode 100644 index 0000000000000..5a839961e20f5 --- /dev/null +++ b/clang/test/SemaSPIRV/BuiltIns/generic_cast_to_ptr_explicit.c @@ -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}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits