Skip to content

Commit 0c38b35

Browse files
authored
[SYCL][FPGA] Add clang support for buffer_location property (#2166)
This is a compiler-time known accessor property which serves as an optimization hint for a compiler on where exactly buffer was allocated. This is needed when a board has multiple disjoint global memories that must be managed explicitly by a programmer. When the property is added as a template parameter of an accessor - SemaSYCL will implicitly add ``intelfpga::kernel_arg_buffer_location`` attribute to an OpenCL kernel generated from SYCL kernel object. It is not allowed to use the attribute explicitly in SYCL code. When the attribute is applied, clang generates metadata attached to OpenCL kernel. Number of values stored in the metadata is the same as number of kernel parameters. Order of metadata values is following the order of pointer kernel parameters. Metadata values are of an integer type and is being set accordingly values passed through accessor property ``buffer_location``. This values are mapped in hardware backend to the actual locations of buffers (DDR, QDR etc). Default value passed in the metadata is '-1'.
1 parent 89804af commit 0c38b35

17 files changed

+388
-39
lines changed

clang/include/clang/Basic/Attr.td

+8
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,14 @@ def SYCLDeviceIndirectlyCallable : InheritableAttr {
11881188
let PragmaAttributeSupport = 0;
11891189
}
11901190

1191+
def SYCLIntelBufferLocation : InheritableAttr {
1192+
// No spelling, as this attribute can't be created in the source code.
1193+
let Spellings = [];
1194+
let Args = [UnsignedArgument<"LocationID">];
1195+
let LangOpts = [SYCLIsDevice, SYCLIsHost];
1196+
let Documentation = [SYCLIntelBufferLocationAttrDocs];
1197+
}
1198+
11911199
def SYCLIntelKernelArgsRestrict : InheritableAttr {
11921200
let Spellings = [ CXX11<"intel", "kernel_args_restrict"> ];
11931201
let Subjects = SubjectList<[Function], ErrorDiag>;

clang/include/clang/Basic/AttrDocs.td

+19
Original file line numberDiff line numberDiff line change
@@ -1994,6 +1994,25 @@ can be lowered.
19941994
}];
19951995
}
19961996

1997+
def SYCLIntelBufferLocationAttrDocs : Documentation {
1998+
let Category = DocCatFunction;
1999+
let Heading = "kernel_args_buffer_location";
2000+
let Content = [{
2001+
This attribute is implicitly added to OpenCL pointer kernel parameters generated
2002+
from a SYCL kernel object. It lacks a spelling, as it is not intended to be used
2003+
by the programmer.
2004+
2005+
This attribute causes clang to generate metadata on the OpenCL kernel containing
2006+
the number of kernel parameters. The metadata contains an integer that is set
2007+
according to the values passed through the ``accessor`` property
2008+
``buffer_location``. These values are mapped to the actual locations of the
2009+
global buffers (such as DDR, QDR, etc) and applied to pointer kernel parameters.
2010+
Number of metadata arguments is the same as a number of kernel parameters, so
2011+
any parameter that isn't an ``accessor`` with ``buffer_location`` property is
2012+
annotated by '-1' in the metadata node.
2013+
}];
2014+
}
2015+
19972016
def SYCLIntelKernelArgsRestrictDocs : Documentation {
19982017
let Category = DocCatVariable;
19992018
let Heading = "kernel_args_restrict";

clang/include/clang/Basic/DiagnosticSemaKinds.td

+10
Original file line numberDiff line numberDiff line change
@@ -10980,6 +10980,16 @@ def warn_boolean_attribute_argument_is_not_valid: Warning<
1098010980
def err_sycl_attibute_cannot_be_applied_here
1098110981
: Error<"%0 attribute cannot be applied to a "
1098210982
"static function or function in an anonymous namespace">;
10983+
def err_sycl_compiletime_property_duplication : Error<
10984+
"Can't apply %0 property twice to the same accessor">;
10985+
def err_sycl_invalid_property_list_param_number : Error<
10986+
"%0 must have exactly one template parameter">;
10987+
def err_sycl_invalid_accessor_property_template_param : Error<
10988+
"Fifth template parameter of the accessor must be of a property_list type">;
10989+
def err_sycl_invalid_property_list_template_param : Error<
10990+
"%select{property_list|property_list pack argument|buffer_location}0 "
10991+
"template parameter must be a "
10992+
"%select{parameter pack|type|non-negative integer}1">;
1098310993
def warn_sycl_attibute_function_raw_ptr
1098410994
: Warning<"SYCL 1.2.1 specification does not allow %0 attribute applied "
1098510995
"to a function with a raw pointer "

clang/lib/CodeGen/CodeGenModule.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -1413,6 +1413,9 @@ void CodeGenModule::GenOpenCLArgMetadata(llvm::Function *Fn,
14131413
// MDNode for the kernel argument names.
14141414
SmallVector<llvm::Metadata *, 8> argNames;
14151415

1416+
// MDNode for the intel_buffer_location attribute.
1417+
SmallVector<llvm::Metadata *, 8> argSYCLBufferLocationAttr;
1418+
14161419
if (FD && CGF)
14171420
for (unsigned i = 0, e = FD->getNumParams(); i != e; ++i) {
14181421
const ParmVarDecl *parm = FD->getParamDecl(i);
@@ -1536,6 +1539,14 @@ void CodeGenModule::GenOpenCLArgMetadata(llvm::Function *Fn,
15361539

15371540
// Get argument name.
15381541
argNames.push_back(llvm::MDString::get(VMContext, parm->getName()));
1542+
1543+
auto *SYCLBufferLocationAttr =
1544+
parm->getAttr<SYCLIntelBufferLocationAttr>();
1545+
argSYCLBufferLocationAttr.push_back(
1546+
(SYCLBufferLocationAttr)
1547+
? llvm::ConstantAsMetadata::get(CGF->Builder.getInt32(
1548+
SYCLBufferLocationAttr->getLocationID()))
1549+
: llvm::ConstantAsMetadata::get(CGF->Builder.getInt32(-1)));
15391550
}
15401551

15411552
Fn->setMetadata("kernel_arg_addr_space",
@@ -1551,6 +1562,9 @@ void CodeGenModule::GenOpenCLArgMetadata(llvm::Function *Fn,
15511562
if (getCodeGenOpts().EmitOpenCLArgMetadata)
15521563
Fn->setMetadata("kernel_arg_name",
15531564
llvm::MDNode::get(VMContext, argNames));
1565+
if (LangOpts.SYCLIsDevice)
1566+
Fn->setMetadata("kernel_arg_buffer_location",
1567+
llvm::MDNode::get(VMContext, argSYCLBufferLocationAttr));
15541568
}
15551569

15561570
/// Determines whether the language options require us to model

clang/lib/Sema/SemaSYCL.cpp

+149-7
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ class Util {
8080
/// half class.
8181
static bool isSyclHalfType(const QualType &Ty);
8282

83+
/// Checks whether given clang type is a full specialization of the SYCL
84+
/// property_list class.
85+
static bool isPropertyListType(const QualType &Ty);
86+
87+
/// Checks whether given clang type is a full specialization of the SYCL
88+
/// buffer_location class.
89+
static bool isSyclBufferLocationType(const QualType &Ty);
90+
8391
/// Checks whether given clang type is a standard SYCL API class with given
8492
/// name.
8593
/// \param Ty the clang type being checked
@@ -1076,6 +1084,66 @@ class SyclKernelFieldChecker : public SyclKernelFieldHandler {
10761084
return false;
10771085
}
10781086

1087+
void checkPropertyListType(TemplateArgument PropList, SourceLocation Loc) {
1088+
if (PropList.getKind() != TemplateArgument::ArgKind::Type) {
1089+
SemaRef.Diag(Loc,
1090+
diag::err_sycl_invalid_accessor_property_template_param);
1091+
return;
1092+
}
1093+
QualType PropListTy = PropList.getAsType();
1094+
if (!Util::isPropertyListType(PropListTy)) {
1095+
SemaRef.Diag(Loc,
1096+
diag::err_sycl_invalid_accessor_property_template_param);
1097+
return;
1098+
}
1099+
const auto *PropListDecl =
1100+
cast<ClassTemplateSpecializationDecl>(PropListTy->getAsRecordDecl());
1101+
if (PropListDecl->getTemplateArgs().size() != 1) {
1102+
SemaRef.Diag(Loc, diag::err_sycl_invalid_property_list_param_number)
1103+
<< "property_list";
1104+
return;
1105+
}
1106+
const auto TemplArg = PropListDecl->getTemplateArgs()[0];
1107+
if (TemplArg.getKind() != TemplateArgument::ArgKind::Pack) {
1108+
SemaRef.Diag(Loc, diag::err_sycl_invalid_property_list_template_param)
1109+
<< /*property_list*/ 0 << /*parameter pack*/ 0;
1110+
return;
1111+
}
1112+
for (TemplateArgument::pack_iterator Prop = TemplArg.pack_begin();
1113+
Prop != TemplArg.pack_end(); ++Prop) {
1114+
if (Prop->getKind() != TemplateArgument::ArgKind::Type) {
1115+
SemaRef.Diag(Loc, diag::err_sycl_invalid_property_list_template_param)
1116+
<< /*property_list pack argument*/ 1 << /*type*/ 1;
1117+
return;
1118+
}
1119+
QualType PropTy = Prop->getAsType();
1120+
if (Util::isSyclBufferLocationType(PropTy))
1121+
checkBufferLocationType(PropTy, Loc);
1122+
}
1123+
}
1124+
1125+
void checkBufferLocationType(QualType PropTy, SourceLocation Loc) {
1126+
const auto *PropDecl =
1127+
dyn_cast<ClassTemplateSpecializationDecl>(PropTy->getAsRecordDecl());
1128+
if (PropDecl->getTemplateArgs().size() != 1) {
1129+
SemaRef.Diag(Loc, diag::err_sycl_invalid_property_list_param_number)
1130+
<< "buffer_location";
1131+
return;
1132+
}
1133+
const auto BufferLoc = PropDecl->getTemplateArgs()[0];
1134+
if (BufferLoc.getKind() != TemplateArgument::ArgKind::Integral) {
1135+
SemaRef.Diag(Loc, diag::err_sycl_invalid_property_list_template_param)
1136+
<< /*buffer_location*/ 2 << /*non-negative integer*/ 2;
1137+
return;
1138+
}
1139+
int LocationID = static_cast<int>(BufferLoc.getAsIntegral().getExtValue());
1140+
if (LocationID < 0) {
1141+
SemaRef.Diag(Loc, diag::err_sycl_invalid_property_list_template_param)
1142+
<< /*buffer_location*/ 2 << /*non-negative integer*/ 2;
1143+
return;
1144+
}
1145+
}
1146+
10791147
void checkAccessorType(QualType Ty, SourceRange Loc) {
10801148
assert(Util::isSyclAccessorType(Ty) &&
10811149
"Should only be called on SYCL accessor types.");
@@ -1087,6 +1155,8 @@ class SyclKernelFieldChecker : public SyclKernelFieldHandler {
10871155
TemplateArgument TA = TAL.get(0);
10881156
const QualType TemplateArgTy = TA.getAsType();
10891157

1158+
if (TAL.size() > 5)
1159+
checkPropertyListType(TAL.get(5), Loc.getBegin());
10901160
llvm::DenseSet<QualType> Visited;
10911161
checkSYCLType(SemaRef, TemplateArgTy, Loc, Visited);
10921162
}
@@ -1158,8 +1228,9 @@ class SyclKernelDeclCreator : public SyclKernelFieldHandler {
11581228

11591229
void addParam(ParamDesc newParamDesc, QualType FieldTy) {
11601230
// Create a new ParmVarDecl based on the new info.
1231+
ASTContext &Ctx = SemaRef.getASTContext();
11611232
auto *NewParam = ParmVarDecl::Create(
1162-
SemaRef.getASTContext(), KernelDecl, SourceLocation(), SourceLocation(),
1233+
Ctx, KernelDecl, SourceLocation(), SourceLocation(),
11631234
std::get<1>(newParamDesc), std::get<0>(newParamDesc),
11641235
std::get<2>(newParamDesc), SC_None, /*DefArg*/ nullptr);
11651236
NewParam->setScopeInfo(0, Params.size());
@@ -1169,11 +1240,56 @@ class SyclKernelDeclCreator : public SyclKernelFieldHandler {
11691240
Params.push_back(NewParam);
11701241
}
11711242

1243+
// Handle accessor properties. If any properties were found in
1244+
// the property_list - add the appropriate attributes to ParmVarDecl.
1245+
void handleAccessorPropertyList(ParmVarDecl *Param,
1246+
const CXXRecordDecl *RecordDecl,
1247+
SourceLocation Loc) {
1248+
const auto *AccTy = cast<ClassTemplateSpecializationDecl>(RecordDecl);
1249+
// TODO: when SYCL headers' part is ready - replace this 'if' with an error
1250+
if (AccTy->getTemplateArgs().size() < 6)
1251+
return;
1252+
const auto PropList = cast<TemplateArgument>(AccTy->getTemplateArgs()[5]);
1253+
QualType PropListTy = PropList.getAsType();
1254+
const auto *PropListDecl =
1255+
cast<ClassTemplateSpecializationDecl>(PropListTy->getAsRecordDecl());
1256+
const auto TemplArg = PropListDecl->getTemplateArgs()[0];
1257+
// Move through TemplateArgs list of a property list and search for
1258+
// properties. If found - apply the appropriate attribute to ParmVarDecl.
1259+
for (TemplateArgument::pack_iterator Prop = TemplArg.pack_begin();
1260+
Prop != TemplArg.pack_end(); ++Prop) {
1261+
QualType PropTy = Prop->getAsType();
1262+
if (Util::isSyclBufferLocationType(PropTy))
1263+
handleBufferLocationProperty(Param, PropTy, Loc);
1264+
}
1265+
}
1266+
1267+
// Obtain an integer value stored in a template parameter of buffer_location
1268+
// property to pass it to buffer_location kernel attribute
1269+
void handleBufferLocationProperty(ParmVarDecl *Param, QualType PropTy,
1270+
SourceLocation Loc) {
1271+
// If we have more than 1 buffer_location properties on a single
1272+
// accessor - emit an error
1273+
if (Param->hasAttr<SYCLIntelBufferLocationAttr>()) {
1274+
SemaRef.Diag(Loc, diag::err_sycl_compiletime_property_duplication)
1275+
<< "buffer_location";
1276+
return;
1277+
}
1278+
ASTContext &Ctx = SemaRef.getASTContext();
1279+
const auto *PropDecl =
1280+
cast<ClassTemplateSpecializationDecl>(PropTy->getAsRecordDecl());
1281+
const auto BufferLoc = PropDecl->getTemplateArgs()[0];
1282+
int LocationID = static_cast<int>(BufferLoc.getAsIntegral().getExtValue());
1283+
Param->addAttr(
1284+
SYCLIntelBufferLocationAttr::CreateImplicit(Ctx, LocationID));
1285+
}
1286+
11721287
// All special SYCL objects must have __init method. We extract types for
11731288
// kernel parameters from __init method parameters. We will use __init method
11741289
// and kernel parameters which we build here to initialize special objects in
11751290
// the kernel body.
1176-
bool handleSpecialType(FieldDecl *FD, QualType FieldTy) {
1291+
bool handleSpecialType(FieldDecl *FD, QualType FieldTy,
1292+
bool isAccessorType = false) {
11771293
const auto *RecordDecl = FieldTy->getAsCXXRecordDecl();
11781294
assert(RecordDecl && "The accessor/sampler must be a RecordDecl");
11791295
CXXMethodDecl *InitMethod = getMethodByName(RecordDecl, InitMethodName);
@@ -1182,8 +1298,13 @@ class SyclKernelDeclCreator : public SyclKernelFieldHandler {
11821298
// Don't do -1 here because we count on this to be the first parameter added
11831299
// (if any).
11841300
size_t ParamIndex = Params.size();
1185-
for (const ParmVarDecl *Param : InitMethod->parameters())
1186-
addParam(FD, Param->getType().getCanonicalType());
1301+
for (const ParmVarDecl *Param : InitMethod->parameters()) {
1302+
QualType ParamTy = Param->getType();
1303+
addParam(FD, ParamTy.getCanonicalType());
1304+
if (ParamTy.getTypePtr()->isPointerType() && isAccessorType)
1305+
handleAccessorPropertyList(Params.back(), RecordDecl,
1306+
FD->getLocation());
1307+
}
11871308
LastParamIndex = ParamIndex;
11881309
return true;
11891310
}
@@ -1253,14 +1374,18 @@ class SyclKernelDeclCreator : public SyclKernelFieldHandler {
12531374
// Don't do -1 here because we count on this to be the first parameter added
12541375
// (if any).
12551376
size_t ParamIndex = Params.size();
1256-
for (const ParmVarDecl *Param : InitMethod->parameters())
1257-
addParam(BS, Param->getType().getCanonicalType());
1377+
for (const ParmVarDecl *Param : InitMethod->parameters()) {
1378+
QualType ParamTy = Param->getType();
1379+
addParam(BS, ParamTy.getCanonicalType());
1380+
if (ParamTy.getTypePtr()->isPointerType())
1381+
handleAccessorPropertyList(Params.back(), RecordDecl, BS.getBeginLoc());
1382+
}
12581383
LastParamIndex = ParamIndex;
12591384
return true;
12601385
}
12611386

12621387
bool handleSyclAccessorType(FieldDecl *FD, QualType FieldTy) final {
1263-
return handleSpecialType(FD, FieldTy);
1388+
return handleSpecialType(FD, FieldTy, /*isAccessorType*/ true);
12641389
}
12651390

12661391
bool handleSyclSamplerType(FieldDecl *FD, QualType FieldTy) final {
@@ -2834,6 +2959,23 @@ bool Util::isSyclSpecConstantType(const QualType &Ty) {
28342959
return matchQualifiedTypeName(Ty, Scopes);
28352960
}
28362961

2962+
bool Util::isPropertyListType(const QualType &Ty) {
2963+
return isSyclType(Ty, "property_list", true /*Tmpl*/);
2964+
}
2965+
2966+
bool Util::isSyclBufferLocationType(const QualType &Ty) {
2967+
const StringRef &Name = "buffer_location";
2968+
std::array<DeclContextDesc, 4> Scopes = {
2969+
Util::DeclContextDesc{clang::Decl::Kind::Namespace, "cl"},
2970+
Util::DeclContextDesc{clang::Decl::Kind::Namespace, "sycl"},
2971+
// TODO: this doesn't belong to property namespace, instead it shall be
2972+
// in its own namespace. Change it, when the actual implementation in SYCL
2973+
// headers is ready
2974+
Util::DeclContextDesc{clang::Decl::Kind::Namespace, "property"},
2975+
Util::DeclContextDesc{Decl::Kind::ClassTemplateSpecialization, Name}};
2976+
return matchQualifiedTypeName(Ty, Scopes);
2977+
}
2978+
28372979
bool Util::isSyclType(const QualType &Ty, StringRef Name, bool Tmpl) {
28382980
Decl::Kind ClassDeclKind =
28392981
Tmpl ? Decl::Kind::ClassTemplateSpecialization : Decl::Kind::CXXRecord;

0 commit comments

Comments
 (0)