Skip to content

Commit 960f71a

Browse files
authored
[SYCL] Add support for free function kernel builtins (#15938)
Support for the following three builtins are added: __builtin_sycl_is_single_task_kernel(function-pointer) __builtin_sycl_is_nd_range_kernel(function-pointer, dimension) __builtin_sycl_is_kernel(function-pointer) __builtin_sycl_is_single_task_kernel returns true if the function pointer represents a function with the IR attribute "sycl-single-task-kernel". __builtin_sycl_is_nd_range_kernel returns true if the function pointer represents a function with the IR attribute "sycl-single-task-kernel" with a matching dimension. __builtin_sycl_is_kernel returns true if the function pointer represents a function with either of the two IR attributes.
1 parent cbd6b7c commit 960f71a

13 files changed

+688
-1
lines changed

clang/include/clang/Basic/Builtins.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ enum LanguageID : uint16_t {
4343
OCL_DSE = 0x400, // builtin requires OpenCL device side enqueue.
4444
ALL_OCL_LANGUAGES = 0x800, // builtin for OCL languages.
4545
HLSL_LANG = 0x1000, // builtin requires HLSL.
46+
SYCL_LANG = 0x2000, // builtin requires SYCL.
4647
ALL_LANGUAGES = C_LANG | CXX_LANG | OBJC_LANG, // builtin for all languages.
4748
ALL_GNU_LANGUAGES = ALL_LANGUAGES | GNU_LANG, // builtin requires GNU mode.
4849
ALL_MS_LANGUAGES = ALL_LANGUAGES | MS_LANG // builtin requires MS mode.

clang/include/clang/Basic/Builtins.td

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4731,6 +4731,25 @@ def GetDeviceSideMangledName : LangBuiltin<"CUDA_LANG"> {
47314731
let Prototype = "char const*(...)";
47324732
}
47334733

4734+
// SYCL
4735+
def SYCLIsKernel : LangBuiltin<"SYCL_LANG"> {
4736+
let Spellings = ["__builtin_sycl_is_kernel"];
4737+
let Attributes = [NoThrow, Const, Constexpr, CustomTypeChecking];
4738+
let Prototype = "bool(...)";
4739+
}
4740+
4741+
def SYCLIsSingleTaskKernel : LangBuiltin<"SYCL_LANG"> {
4742+
let Spellings = ["__builtin_sycl_is_single_task_kernel"];
4743+
let Attributes = [NoThrow, Const, Constexpr, CustomTypeChecking];
4744+
let Prototype = "bool(...)";
4745+
}
4746+
4747+
def SYCLIsNDRangeKernel : LangBuiltin<"SYCL_LANG"> {
4748+
let Spellings = ["__builtin_sycl_is_nd_range_kernel"];
4749+
let Attributes = [NoThrow, Const, Constexpr, CustomTypeChecking];
4750+
let Prototype = "bool(...)";
4751+
}
4752+
47344753
// HLSL
47354754
def HLSLAll : LangBuiltin<"HLSL_LANG"> {
47364755
let Spellings = ["__builtin_hlsl_all"];

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,9 @@ def warn_unreachable_association : Warning<
827827
InGroup<UnreachableCodeGenericAssoc>;
828828

829829
/// Built-in functions.
830+
def err_builtin_invalid_argument_count : Error<
831+
"builtin %plural{0:takes no arguments|1:takes one argument|"
832+
":requires exactly %0 arguments}0">;
830833
def ext_implicit_lib_function_decl : ExtWarn<
831834
"implicitly declaring library function '%0' with type %1">,
832835
InGroup<ImplicitFunctionDeclare>;
@@ -12397,7 +12400,11 @@ def err_builtin_invalid_arg_type: Error <
1239712400
"a vector of integers|"
1239812401
"an unsigned integer|"
1239912402
"an 'int'|"
12400-
"a vector of floating points}1 (was %2)">;
12403+
"a vector of floating points|"
12404+
"a function pointer}1 (was %2)">;
12405+
12406+
def err_builtin_invalid_arg_value: Error<
12407+
"%ordinal0 argument must be a strictly positive value (was %1)">;
1240112408

1240212409
def err_builtin_matrix_disabled: Error<
1240312410
"matrix types extension is disabled. Pass -fenable-matrix to enable it">;

clang/lib/AST/ExprConstant.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12729,6 +12729,45 @@ static bool getBuiltinAlignArguments(const CallExpr *E, EvalInfo &Info,
1272912729
return true;
1273012730
}
1273112731

12732+
static bool isSYCLFreeFunctionKernel(IntExprEvaluator &IEV,
12733+
const EvalInfo &Info, const CallExpr *E,
12734+
StringRef NameStr1, StringRef NameStr2,
12735+
bool CheckNDRangeKernelDim = false) {
12736+
const Expr *ArgExpr = E->getArg(0)->IgnoreParenImpCasts();
12737+
while (isa<CastExpr>(ArgExpr))
12738+
ArgExpr = cast<CastExpr>(ArgExpr)->getSubExpr();
12739+
auto *DRE = dyn_cast<DeclRefExpr>(ArgExpr);
12740+
if (DRE) {
12741+
const FunctionDecl *FD = dyn_cast<FunctionDecl>(DRE->getDecl());
12742+
if (FD) {
12743+
auto *SAIRAttr = FD->getAttr<SYCLAddIRAttributesFunctionAttr>();
12744+
if (!SAIRAttr)
12745+
return IEV.Success(false, E);
12746+
SmallVector<std::pair<std::string, std::string>, 4> NameValuePairs =
12747+
SAIRAttr->getFilteredAttributeNameValuePairs(Info.Ctx);
12748+
for (const auto &NVPair : NameValuePairs) {
12749+
if (!NVPair.first.compare(NameStr1) ||
12750+
(!NameStr2.empty() && !NVPair.first.compare(NameStr2))) {
12751+
if (CheckNDRangeKernelDim) {
12752+
uint64_t Dim =
12753+
E->getArg(1)->EvaluateKnownConstInt(Info.Ctx).getZExtValue();
12754+
// Return true only if the dimensions match.
12755+
if (std::stoul(NVPair.second) == Dim)
12756+
return IEV.Success(true, E);
12757+
else
12758+
return IEV.Success(false, E);
12759+
}
12760+
// Return true if it has the sycl-single-task-kernel or the
12761+
// sycl-nd-range-kernel attribute.
12762+
return IEV.Success(true, E);
12763+
}
12764+
}
12765+
return IEV.Success(false, E);
12766+
}
12767+
}
12768+
return false;
12769+
}
12770+
1273212771
bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
1273312772
unsigned BuiltinOp) {
1273412773
switch (BuiltinOp) {
@@ -13674,6 +13713,19 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
1367413713
Result.setBitVal(P++, Val[I]);
1367513714
return Success(Result, E);
1367613715
}
13716+
13717+
case Builtin::BI__builtin_sycl_is_kernel: {
13718+
return isSYCLFreeFunctionKernel(*this, Info, E, "sycl-single-task-kernel",
13719+
"sycl-nd-range-kernel");
13720+
}
13721+
case Builtin::BI__builtin_sycl_is_single_task_kernel: {
13722+
return isSYCLFreeFunctionKernel(*this, Info, E, "sycl-single-task-kernel",
13723+
"");
13724+
}
13725+
case Builtin::BI__builtin_sycl_is_nd_range_kernel: {
13726+
return isSYCLFreeFunctionKernel(*this, Info, E, "sycl-nd-range-kernel", "",
13727+
/*CheckNDRangeDim=*/true);
13728+
}
1367713729
}
1367813730
}
1367913731

clang/lib/Basic/Builtins.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ static bool builtinIsSupported(const Builtin::Info &BuiltinInfo,
119119
/* CUDA Unsupported */
120120
if (!LangOpts.CUDA && BuiltinInfo.Langs == CUDA_LANG)
121121
return false;
122+
/* SYCL Unsupported */
123+
if (!LangOpts.isSYCL() && BuiltinInfo.Langs == SYCL_LANG)
124+
return false;
122125
/* CPlusPlus Unsupported */
123126
if (!LangOpts.CPlusPlus && BuiltinInfo.Langs == CXX_LANG)
124127
return false;

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2716,6 +2716,53 @@ static RValue EmitHipStdParUnsupportedBuiltin(CodeGenFunction *CGF,
27162716
return RValue::get(CGF->Builder.CreateCall(UBF, Args));
27172717
}
27182718

2719+
static RValue EmitSYCLFreeFunctionKernelBuiltin(CodeGenFunction &CGF,
2720+
const CallExpr *E,
2721+
StringRef NameStr1,
2722+
StringRef NameStr2,
2723+
bool CheckNDRangeDim = false) {
2724+
const Expr *ArgExpr = E->getArg(0)->IgnoreImpCasts();
2725+
auto *UO = dyn_cast<clang::UnaryOperator>(ArgExpr);
2726+
// If this is of the form &function or *function, get to the function
2727+
// sub-expression.
2728+
if (UO && (UO->getOpcode() == UO_AddrOf || UO->getOpcode() == UO_Deref))
2729+
ArgExpr = UO->getSubExpr()->IgnoreParenImpCasts();
2730+
while (isa<CastExpr>(ArgExpr))
2731+
ArgExpr = cast<CastExpr>(ArgExpr)->getSubExpr();
2732+
auto *DRE = dyn_cast<DeclRefExpr>(ArgExpr);
2733+
if (DRE) {
2734+
const FunctionDecl *FD = dyn_cast<FunctionDecl>(DRE->getDecl());
2735+
if (FD && FD->hasAttr<SYCLAddIRAttributesFunctionAttr>()) {
2736+
auto *SAIRAttr = FD->getAttr<SYCLAddIRAttributesFunctionAttr>();
2737+
SmallVector<std::pair<std::string, std::string>, 4> NameValuePairs =
2738+
SAIRAttr->getFilteredAttributeNameValuePairs(CGF.CGM.getContext());
2739+
for (const auto &NVPair : NameValuePairs) {
2740+
if (!NVPair.first.compare(NameStr1) ||
2741+
(!NameStr2.empty() && !!NVPair.first.compare(NameStr2))) {
2742+
if (CheckNDRangeDim) {
2743+
uint64_t Dim = E->getArg(1)
2744+
->EvaluateKnownConstInt(CGF.CGM.getContext())
2745+
.getZExtValue();
2746+
// Return true only if the dimensions match.
2747+
if (std::stoul(NVPair.second) == Dim)
2748+
return RValue::get(
2749+
llvm::ConstantInt::getTrue(CGF.ConvertType(E->getType())));
2750+
else
2751+
return RValue::get(
2752+
llvm::ConstantInt::getFalse(CGF.ConvertType(E->getType())));
2753+
}
2754+
// Return true if the kernel type matches.
2755+
return RValue::get(
2756+
llvm::ConstantInt::getTrue(CGF.ConvertType(E->getType())));
2757+
}
2758+
}
2759+
}
2760+
}
2761+
// Return false otherwise.
2762+
return RValue::get(
2763+
llvm::ConstantInt::getFalse(CGF.ConvertType(E->getType())));
2764+
}
2765+
27192766
RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
27202767
const CallExpr *E,
27212768
ReturnValueSlot ReturnValue) {
@@ -6363,6 +6410,18 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
63636410
auto Str = CGM.GetAddrOfConstantCString(Name, "");
63646411
return RValue::get(Str.getPointer());
63656412
}
6413+
case Builtin::BI__builtin_sycl_is_kernel: {
6414+
return EmitSYCLFreeFunctionKernelBuiltin(
6415+
*this, E, "sycl-single-task-kernel", "sycl-nd-range-kernel");
6416+
}
6417+
case Builtin::BI__builtin_sycl_is_single_task_kernel: {
6418+
return EmitSYCLFreeFunctionKernelBuiltin(*this, E,
6419+
"sycl-single-task-kernel", "");
6420+
}
6421+
case Builtin::BI__builtin_sycl_is_nd_range_kernel: {
6422+
return EmitSYCLFreeFunctionKernelBuiltin(*this, E, "sycl-nd-range-kernel",
6423+
"", /*CheckNDRangeDim=*/true);
6424+
}
63666425
}
63676426

63686427
// If this is an alias for a lib function (e.g. __builtin_sin), emit

clang/lib/Sema/SemaChecking.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3026,6 +3026,40 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
30263026
}
30273027
break;
30283028
}
3029+
3030+
case Builtin::BI__builtin_sycl_is_kernel:
3031+
case Builtin::BI__builtin_sycl_is_single_task_kernel:
3032+
case Builtin::BI__builtin_sycl_is_nd_range_kernel: {
3033+
unsigned int ExpNumArgs =
3034+
BuiltinID == Builtin::BI__builtin_sycl_is_nd_range_kernel ? 2 : 1;
3035+
// Builtin takes either 1 or 2 arguments.
3036+
if (TheCall->getNumArgs() != ExpNumArgs) {
3037+
Diag(TheCall->getBeginLoc(), diag::err_builtin_invalid_argument_count)
3038+
<< ExpNumArgs;
3039+
return ExprError();
3040+
}
3041+
3042+
const Expr *Arg = TheCall->getArg(0);
3043+
QualType ArgTy = Arg->getType();
3044+
3045+
if (!ArgTy->isFunctionProtoType() && !ArgTy->isFunctionPointerType()) {
3046+
Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
3047+
<< 1 << /* pointer to function type */ 10 << ArgTy;
3048+
return ExprError();
3049+
}
3050+
3051+
if (ExpNumArgs == 2) {
3052+
int64_t DimArg =
3053+
TheCall->getArg(1)->EvaluateKnownConstInt(Context).getSExtValue();
3054+
if (DimArg <= 0) {
3055+
Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_value)
3056+
<< 2 << DimArg;
3057+
return ExprError();
3058+
}
3059+
}
3060+
3061+
break;
3062+
}
30293063
}
30303064

30313065
if (getLangOpts().HLSL && HLSL().CheckBuiltinFunctionCall(BuiltinID, TheCall))
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// RUN: %clang_cc1 -internal-isystem %S/Inputs -fsycl-is-device -triple spir64-unknown-unknown -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s
2+
3+
// This test tests the builtin __builtin_sycl_is_kernel
4+
5+
#include "sycl.hpp"
6+
7+
using namespace sycl;
8+
queue q;
9+
10+
__attribute__((sycl_device))
11+
[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 0)]]
12+
void sstk_free_func() {
13+
}
14+
15+
template <typename T>
16+
[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 4)]]
17+
void sstk_free_func_tmpl(T *ptr) {
18+
for (int i = 0; i <= 7; i++)
19+
ptr[i] = i + 11;
20+
}
21+
22+
__attribute__((sycl_device))
23+
[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 1)]]
24+
void ovl_free_func(int *ptr) {
25+
for (int i = 0; i <= 7; i++)
26+
ptr[i] = i;
27+
}
28+
29+
__attribute__((sycl_device))
30+
[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 1)]]
31+
void ovl_free_func(int *ptr, int val) {
32+
for (int i = 0; i <= 7; i++)
33+
ptr[i] = i + val;
34+
}
35+
36+
__attribute__((sycl_device))
37+
[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 4)]]
38+
void sndrk_free_func() {
39+
}
40+
41+
void func() {}
42+
43+
void foo() {
44+
bool b1 = __builtin_sycl_is_kernel(sstk_free_func);
45+
// CHECK: store i8 1, ptr addrspace(4) %b1{{.*}}, align 1
46+
bool b2 = __builtin_sycl_is_kernel(*sstk_free_func);
47+
// CHECK: store i8 1, ptr addrspace(4) %b2{{.*}}, align 1
48+
bool b3 = __builtin_sycl_is_kernel(&sstk_free_func);
49+
// CHECK: store i8 1, ptr addrspace(4) %b3{{.*}}, align 1
50+
bool b4 = __builtin_sycl_is_kernel(func);
51+
// CHECK: store i8 0, ptr addrspace(4) %b4{{.*}}, align 1
52+
bool b5 = __builtin_sycl_is_kernel(sndrk_free_func);
53+
// CHECK: store i8 1, ptr addrspace(4) %b5{{.*}}, align 1
54+
55+
// Constexpr forms of the valid cases.
56+
constexpr bool b6 = __builtin_sycl_is_kernel(sstk_free_func); // Okay and true.
57+
// CHECK: store i8 1, ptr addrspace(4) %b6{{.*}}, align 1
58+
constexpr bool b7 = __builtin_sycl_is_kernel(func); // Okay, but false.
59+
// CHECK: store i8 0, ptr addrspace(4) %b7{{.*}}, align 1
60+
constexpr bool b8 = __builtin_sycl_is_kernel(sndrk_free_func); // Okay, but false.
61+
// CHECK: store i8 1, ptr addrspace(4) %b8{{.*}}, align 1
62+
63+
// Test function template.
64+
constexpr bool b9 = __builtin_sycl_is_kernel((void(*)(int *))sstk_free_func_tmpl<int>); // Okay
65+
// CHECK: store i8 1, ptr addrspace(4) %b9{{.*}}, align 1
66+
67+
// Test overloaded functions.
68+
constexpr bool b10 = __builtin_sycl_is_kernel((void(*)(int *))ovl_free_func); // Okay
69+
// CHECK: store i8 1, ptr addrspace(4) %b10{{.*}}, align 1
70+
constexpr bool b11 = __builtin_sycl_is_kernel((void(*)(int *, int))ovl_free_func); // Okay
71+
// CHECK: store i8 1, ptr addrspace(4) %b11{{.*}}, align 1
72+
}
73+
74+
void f() {
75+
auto L = []() { foo(); };
76+
q.submit([&](handler &h) {
77+
h.single_task<class kernel_name_1>(L);
78+
});
79+
}

0 commit comments

Comments
 (0)