Skip to content

Commit 22318ba

Browse files
authored
[SYCL] Prohibit taking address of non-indirectly callable functions (#5151)
Emit a error when address of function not marked with `[[intel::device_indirectly_callable]]` attribute is taken. The error is emitted only if flag `-fsycl-allow-func-ptr` if passed.
1 parent 3b3faf4 commit 22318ba

File tree

5 files changed

+181
-8
lines changed

5 files changed

+181
-8
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11627,6 +11627,10 @@ def err_sycl_expected_finalize_method : Error<
1162711627
def ext_sycl_2020_attr_spelling : ExtWarn<
1162811628
"use of attribute %0 is a SYCL 2020 extension">,
1162911629
InGroup<Sycl2017Compat>;
11630+
def err_sycl_taking_address_of_wrong_function : Error<
11631+
"taking address of a function not marked with "
11632+
"'intel::device_indirectly_callable' attribute is not allowed in SYCL device "
11633+
"code">;
1163011634

1163111635
// errors of expect.with.probability
1163211636
def err_probability_not_constant_float : Error<

clang/lib/Sema/SemaOverload.cpp

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,11 +1807,28 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
18071807
// Function-to-pointer conversion (C++ 4.3).
18081808
SCS.First = ICK_Function_To_Pointer;
18091809

1810-
if (auto *DRE = dyn_cast<DeclRefExpr>(From->IgnoreParenCasts()))
1811-
if (auto *FD = dyn_cast<FunctionDecl>(DRE->getDecl()))
1810+
if (auto *DRE = dyn_cast<DeclRefExpr>(From->IgnoreParenCasts())) {
1811+
if (auto *FD = dyn_cast<FunctionDecl>(DRE->getDecl())) {
18121812
if (!S.checkAddressOfFunctionIsAvailable(FD))
18131813
return false;
18141814

1815+
// Some parts of clang are not designed for deferred diagnostics.
1816+
// One of the examples - initialization. When a new initialization is
1817+
// performed - it may end up here checking validity of a conversion.
1818+
// If false is returned from here, initialization sequence is marked as
1819+
// invalid, then checkAddressOfFunctionIsAvailable is called again
1820+
// to understand the reason of invaid initialization and in the end
1821+
// it is called with 'Complain' parameter to emit diagnostics.
1822+
// We cannot mark an initialization permanently invalid for SYCL device,
1823+
// because we may not know yet where the device code is.
1824+
// So, just call 'checkAddressOfFunctionIsAvailable' again but with
1825+
// 'Complain' parameter to issue a deferred diagnostic.
1826+
if (S.getLangOpts().SYCLIsDevice)
1827+
S.checkAddressOfFunctionIsAvailable(FD, /*Complain=*/true,
1828+
DRE->getExprLoc());
1829+
}
1830+
}
1831+
18151832
// An lvalue of function type T can be converted to an rvalue of
18161833
// type "pointer to T." The result is a pointer to the
18171834
// function. (C++ 4.3p1).
@@ -10243,6 +10260,15 @@ static bool checkAddressOfFunctionIsAvailable(Sema &S, const FunctionDecl *FD,
1024310260
bool Complain,
1024410261
bool InOverloadResolution,
1024510262
SourceLocation Loc) {
10263+
if (Complain && S.getLangOpts().SYCLIsDevice &&
10264+
S.getLangOpts().SYCLAllowFuncPtr) {
10265+
if (!FD->hasAttr<SYCLDeviceIndirectlyCallableAttr>()) {
10266+
S.SYCLDiagIfDeviceCode(Loc,
10267+
diag::err_sycl_taking_address_of_wrong_function,
10268+
Sema::DeviceDiagnosticReason::Sycl);
10269+
}
10270+
}
10271+
1024610272
if (!isFunctionAlwaysEnabled(S.Context, FD)) {
1024710273
if (Complain) {
1024810274
if (InOverloadResolution)

clang/test/CodeGenSYCL/invoke-function-addrspace.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
using namespace cl::sycl;
99
queue q;
1010

11+
// CHECK: define dso_local spir_func i32 @{{.*}}bar10{{.*}}()
12+
[[intel::device_indirectly_callable]] int bar10() { return 10; }
13+
1114
// CHECK: define linkonce_odr spir_func i32 @{{.*}}invoke_function{{.*}}(i32 () addrspace(4)* %f)
1215
template <typename Callable>
1316
auto invoke_function(Callable &&f) {
@@ -19,9 +22,6 @@ auto invoke_function(Callable &&f) {
1922
return f();
2023
}
2124

22-
// CHECK: define dso_local spir_func i32 @{{.*}}bar10{{.*}}()
23-
int bar10() { return 10; }
24-
2525
int main() {
2626
kernel_single_task<class KernelName>(
2727
[=]() {

clang/test/SemaSYCL/sycl-restrict.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ struct trickyStruct {
189189

190190
// function return type and argument both unsupported
191191
// expected-note@+1 2{{'commitInfraction' defined here}}
192-
__int128 commitInfraction(__int128 a) {
192+
[[intel::device_indirectly_callable]] __int128 commitInfraction(__int128 a) {
193193
return 0;
194194
}
195195

@@ -403,8 +403,7 @@ int moar_globals = 5;
403403
template<const auto &T>
404404
int uses_global(){}
405405

406-
407-
int addInt(int n, int m) {
406+
[[intel::device_indirectly_callable]] int addInt(int n, int m) {
408407
return n + m;
409408
}
410409

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// RUN: %clang_cc1 -fsycl-is-device -fsycl-allow-func-ptr -internal-isystem %S/Inputs -fsyntax-only -verify -sycl-std=2020 -std=c++17 %s
2+
3+
#include "sycl.hpp"
4+
5+
int badFoo(int P) {
6+
return P + 2;
7+
}
8+
9+
[[intel::device_indirectly_callable]] int goodFoo(int P) {
10+
return P + 2;
11+
}
12+
13+
SYCL_EXTERNAL float externalBadFoo(int P);
14+
[[intel::device_indirectly_callable]] unsigned externalGoodFoo(int P);
15+
16+
sycl::queue myQueue;
17+
18+
SYCL_EXTERNAL int runFn(int (&)(int));
19+
SYCL_EXTERNAL int runFn1(int (*)(int));
20+
21+
struct ForMembers {
22+
[[intel::device_indirectly_callable]] int goodMember(int) { return 1; }
23+
int badMember(int) { return 2; }
24+
25+
static int badStaticMember(int) { return 2; }
26+
};
27+
28+
template <typename Fn, typename... Args> void templateCaller(Fn F, Args... As) {
29+
F(As...);
30+
}
31+
32+
template <auto Fn, typename... Args> void templateCaller1(Args... As) {
33+
// expected-error@+1 2{{taking address of a function not marked with 'intel::device_indirectly_callable' attribute is not allowed in SYCL device code}}
34+
Fn(As...);
35+
// expected-error@+1 2{{taking address of a function not marked with 'intel::device_indirectly_callable' attribute is not allowed in SYCL device code}}
36+
runFn(*Fn);
37+
}
38+
39+
void basicUsage() {
40+
// expected-error@+1 {{taking address of a function not marked with 'intel::device_indirectly_callable' attribute is not allowed in SYCL device code}}
41+
int (*p)(int) = &badFoo;
42+
// expected-error@+1 {{taking address of a function not marked with 'intel::device_indirectly_callable' attribute is not allowed in SYCL device code}}
43+
int (*p2)(int) = badFoo;
44+
}
45+
46+
template <typename T> void templatedContext() {
47+
48+
// FIXME: this is likely not diagnosed because of a common problem among
49+
// deferred diagnostics. They don't work from templated context if the
50+
// problematic code doesn't depend on a template parameter. See
51+
// https://github.com/intel/llvm/pull/5114 for an explanation of the problem
52+
// and possible solution.
53+
int (*p)(int) = &badFoo;
54+
auto p1 = &ForMembers::badMember;
55+
56+
// expected-note@+1 {{called by 'templatedContext<int>'}}
57+
templateCaller1<badFoo>(1);
58+
}
59+
60+
int main() {
61+
62+
myQueue.submit([&](sycl::handler &h) {
63+
// expected-note@#KernelSingleTaskKernelFuncCall 2{{called by 'kernel_single_task<Basic}}
64+
h.single_task<class Basic>(
65+
[=]() {
66+
// expected-error@+1 {{taking address of a function not marked with 'intel::device_indirectly_callable' attribute is not allowed in SYCL device code}}
67+
int (*p)(int) = &badFoo;
68+
// expected-error@+1 {{taking address of a function not marked with 'intel::device_indirectly_callable' attribute is not allowed in SYCL device code}}
69+
int (*p2)(int) = badFoo;
70+
71+
// OK
72+
int (*p3)(int) = &goodFoo;
73+
int (*p4)(int) = goodFoo;
74+
75+
// expected-error@+1 {{taking address of a function not marked with 'intel::device_indirectly_callable' attribute is not allowed in SYCL device code}}
76+
auto p5 = &externalBadFoo;
77+
auto *p6 = &externalGoodFoo;
78+
79+
// Make sure that assignment is diagnosed correctly;
80+
int (*a)(int);
81+
// expected-error@+1 {{taking address of a function not marked with 'intel::device_indirectly_callable' attribute is not allowed in SYCL device code}}
82+
a = badFoo;
83+
// expected-error@+1 {{taking address of a function not marked with 'intel::device_indirectly_callable' attribute is not allowed in SYCL device code}}
84+
a = &badFoo;
85+
86+
a = goodFoo;
87+
a = &goodFoo;
88+
89+
// expected-error@+1 {{taking address of a function not marked with 'intel::device_indirectly_callable' attribute is not allowed in SYCL device code}}
90+
constexpr auto b = badFoo;
91+
// expected-error@+1 {{taking address of a function not marked with 'intel::device_indirectly_callable' attribute is not allowed in SYCL device code}}
92+
constexpr auto c = &badFoo;
93+
// expected-note@+1 {{called by 'operator()'}}
94+
basicUsage();
95+
});
96+
});
97+
98+
myQueue.submit([&](sycl::handler &h) {
99+
// expected-note@#KernelSingleTaskKernelFuncCall {{called by 'kernel_single_task<Members}}
100+
h.single_task<class Members>(
101+
[=]() {
102+
// expected-error@+1 {{taking address of a function not marked with 'intel::device_indirectly_callable' attribute is not allowed in SYCL device code}}
103+
auto p = &ForMembers::badMember;
104+
auto p1 = &ForMembers::goodMember;
105+
// expected-error@+1 {{taking address of a function not marked with 'intel::device_indirectly_callable' attribute is not allowed in SYCL device code}}
106+
auto *p2 = &ForMembers::badStaticMember;
107+
});
108+
});
109+
110+
myQueue.submit([&](sycl::handler &h) {
111+
// expected-note@#KernelSingleTaskKernelFuncCall 2{{called by 'kernel_single_task<RunVia}}
112+
h.single_task<class RunVia>(
113+
[=]() {
114+
// expected-error@+1 {{taking address of a function not marked with 'intel::device_indirectly_callable' attribute is not allowed in SYCL device code}}
115+
int baz = runFn(badFoo);
116+
117+
baz = runFn(goodFoo);
118+
119+
// expected-error@+1 2{{taking address of a function not marked with 'intel::device_indirectly_callable' attribute is not allowed in SYCL device code}}
120+
baz = runFn1(badFoo);
121+
122+
baz = runFn1(goodFoo);
123+
124+
// expected-error@+1 2{{taking address of a function not marked with 'intel::device_indirectly_callable' attribute is not allowed in SYCL device code}}
125+
templateCaller(badFoo, 2);
126+
templateCaller(goodFoo, 1);
127+
128+
templateCaller1<goodFoo>(1);
129+
130+
// expected-note@+2 {{called by 'operator()'}}
131+
// expected-error@+1 {{taking address of a function not marked with 'intel::device_indirectly_callable' attribute is not allowed in SYCL device code}}
132+
templateCaller1<badFoo>(1);
133+
});
134+
});
135+
myQueue.submit([&](sycl::handler &h) {
136+
// expected-note@#KernelSingleTaskKernelFuncCall {{called by 'kernel_single_task<RunTemplatedContext}}
137+
h.single_task<class RunTemplatedContext>(
138+
[=]() {
139+
// expected-note@+1 {{called by 'operator()'}}
140+
templatedContext<int>();
141+
});
142+
});
143+
return 0;
144+
}

0 commit comments

Comments
 (0)