Skip to content

[SYCL][ESIMD] Implement "private globals" (file scope private vars) in FE #1756

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 4 commits into from
Jun 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1135,6 +1135,19 @@ def SYCLKernel : InheritableAttr {
let Documentation = [SYCLKernelDocs];
}

// Available in SYCL explicit SIMD extension. Binds a file scope private
// variable to a specific register.
def SYCLRegisterNum : InheritableAttr {
let Spellings = [GNU<"register_num">, Declspec<"register_num">];
let Args = [UnsignedArgument<"Number">];
let Subjects = SubjectList<[GlobalVar]>;
// This attribute is applied to file-scope variables and must be compilable
// for the host device as well
let LangOpts = [SYCLExplicitSIMD];
let Documentation = [SYCLRegisterNumDocs];
let PragmaAttributeSupport = 0;
}

def SYCLScope : Attr {
// No spelling, as this attribute can't be created in the source code.
let Spellings = [];
Expand Down
11 changes: 11 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,17 @@ The SYCL kernel in the previous code sample meets these expectations.
}];
}

def SYCLRegisterNumDocs : Documentation {
let Category = DocCatVariable;
let Content = [{
The ``__attribute__((register_num(n)))`` attribute can be used to bind
"private globals" (SYCL private address space variables declared in the file
scope) to a particular register with number 'n'. Actual mapping of registers
to numbers is target-specific. For Intel GPUs 'n' is a byte offset in the
GRF.
}];
}

def C11NoReturnDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -10888,4 +10888,6 @@ def err_ext_int_bad_size : Error<"%select{signed|unsigned}0 _ExtInt must "
"have a bit size of at least %select{2|1}0">;
def err_ext_int_max_size : Error<"%select{signed|unsigned}0 _ExtInt of bit "
"sizes greater than %1 not supported">;
def err_esimd_glob_cant_init : Error<
"SYCL explicit SIMD does not permit private global variable to have an initializer">;
} // end of sema component.
8 changes: 8 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -12660,6 +12660,14 @@ class Sema final {
void finalizeSYCLDelayedAnalysis(const FunctionDecl *Caller,
const FunctionDecl *Callee,
SourceLocation Loc);

/// Tells whether given variable is a SYCL explicit SIMD extension's "private
/// global" variable - global variable in the private address space.
bool isSYCLEsimdPrivateGlobal(VarDecl *VDecl) {
return getLangOpts().SYCLIsDevice && getLangOpts().SYCLExplicitSIMD &&
VDecl->hasGlobalStorage() &&
(VDecl->getType().getAddressSpace() != LangAS::opencl_constant);
}
};

template <typename AttrType>
Expand Down
21 changes: 18 additions & 3 deletions clang/lib/CodeGen/CGSYCLRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,11 @@ static bool isPFWI(const FunctionDecl &FD) {
return FD.getName() == "parallel_for_work_item";
}

const char *WG_SCOPE_MD_ID = "work_group_scope";
const char *WI_SCOPE_MD_ID = "work_item_scope";
const char *PFWI_MD_ID = "parallel_for_work_item";
constexpr char WG_SCOPE_MD_ID[] = "work_group_scope";
constexpr char WI_SCOPE_MD_ID[] = "work_item_scope";
constexpr char PFWI_MD_ID[] = "parallel_for_work_item";
constexpr char ATTR_GENX_VOLATILE[] = "genx_volatile";
constexpr char ATTR_GENX_BYTE_OFFSET[] = "genx_byte_offset";

} // anonymous namespace

Expand Down Expand Up @@ -101,6 +103,19 @@ bool CGSYCLRuntime::actOnAutoVarEmit(CodeGenFunction &CGF, const VarDecl &D,
return true;
}

bool CGSYCLRuntime::actOnGlobalVarEmit(CodeGenModule &CGM, const VarDecl &D,
llvm::Value *Addr) {
SYCLRegisterNumAttr *RegAttr = D.getAttr<SYCLRegisterNumAttr>();
if (!RegAttr)
return false;
auto *GlobVar = cast<llvm::GlobalVariable>(Addr);
GlobVar->addAttribute(ATTR_GENX_VOLATILE);
GlobVar->addAttribute(ATTR_GENX_BYTE_OFFSET,
Twine(RegAttr->getNumber()).str());
// TODO consider reversing the error/success return values
return true;
}

bool Util::matchQualifiedTypeName(const CXXRecordDecl *RecTy,
ArrayRef<Util::DeclContextDesc> Scopes) {
// The idea: check the declaration context chain starting from the type
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CodeGen/CGSYCLRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class CGSYCLRuntime {
void emitWorkGroupLocalVarDecl(CodeGenFunction &CGF, const VarDecl &D);
bool actOnAutoVarEmit(CodeGenFunction &CGF, const VarDecl &D,
llvm::Value *Addr);
bool actOnGlobalVarEmit(CodeGenModule &CGM, const VarDecl &D,
llvm::Value *Addr);
};

} // namespace CodeGen
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4415,8 +4415,12 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
if (getCodeGenOpts().hasReducedDebugInfo())
DI->EmitGlobalVariable(GV, D);

if (LangOpts.SYCLIsDevice)
if (LangOpts.SYCLIsDevice) {
maybeEmitPipeStorageMetadata(D, GV, *this);
// Notify SYCL code generation infrastructure that a global variable is
// being generated.
getSYCLRuntime().actOnGlobalVarEmit(*this, *D, GV);
}
}

void CodeGenModule::EmitExternalVarDeclaration(const VarDecl *D) {
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11979,6 +11979,13 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
VDecl->setInvalidDecl();
return;
}
// In the SYCL explicit SIMD extension non constant "private globals" can't
// be explicitly initialized in the declaration.
if (isSYCLEsimdPrivateGlobal(VDecl)) {
Diag(VDecl->getLocation(), diag::err_esimd_glob_cant_init);
VDecl->setInvalidDecl();
return;
}

// The LoaderUninitialized attribute acts as a definition (of undef).
if (VDecl->hasAttr<LoaderUninitializedAttr>()) {
Expand Down Expand Up @@ -12578,6 +12585,11 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
if (getLangOpts().OpenCL &&
Var->getType().getAddressSpace() == LangAS::opencl_local)
return;
// In SYCL explicit SIMD extension "private global" variables can't be
// initialized even implicitly, so don't synthesize an implicit initializer.
if (isSYCLEsimdPrivateGlobal(Var))
return;

// C++03 [dcl.init]p9:
// If no initializer is specified for an object, and the
// object is of (possibly cv-qualified) non-POD class type (or
Expand Down
19 changes: 19 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4497,6 +4497,22 @@ static void handleSYCLDeviceIndirectlyCallableAttr(Sema &S, Decl *D,
handleSimpleAttribute<SYCLDeviceIndirectlyCallableAttr>(S, D, AL);
}

static void handleSYCLRegisterNumAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
auto *VD = cast<VarDecl>(D);
if (!VD->hasGlobalStorage()) {
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here)
<< AL << 0;
return;
}
if (!checkAttributeNumArgs(S, AL, 1))
return;
uint32_t RegNo = 0;
const Expr *E = AL.getArgAsExpr(0);
if (!checkUInt32Argument(S, AL, E, RegNo, 0, /*StrictlyUnsigned=*/true))
return;
D->addAttr(::new (S.Context) SYCLRegisterNumAttr(S.Context, AL, RegNo));
}

static void handleConstantAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
if (checkAttrMutualExclusion<CUDASharedAttr>(S, D, AL))
return;
Expand Down Expand Up @@ -7536,6 +7552,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
case ParsedAttr::AT_SYCLDeviceIndirectlyCallable:
handleSYCLDeviceIndirectlyCallableAttr(S, D, AL);
break;
case ParsedAttr::AT_SYCLRegisterNum:
handleSYCLRegisterNumAttr(S, D, AL);
break;
case ParsedAttr::AT_Format:
handleFormatAttr(S, D, AL);
break;
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,9 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
if (!IsConst && VD->getStorageClass() == SC_Static)
SYCLDiagIfDeviceCode(*Locs.begin(), diag::err_sycl_restrict)
<< Sema::KernelNonConstStaticDataVariable;
else if (!IsConst && VD->hasGlobalStorage() && !isa<ParmVarDecl>(VD))
// Non-const globals are allowed for SYCL explicit SIMD.
else if (!isSYCLEsimdPrivateGlobal(VD) && !IsConst &&
VD->hasGlobalStorage() && !isa<ParmVarDecl>(VD))
SYCLDiagIfDeviceCode(*Locs.begin(), diag::err_sycl_restrict)
<< Sema::KernelGlobalVariable;
}
Expand Down
14 changes: 14 additions & 0 deletions clang/test/CodeGenSYCL/esimd-private-global.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// RUN: %clang_cc1 -disable-llvm-passes -triple spir64-unknown-unknown-sycldevice \
// RUN: -fsycl -fsycl-is-device -fsycl-explicit-simd -emit-llvm %s -o - | \
// RUN: FileCheck %s

// This test checks that FE allows globals with register_num attribute in ESIMD mode.

__attribute__((opencl_private)) __attribute__((register_num(17))) int vc;
// CHECK: @vc = {{.+}} i32 0, align 4 #0

SYCL_EXTERNAL void init_vc(int x) {
vc = x;
// CHECK: store i32 %0, i32* @vc
}
// CHECK: attributes #0 = { "genx_byte_offset"="17" "genx_volatile" }
18 changes: 18 additions & 0 deletions clang/test/SemaSYCL/esimd-private-global.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// RUN: %clang_cc1 -fsycl -fsycl-is-device -fsycl-explicit-simd -fsyntax-only -verify -pedantic %s

// no error expected
__attribute__((opencl_private)) __attribute__((register_num(17))) int privGlob;

// expected-error@+1{{'register_num' attribute takes one argument}}
__attribute__((opencl_private)) __attribute__((register_num())) int privGlob1;

// expected-error@+1{{'register_num' attribute takes one argument}}
__attribute__((opencl_private)) __attribute__((register_num(10, 11))) int privGlob2;

// expected-error@+1{{SYCL explicit SIMD does not permit private global variable to have an initializer}}
__attribute__((opencl_private)) int privGlob3 = 10;

void foo() {
// expected-warning@+1{{'register_num' attribute only applies to global variables}}
__attribute__((register_num(17))) int privLoc;
}