Skip to content

[SYCL][FPGA] Add clang support for buffer_location property #2166

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Aug 5, 2020
Merged
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,14 @@ def SYCLDeviceIndirectlyCallable : InheritableAttr {
let PragmaAttributeSupport = 0;
}

def SYCLIntelBufferLocation : InheritableAttr {
// No spelling, as this attribute can't be created in the source code.
let Spellings = [];
let Args = [UnsignedArgument<"LocationID">];
let LangOpts = [SYCLIsDevice, SYCLIsHost];
let Documentation = [SYCLIntelBufferLocationAttrDocs];
}

def SYCLIntelKernelArgsRestrict : InheritableAttr {
let Spellings = [ CXX11<"intel", "kernel_args_restrict"> ];
let Subjects = SubjectList<[Function], ErrorDiag>;
Expand Down
17 changes: 17 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -1994,6 +1994,23 @@ can be lowered.
}];
}

def SYCLIntelBufferLocationAttrDocs : Documentation {
let Category = DocCatFunction;
let Heading = "kernel_args_buffer_location";
let Content = [{
This attribute is implicitly added to OpenCL pointer kernel parameters generated
from a SYCL kernel object. It lacks a spelling, as it is not intended to be used
by the programmer.

This attribute causes clang to generate metadata on the OpenCL kernel containing
the number of kernel parameters. The metadata contains an integer that is set
according to the values passed through the ``accessor`` property
``buffer_location``. These values are mapped to the actual locations of the
global buffers (such as DDR, QDR, etc) and applied to pointer kernel parameters.
The default value is -1.
}];
}

def SYCLIntelKernelArgsRestrictDocs : Documentation {
let Category = DocCatVariable;
let Heading = "kernel_args_restrict";
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -10969,6 +10969,11 @@ def warn_boolean_attribute_argument_is_not_valid: Warning<
def err_sycl_attibute_cannot_be_applied_here
: Error<"%0 attribute cannot be applied to a "
"static function or function in an anonymous namespace">;
def err_sycl_compiletime_property_duplication : Error<
"Can't apply %0 property twice to the same accessor">;
def err_sycl_invalid_property_template_param : Error<
"%0 template parameter must be a "
"%select{parameter pack|type|non-negative integer|property_list}1">;
def warn_sycl_attibute_function_raw_ptr
: Warning<"SYCL 1.2.1 specification does not allow %0 attribute applied "
"to a function with a raw pointer "
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1413,6 +1413,9 @@ void CodeGenModule::GenOpenCLArgMetadata(llvm::Function *Fn,
// MDNode for the kernel argument names.
SmallVector<llvm::Metadata *, 8> argNames;

// MDNode for the intel_buffer_location attribute.
SmallVector<llvm::Metadata *, 8> argSYCLBufferLocationAttr;

if (FD && CGF)
for (unsigned i = 0, e = FD->getNumParams(); i != e; ++i) {
const ParmVarDecl *parm = FD->getParamDecl(i);
Expand Down Expand Up @@ -1536,6 +1539,14 @@ void CodeGenModule::GenOpenCLArgMetadata(llvm::Function *Fn,

// Get argument name.
argNames.push_back(llvm::MDString::get(VMContext, parm->getName()));

auto *SYCLBufferLocationAttr =
parm->getAttr<SYCLIntelBufferLocationAttr>();
argSYCLBufferLocationAttr.push_back(
(SYCLBufferLocationAttr)
? llvm::ConstantAsMetadata::get(CGF->Builder.getInt32(
SYCLBufferLocationAttr->getLocationID()))
: llvm::ConstantAsMetadata::get(CGF->Builder.getInt32(-1)));
}

Fn->setMetadata("kernel_arg_addr_space",
Expand All @@ -1551,6 +1562,9 @@ void CodeGenModule::GenOpenCLArgMetadata(llvm::Function *Fn,
if (getCodeGenOpts().EmitOpenCLArgMetadata)
Fn->setMetadata("kernel_arg_name",
llvm::MDNode::get(VMContext, argNames));
if (LangOpts.SYCLIsDevice)
Fn->setMetadata("kernel_arg_buffer_location",
llvm::MDNode::get(VMContext, argSYCLBufferLocationAttr));
}

/// Determines whether the language options require us to model
Expand Down
125 changes: 118 additions & 7 deletions clang/lib/Sema/SemaSYCL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ class Util {
/// half class.
static bool isSyclHalfType(const QualType &Ty);

/// Checks whether given clang type is a full specialization of the SYCL
/// property_list class.
static bool isPropertyListType(const QualType &Ty);

/// Checks whether given clang type is a full specialization of the SYCL
/// buffer_location class.
static bool isSyclBufferLocationType(const QualType &Ty);

/// Checks whether given clang type is a standard SYCL API class with given
/// name.
/// \param Ty the clang type being checked
Expand Down Expand Up @@ -1189,8 +1197,9 @@ class SyclKernelDeclCreator : public SyclKernelFieldHandler {

void addParam(ParamDesc newParamDesc, QualType FieldTy) {
// Create a new ParmVarDecl based on the new info.
ASTContext &Ctx = SemaRef.getASTContext();
auto *NewParam = ParmVarDecl::Create(
SemaRef.getASTContext(), KernelDecl, SourceLocation(), SourceLocation(),
Ctx, KernelDecl, SourceLocation(), SourceLocation(),
std::get<1>(newParamDesc), std::get<0>(newParamDesc),
std::get<2>(newParamDesc), SC_None, /*DefArg*/ nullptr);
NewParam->setScopeInfo(0, Params.size());
Expand All @@ -1200,11 +1209,82 @@ class SyclKernelDeclCreator : public SyclKernelFieldHandler {
Params.push_back(NewParam);
}

// Handle accessor properties. If any properties were found in
// the property_list - add the appropriate attributes to ParmVarDecl.
void handleAccessorPropertyList(ParmVarDecl *Param,
const CXXRecordDecl *RecordDecl,
SourceLocation Loc) {
const auto *AccTy = cast<ClassTemplateSpecializationDecl>(RecordDecl);
// TODO: when SYCL headers' part is ready - replace this 'if' with an error
if (AccTy->getTemplateArgs().size() < 6)
return;
const auto PropList = cast<TemplateArgument>(AccTy->getTemplateArgs()[5]);
if (PropList.getKind() != TemplateArgument::ArgKind::Type) {
SemaRef.Diag(Loc, diag::err_sycl_invalid_property_template_param)
<< "accessor's 5th" << /*type*/ 1;
return;
}
QualType PropListTy = PropList.getAsType();
if (!Util::isPropertyListType(PropListTy)) {
SemaRef.Diag(Loc, diag::err_sycl_invalid_property_template_param)
<< "accessor's 5th" << /*property_list*/ 3;
return;
}

const auto *PropListDecl =
cast<ClassTemplateSpecializationDecl>(PropListTy->getAsRecordDecl());
const auto TemplArg = PropListDecl->getTemplateArgs()[0];
if (TemplArg.getKind() != TemplateArgument::ArgKind::Pack) {
SemaRef.Diag(Loc, diag::err_sycl_invalid_property_template_param)
<< "property_list" << /*parameter pack*/ 0;
return;
}
// Move through TemplateArgs list of a property list and search for
// properties. If found - apply the appropriate attribute to ParmVarDecl.
for (TemplateArgument::pack_iterator Prop = TemplArg.pack_begin();
Prop != TemplArg.pack_end(); ++Prop) {
QualType PropTy = Prop->getAsType();
if (Util::isSyclBufferLocationType(PropTy))
handleBufferLocationProperty(Param, PropTy, Loc);
}
}

// Obtain an integer value stored in a template parameter of buffer_location
// property to pass it to buffer_location kernel attribute
void handleBufferLocationProperty(ParmVarDecl *Param, QualType PropTy,
SourceLocation Loc) {
// If we have more than 1 buffer_location properties on a single
// accessor - emit an error
if (Param->hasAttr<SYCLIntelBufferLocationAttr>()) {
SemaRef.Diag(Loc, diag::err_sycl_compiletime_property_duplication)
<< "buffer_location";
return;
}
ASTContext &Ctx = SemaRef.getASTContext();
const auto *PropDecl =
cast<ClassTemplateSpecializationDecl>(PropTy->getAsRecordDecl());
const auto BufferLoc = PropDecl->getTemplateArgs()[0];
if (BufferLoc.getKind() != TemplateArgument::ArgKind::Integral) {
SemaRef.Diag(Loc, diag::err_sycl_invalid_property_template_param)
<< "buffer_location" << /*non-negative integer*/ 2;
return;
}
int LocationID = static_cast<int>(BufferLoc.getAsIntegral().getExtValue());
if (LocationID < 0) {
SemaRef.Diag(Loc, diag::err_sycl_invalid_property_template_param)
<< "buffer_location" << /*non-negative integer*/ 2;
return;
}
Param->addAttr(
SYCLIntelBufferLocationAttr::CreateImplicit(Ctx, LocationID));
}

// All special SYCL objects must have __init method. We extract types for
// kernel parameters from __init method parameters. We will use __init method
// and kernel parameters which we build here to initialize special objects in
// the kernel body.
bool handleSpecialType(FieldDecl *FD, QualType FieldTy) {
bool handleSpecialType(FieldDecl *FD, QualType FieldTy,
bool isAccessorType = false) {
const auto *RecordDecl = FieldTy->getAsCXXRecordDecl();
assert(RecordDecl && "The accessor/sampler must be a RecordDecl");
CXXMethodDecl *InitMethod = getMethodByName(RecordDecl, InitMethodName);
Expand All @@ -1213,8 +1293,16 @@ class SyclKernelDeclCreator : public SyclKernelFieldHandler {
// Don't do -1 here because we count on this to be the first parameter added
// (if any).
size_t ParamIndex = Params.size();
for (const ParmVarDecl *Param : InitMethod->parameters())
addParam(FD, Param->getType().getCanonicalType());
ParmVarDecl **ParamIt = InitMethod->parameters().begin();
if (*ParamIt) {
addParam(FD, (*ParamIt)->getType().getCanonicalType());
if (isAccessorType)
handleAccessorPropertyList(Params.back(), RecordDecl,
FD->getLocation());
++ParamIt;
for (; ParamIt != InitMethod->parameters().end(); ++ParamIt)
addParam(FD, (*ParamIt)->getType().getCanonicalType());
}
LastParamIndex = ParamIndex;
return true;
}
Expand Down Expand Up @@ -1284,14 +1372,20 @@ class SyclKernelDeclCreator : public SyclKernelFieldHandler {
// Don't do -1 here because we count on this to be the first parameter added
// (if any).
size_t ParamIndex = Params.size();
for (const ParmVarDecl *Param : InitMethod->parameters())
addParam(BS, Param->getType().getCanonicalType());
ParmVarDecl **ParamIt = InitMethod->parameters().begin();
if (*ParamIt) {
addParam(BS, (*ParamIt)->getType().getCanonicalType());
handleAccessorPropertyList(Params.back(), RecordDecl, BS.getBeginLoc());
++ParamIt;
for (; ParamIt != InitMethod->parameters().end(); ++ParamIt)
addParam(BS, (*ParamIt)->getType().getCanonicalType());
}
LastParamIndex = ParamIndex;
return true;
}

bool handleSyclAccessorType(FieldDecl *FD, QualType FieldTy) final {
return handleSpecialType(FD, FieldTy);
return handleSpecialType(FD, FieldTy, /*isAccessorType*/ true);
}

bool handleSyclSamplerType(FieldDecl *FD, QualType FieldTy) final {
Expand Down Expand Up @@ -2820,6 +2914,23 @@ bool Util::isSyclSpecConstantType(const QualType &Ty) {
return matchQualifiedTypeName(Ty, Scopes);
}

bool Util::isPropertyListType(const QualType &Ty) {
return isSyclType(Ty, "property_list", true /*Tmpl*/);
}

bool Util::isSyclBufferLocationType(const QualType &Ty) {
const StringRef &Name = "buffer_location";
std::array<DeclContextDesc, 4> Scopes = {
Util::DeclContextDesc{clang::Decl::Kind::Namespace, "cl"},
Util::DeclContextDesc{clang::Decl::Kind::Namespace, "sycl"},
// TODO: this doesn't belong to property namespace, instead it shall be
// in its own namespace. Change it, when the actual implementation in SYCL
// headers is ready
Util::DeclContextDesc{clang::Decl::Kind::Namespace, "property"},
Util::DeclContextDesc{Decl::Kind::ClassTemplateSpecialization, Name}};
return matchQualifiedTypeName(Ty, Scopes);
}

bool Util::isSyclType(const QualType &Ty, StringRef Name, bool Tmpl) {
Decl::Kind ClassDeclKind =
Tmpl ? Decl::Kind::ClassTemplateSpecialization : Decl::Kind::CXXRecord;
Expand Down
31 changes: 22 additions & 9 deletions clang/test/CodeGenSYCL/Inputs/sycl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,23 @@ enum prop_type {
base_prop
};

// Compile time known accessor property
// TODO: this doesn't belong to property namespace, instead it shall be in its
// own namespace. Change it, when the actual implementation in SYCL headers is
// ready
template <int>
class buffer_location {};

struct property_base {
virtual prop_type type() const = 0;
};
} // namespace property

template <typename... properties>
class property_list {
public:
template <typename... propertyTN>
property_list(propertyTN... props) {}
template <typename... propertiesTN>
property_list(propertiesTN... props){};

template <typename propertyT>
bool has_property() const { return true; }
Expand Down Expand Up @@ -127,7 +135,8 @@ struct _ImplT {

template <typename dataT, int dimensions, access::mode accessmode,
access::target accessTarget = access::target::global_buffer,
access::placeholder isPlaceholder = access::placeholder::false_t>
access::placeholder isPlaceholder = access::placeholder::false_t,
typename propertyListT = property_list<>>
class accessor {

public:
Expand All @@ -141,6 +150,8 @@ class accessor {
private:
void __init(__attribute__((opencl_global)) dataT *Ptr, range<dimensions> AccessRange,
range<dimensions> MemRange, id<dimensions> Offset) {}

propertyListT prop_list;
};

template <int dimensions, access::mode accessmode, access::target accesstarget>
Expand Down Expand Up @@ -326,7 +337,8 @@ const stream& operator<<(const stream &S, T&&) {
}

template <typename T, int dimensions = 1,
typename AllocatorT = int /*fake type as AllocatorT is not used*/>
typename AllocatorT = int /*fake type as AllocatorT is not used*/,
typename... properties>
class buffer {
public:
using value_type = T;
Expand All @@ -338,13 +350,13 @@ class buffer {
buffer(ParamTypes... args) {} // fake constructor

buffer(const range<dimensions> &bufferRange,
const property_list &propList = {}) {}
const property_list<properties...> &propList = {}) {}

buffer(T *hostData, const range<dimensions> &bufferRange,
const property_list &propList = {}) {}
const property_list<properties...> &propList = {}) {}

buffer(const T *hostData, const range<dimensions> &bufferRange,
const property_list &propList = {}) {}
const property_list<properties...> &propList = {}) {}

buffer(const buffer &rhs) = default;

Expand Down Expand Up @@ -412,11 +424,12 @@ enum class image_channel_type : unsigned int {
fp32
};

template <int dimensions = 1, typename AllocatorT = int>
template <int dimensions = 1, typename AllocatorT = int, typename... properties>
class image {
public:
image(image_channel_order Order, image_channel_type Type,
const range<dimensions> &Range, const property_list &PropList = {}) {}
const range<dimensions> &Range,
const property_list<properties...> &PropList = {}) {}

/* -- common interface members -- */

Expand Down
6 changes: 3 additions & 3 deletions clang/test/CodeGenSYCL/accessor_inheritance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,13 @@ int main() {
// CHECK: [[ACC_FIELD:%[a-zA-Z0-9_]+]] = getelementptr inbounds %struct{{.*}}Base, %struct{{.*}}Base* [[BITCAST]], i32 0, i32 2
// CHECK: [[ACC1_AS_CAST:%[a-zA-Z0-9_]+]] = addrspacecast %"class{{.*}}cl::sycl::accessor"* [[ACC_FIELD]] to %"class{{.*}}cl::sycl::accessor" addrspace(4)*
// Default constructor call
// CHECK: call spir_func void @_ZN2cl4sycl8accessorIcLi1ELNS0_6access4modeE1024ELNS2_6targetE2014ELNS2_11placeholderE0EEC1Ev(%"class{{.*}}cl::sycl::accessor" addrspace(4)* [[ACC1_AS_CAST]])
// CHECK: call spir_func void @_ZN2cl4sycl8accessorIcLi1ELNS0_6access4modeE1024ELNS2_6targetE2014ELNS2_11placeholderE0ENS0_13property_listIJEEEEC1Ev(%"class{{.*}}cl::sycl::accessor" addrspace(4)* [[ACC1_AS_CAST]])
// CHECK: [[BITCAST1:%[a-zA-Z0-9_]+]] = bitcast %struct{{.*}}Captured* [[GEP]] to i8*
// CHECK: [[GEP1:%[a-zA-Z0-9_]+]] = getelementptr inbounds i8, i8* [[BITCAST1]], i64 20
// CHECK: [[GEP1:%[a-zA-Z0-9_]+]] = getelementptr inbounds i8, i8* [[BITCAST1]], i64 24
// CHECK: [[BITCAST2:%[a-zA-Z0-9_]+]] = bitcast i8* [[GEP1]] to %"class{{.*}}cl::sycl::accessor"*
// CHECK: [[ACC2_AS_CAST:%[a-zA-Z0-9_]+]] = addrspacecast %"class{{.*}}cl::sycl::accessor"* [[BITCAST2]] to %"class{{.*}}cl::sycl::accessor" addrspace(4)*
// Default constructor call
// CHECK: call spir_func void @_ZN2cl4sycl8accessorIcLi1ELNS0_6access4modeE1024ELNS2_6targetE2014ELNS2_11placeholderE0EEC2Ev(%"class{{.*}}cl::sycl::accessor" addrspace(4)* [[ACC2_AS_CAST]])
// CHECK: call spir_func void @_ZN2cl4sycl8accessorIcLi1ELNS0_6access4modeE1024ELNS2_6targetE2014ELNS2_11placeholderE0ENS0_13property_listIJEEEEC2Ev(%"class{{.*}}cl::sycl::accessor" addrspace(4)* [[ACC2_AS_CAST]])

// CHECK C field initialization
// CHECK: [[FIELD_C:%[a-zA-Z0-9_]+]] = getelementptr inbounds %struct{{.*}}Captured, %struct{{.*}}Captured* [[GEP]], i32 0, i32 2
Expand Down
Loading