Skip to content

Commit 54dd232

Browse files
[SYCL] Turn indirectly-callable property into sycl_device attribute (#13819)
This PR is a part of virtual functions feature being designed in #10540. One aspects of the proposed language specification is to be able to mark functions as device functions using SYCL compile-time properties mechanism. Compile-time properties are lowered into `[[__sycl_detail__::add_ir_attribute_function()]]` attribute and this patch introduces extra processing for the attribute: property names passed to the attribute are analyzed so we can generate other attributes based on those strings. The only example of such property/attribute pair is `indirectly-callable` string that indicates that a function with such property should be treated as if it had `sycl_device` attribute attached to it and we do exactly that implicitly now. Note that there were changes required to the attribute parsing logic: because we now access its argument way earlier than we used to, that logic had to be improved to be more robust and pass existing `SemaSYCL/attr-add-ir-attributes.cpp` test.
1 parent a392f07 commit 54dd232

File tree

4 files changed

+208
-5
lines changed

4 files changed

+208
-5
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1768,6 +1768,30 @@ class SYCLAddIRAttrMemberCodeHolder<code Code> {
17681768

17691769
// Common class for SYCL add_ir_attributes_* attributes.
17701770
def SYCLAddIRAttrCommonMembers : SYCLAddIRAttrMemberCodeHolder<[{
1771+
static std::optional<std::string>
1772+
getValidAttributeNameAsString(const APValue &Value,
1773+
const ASTContext &Context,
1774+
QualType ValueQType) {
1775+
assert(!Value.isLValue());
1776+
if (ValueQType->isCharType()) {
1777+
char C = static_cast<char>(Value.getInt().getExtValue());
1778+
return std::string(&C, 1);
1779+
}
1780+
if (ValueQType->isArrayType() &&
1781+
(ValueQType->getArrayElementTypeNoTypeQual()->isCharType())) {
1782+
SmallString<10> StrBuffer;
1783+
for (unsigned I = 0; I < Value.getArrayInitializedElts(); ++I) {
1784+
const APValue &ArrayElem = Value.getArrayInitializedElt(I);
1785+
char C = static_cast<char>(ArrayElem.getInt().getExtValue());
1786+
if (C == '\0')
1787+
break;
1788+
StrBuffer += C;
1789+
}
1790+
return std::string(StrBuffer);
1791+
}
1792+
return std::nullopt;
1793+
}
1794+
17711795
static std::optional<std::string>
17721796
getValidAttributeNameAsString(const Expr *NameE, const ASTContext &Context) {
17731797
if (const auto *NameLiteral = dyn_cast<StringLiteral>(NameE))
@@ -1782,7 +1806,8 @@ def SYCLAddIRAttrCommonMembers : SYCLAddIRAttrMemberCodeHolder<[{
17821806
NameLValue = NameCE->getAPValueResult();
17831807

17841808
if (!NameLValue.isLValue())
1785-
return std::nullopt;
1809+
return getValidAttributeNameAsString(NameLValue, Context,
1810+
NameCE->getType());
17861811

17871812
if (const auto *NameValExpr =
17881813
NameLValue.getLValueBase().dyn_cast<const Expr *>())
@@ -1816,10 +1841,10 @@ def SYCLAddIRAttrCommonMembers : SYCLAddIRAttrMemberCodeHolder<[{
18161841
ValueQType->getArrayElementTypeNoTypeQual()
18171842
->isIntegralOrEnumerationType())) {
18181843
SmallString<10> StrBuffer;
1819-
for (unsigned I = 0; I < Value.getArraySize(); ++I) {
1844+
for (unsigned I = 0; I < Value.getArrayInitializedElts(); ++I) {
18201845
const APValue &ArrayElem = Value.getArrayInitializedElt(I);
18211846
char C = static_cast<char>(ArrayElem.getInt().getExtValue());
1822-
if (C == 0)
1847+
if (C == '\0')
18231848
break;
18241849
StrBuffer += C;
18251850
}
@@ -1859,7 +1884,7 @@ def SYCLAddIRAttrCommonMembers : SYCLAddIRAttrMemberCodeHolder<[{
18591884
SmallString<10> StrBuffer;
18601885
for (const auto *InitE : InitListE->inits()) {
18611886
const Expr *InitNoImpCastE = InitE->IgnoreParenImpCasts();
1862-
char C = 0;
1887+
char C = '\0';
18631888
if (const auto *CharacterVal =
18641889
dyn_cast<CharacterLiteral>(InitNoImpCastE))
18651890
C = static_cast<char>(CharacterVal->getValue());
@@ -1870,7 +1895,7 @@ def SYCLAddIRAttrCommonMembers : SYCLAddIRAttrMemberCodeHolder<[{
18701895
return std::nullopt;
18711896

18721897
// Null terminator will end the string reading.
1873-
if (C == 0)
1898+
if (C == '\0')
18741899
break;
18751900

18761901
StrBuffer += C;

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8712,6 +8712,32 @@ void Sema::AddSYCLAddIRAttributesFunctionAttr(Decl *D,
87128712
CI))
87138713
return;
87148714
D->addAttr(Attr);
8715+
8716+
// There are compile-time SYCL properties which we would like to turn into
8717+
// attributes to enable compiler diagnostics.
8718+
// At the moment the only such property is related to virtual functions and
8719+
// it is turned into sycl_device attribute. This is a tiny optimization to
8720+
// avoid deep dive into the attribute if we already know that a declaration
8721+
// is a device declaration. It may have to be removed later if/when we add
8722+
// handling of more compile-time properties here.
8723+
if (D->hasAttr<SYCLDeviceAttr>())
8724+
return;
8725+
8726+
// SYCL Headers use template magic to pass key=value pairs to the attribute
8727+
// and we should make sure that all template instantiations are done before
8728+
// accessing attribute arguments.
8729+
if (hasDependentExpr(Attr->args_begin(), Attr->args_size()))
8730+
return;
8731+
8732+
SmallVector<std::pair<std::string, std::string>, 4> Pairs =
8733+
Attr->getFilteredAttributeNameValuePairs(Context);
8734+
8735+
for (const auto &[Key, Value] : Pairs) {
8736+
if (Key == "indirectly-callable") {
8737+
D->addAttr(SYCLDeviceAttr::CreateImplicit(Context));
8738+
break;
8739+
}
8740+
}
87158741
}
87168742

87178743
static void handleSYCLAddIRAttributesFunctionAttr(Sema &S, Decl *D,
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// RUN: %clang_cc1 -fsycl-is-device -std=gnu++11 -ast-dump %s | FileCheck %s
2+
3+
// add_ir_attributes_function attribute used to represent compile-time SYCL
4+
// properties and some of those properties are intended to be turned into
5+
// attributes to enable various diagnostics.
6+
//
7+
// This test is intended to check one (and only, at least for now) of such
8+
// tranformations: property with "indirectly-callable" key should have the same
9+
// effect as applying sycl_device attribute and the test checks that we do add
10+
// that attribute implicitly.
11+
12+
// CHECK-LABEL: ToBeTurnedIntoDeviceFunction 'void ()'
13+
// CHECK: SYCLAddIRAttributesFunctionAttr
14+
// CHECK: SYCLDeviceAttr {{.*}} Implicit
15+
[[__sycl_detail__::add_ir_attributes_function("indirectly-callable", "void")]]
16+
void ToBeTurnedIntoDeviceFunction();
17+
18+
// CHECK-LABEL: NotToBeTurnedIntoDeviceFunction 'void ()'
19+
// CHECK-NOT: SYCLDeviceAttr {{.*}} Implicit
20+
// CHECK: SYCLAddIRAttributesFunctionAttr
21+
// CHECK-NOT: SYCLDeviceAttr {{.*}} Implicit
22+
[[__sycl_detail__::add_ir_attributes_function("not-indirectly-callable", "void")]]
23+
void NotToBeTurnedIntoDeviceFunction();
24+
25+
template <int V>
26+
struct Metadata {
27+
static constexpr const char *name = "not-indirectly-callable";
28+
static constexpr const char *value = "void";
29+
};
30+
31+
template <>
32+
struct Metadata<42> {
33+
static constexpr const char *name = "indirectly-callable";
34+
static constexpr const char *value = "void";
35+
};
36+
37+
// CHECK-LABEL: ToBeTurnedIntoDeviceFunctionAttrTemplateArgs 'void ()'
38+
// CHECK: SYCLAddIRAttributesFunctionAttr
39+
// CHECK: SYCLDeviceAttr {{.*}} Implicit
40+
[[__sycl_detail__::add_ir_attributes_function(Metadata<42>::name, Metadata<42>::value)]]
41+
void ToBeTurnedIntoDeviceFunctionAttrTemplateArgs();
42+
43+
// CHECK-LABEL: NotToBeTurnedIntoDeviceFunctionAttrTemplateArgs 'void ()'
44+
// CHECK-NOT: SYCLDeviceAttr {{.*}} Implicit
45+
// CHECK: SYCLAddIRAttributesFunctionAttr
46+
// CHECK-NOT: SYCLDeviceAttr {{.*}} Implicit
47+
[[__sycl_detail__::add_ir_attributes_function(Metadata<1>::name, Metadata<1>::value)]]
48+
void NotToBeTurnedIntoDeviceFunctionAttrTemplateArgs();
49+
50+
// CHECK-LABEL: class Base definition
51+
class Base {
52+
// CHECK-LABEL: ToBeTurnedIntoDeviceFunctionAttrTemplateArgs 'void ()'
53+
// CHECK: SYCLAddIRAttributesFunctionAttr
54+
// CHECK: SYCLDeviceAttr {{.*}} Implicit
55+
[[__sycl_detail__::add_ir_attributes_function(Metadata<42>::name, Metadata<42>::value)]]
56+
virtual void ToBeTurnedIntoDeviceFunctionAttrTemplateArgs();
57+
58+
// CHECK-LABEL: NotToBeTurnedIntoDeviceFunctionAttrTemplateArgs 'void ()'
59+
// CHECK-NOT: SYCLDeviceAttr {{.*}} Implicit
60+
// CHECK: SYCLAddIRAttributesFunctionAttr
61+
// CHECK-NOT: SYCLDeviceAttr {{.*}} Implicit
62+
[[__sycl_detail__::add_ir_attributes_function(Metadata<1>::name, Metadata<1>::value)]]
63+
virtual void NotToBeTurnedIntoDeviceFunctionAttrTemplateArgs();
64+
};
65+
66+
// CHECK-LABEL: class Derived definition
67+
class Derived : public Base {
68+
// CHECK-LABEL: ToBeTurnedIntoDeviceFunctionAttrTemplateArgs 'void ()'
69+
// CHECK: SYCLAddIRAttributesFunctionAttr
70+
// CHECK: SYCLDeviceAttr {{.*}} Implicit
71+
[[__sycl_detail__::add_ir_attributes_function(Metadata<42>::name, Metadata<42>::value)]]
72+
void ToBeTurnedIntoDeviceFunctionAttrTemplateArgs() override;
73+
74+
// CHECK-LABEL: NotToBeTurnedIntoDeviceFunctionAttrTemplateArgs 'void ()'
75+
// CHECK-NOT: SYCLDeviceAttr {{.*}} Implicit
76+
// CHECK: SYCLAddIRAttributesFunctionAttr
77+
// CHECK-NOT: SYCLDeviceAttr {{.*}} Implicit
78+
[[__sycl_detail__::add_ir_attributes_function(Metadata<1>::name, Metadata<1>::value)]]
79+
void NotToBeTurnedIntoDeviceFunctionAttrTemplateArgs() override;
80+
};
81+
82+
// CHECK-LABEL: class SubDerived definition
83+
class SubDerived : public Derived {
84+
// CHECK-LABEL: ToBeTurnedIntoDeviceFunctionAttrTemplateArgs 'void ()'
85+
// CHECK: SYCLAddIRAttributesFunctionAttr
86+
// CHECK: SYCLDeviceAttr {{.*}} Implicit
87+
[[__sycl_detail__::add_ir_attributes_function(Metadata<2>::name, Metadata<42>::name, Metadata<2>::value, Metadata<42>::value)]]
88+
void ToBeTurnedIntoDeviceFunctionAttrTemplateArgs() override;
89+
90+
// CHECK-LABEL: NotToBeTurnedIntoDeviceFunctionAttrTemplateArgs 'void ()'
91+
// CHECK-NOT: SYCLDeviceAttr {{.*}} Implicit
92+
// CHECK: SYCLAddIRAttributesFunctionAttr
93+
// CHECK-NOT: SYCLDeviceAttr {{.*}} Implicit
94+
[[__sycl_detail__::add_ir_attributes_function(Metadata<1>::name, Metadata<2>::name, Metadata<1>::value, Metadata<2>::value)]]
95+
void NotToBeTurnedIntoDeviceFunctionAttrTemplateArgs() override;
96+
};
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// RUN: %clang_cc1 -fsycl-is-device -fcxx-exceptions -triple spir64 \
2+
// RUN: -aux-triple x86_64-unknown-linux-gnu -Wno-return-type -verify \
3+
// RUN: -Wno-sycl-2017-compat -fsyntax-only -std=c++17 %s
4+
5+
// add_ir_attributes_function attribute used to represent compile-time SYCL
6+
// properties and some of those properties are intended to be turned into
7+
// attributes to enable various diagnostics.
8+
//
9+
// "indirectly-callable" property is supposed to be turned into sycl_device
10+
// attribute to make sure that functions market with that property are being
11+
// diagnosed for violating SYCL device code restrictions.
12+
//
13+
// This test ensures that this is indeed the case.
14+
15+
namespace std {
16+
class type_info; // needed to make typeid compile without corresponding include
17+
} // namespace std
18+
19+
[[__sycl_detail__::add_ir_attributes_function("indirectly-callable", "void")]]
20+
void cannot_use_recursion() {
21+
// expected-error@+2 {{SYCL kernel cannot call a recursive function}}
22+
// expected-note@-2 {{function implemented using recursion declared here}}
23+
cannot_use_recursion();
24+
}
25+
26+
[[__sycl_detail__::add_ir_attributes_function("indirectly-callable", "void")]]
27+
void cannot_allocate_storage() {
28+
new int; // expected-error {{SYCL kernel cannot allocate storage}}
29+
}
30+
31+
[[__sycl_detail__::add_ir_attributes_function("indirectly-callable", "void")]]
32+
void cannot_use_rtti() {
33+
(void)typeid(int); // expected-error {{SYCL kernel cannot use rtti}}
34+
}
35+
36+
[[__sycl_detail__::add_ir_attributes_function("indirectly-callable", "void")]]
37+
void cannot_use_zero_length_array() {
38+
// expected-error@+1 {{zero-length arrays are not permitted in SYCL device code}}
39+
int mosterArr[0];
40+
}
41+
42+
[[__sycl_detail__::add_ir_attributes_function("indirectly-callable", "void")]]
43+
void cannot_use_long_double() {
44+
// expected-error@+1 {{'long double' is not supported on this target}}
45+
long double terrorLD;
46+
}
47+
48+
[[__sycl_detail__::add_ir_attributes_function("indirectly-callable", "void")]]
49+
void cannot_use_exceptions() {
50+
try { // expected-error {{SYCL kernel cannot use exceptions}}
51+
;
52+
} catch (...) {
53+
;
54+
}
55+
throw 20; // expected-error {{SYCL kernel cannot use exceptions}}
56+
}

0 commit comments

Comments
 (0)