Skip to content

Commit a445578

Browse files
committed
[SYCL][ESIMD] Implement "private globals" (file scope private vars) in Front End.
"private globals" are essentially private variables in a global scope. Marked with __attribute__((opencl_private)). Additionally can be marked with __attribute__((register_num(n))) to bind to a specific register. Signed-off-by: Konstantin S Bobrovsky <[email protected]>
1 parent 0539c10 commit a445578

File tree

10 files changed

+134
-5
lines changed

10 files changed

+134
-5
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,6 +1135,28 @@ def SYCLKernel : InheritableAttr {
11351135
let Documentation = [SYCLKernelDocs];
11361136
}
11371137

1138+
// Marks functions which must not be vectorized via horizontal SIMT widening,
1139+
// e.g. because the function is already vectorized. Used to mark SYCL
1140+
// explicit SIMD kernels and functions.
1141+
def SYCLSimd : InheritableAttr {
1142+
let Spellings = [GNU<"sycl_explicit_simd">];
1143+
let Subjects = SubjectList<[Function]>;
1144+
let LangOpts = [SYCLIsDevice];
1145+
let Documentation = [SYCLSimdDocs];
1146+
}
1147+
1148+
// Available in SYCL explicit SIMD extension. Binds a file scope private
1149+
// variable to a specific register.
1150+
def SYCLRegisterNum : InheritableAttr {
1151+
let Spellings = [GNU<"register_num">, Declspec<"register_num">];
1152+
let Args = [UnsignedArgument<"Number">];
1153+
let Subjects = SubjectList<[GlobalVar]>;
1154+
// This attribute is applied to file-scope variables and must be compilable
1155+
// for the host device as well
1156+
let LangOpts = [SYCLExplicitSIMD];
1157+
let Documentation = [SYCLRegisterNumDocs];
1158+
}
1159+
11381160
def SYCLScope : Attr {
11391161
// No spelling, as this attribute can't be created in the source code.
11401162
let Spellings = [];

clang/include/clang/Basic/AttrDocs.td

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,30 @@ The SYCL kernel in the previous code sample meets these expectations.
330330
}];
331331
}
332332

333+
def SYCLSimdDocs : Documentation {
334+
let Category = DocCatFunction;
335+
let Content = [{
336+
The ``__attribute__((sycl_explicit_simd))`` attribute is used by the device
337+
compiler front end to mark kernels and functions to be compiled and executed
338+
in the explicit SIMD mode. In this mode subgroup size is always 1 and
339+
explicit SIMD extensions - such as manual vectorization using wide vector
340+
data types and operations can be used. Compiler may decide to compile such
341+
functions using different different optimization and code generation
342+
pipeline.
343+
}];
344+
}
345+
346+
def SYCLRegisterNumDocs : Documentation {
347+
let Category = DocCatVariable;
348+
let Content = [{
349+
The ``__attribute__((register_num(n)))`` attribute can be used to bind
350+
"private globals" (SYCL private address space variables declared in the file
351+
scope) to a particular register with number 'n'. Actual mapping of registers
352+
to numbers is target-specific. For Intel GPUs 'n' is a byte offset in the
353+
GRF.
354+
}];
355+
}
356+
333357
def C11NoReturnDocs : Documentation {
334358
let Category = DocCatFunction;
335359
let Content = [{

clang/lib/CodeGen/CGSYCLRuntime.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,11 @@ static bool isPFWI(const FunctionDecl &FD) {
5050
return FD.getName() == "parallel_for_work_item";
5151
}
5252

53-
const char *WG_SCOPE_MD_ID = "work_group_scope";
54-
const char *WI_SCOPE_MD_ID = "work_item_scope";
55-
const char *PFWI_MD_ID = "parallel_for_work_item";
53+
constexpr char WG_SCOPE_MD_ID[] = "work_group_scope";
54+
constexpr char WI_SCOPE_MD_ID[] = "work_item_scope";
55+
constexpr char PFWI_MD_ID[] = "parallel_for_work_item";
56+
constexpr char ATTR_GENX_VOLATILE[] = "genx_volatile";
57+
constexpr char ATTR_GENX_BYTE_OFFSET[] = "genx_byte_offset";
5658

5759
} // anonymous namespace
5860

@@ -101,6 +103,19 @@ bool CGSYCLRuntime::actOnAutoVarEmit(CodeGenFunction &CGF, const VarDecl &D,
101103
return true;
102104
}
103105

106+
bool CGSYCLRuntime::actOnGlobalVarEmit(CodeGenModule &CGM, const VarDecl &D,
107+
llvm::Value *Addr) {
108+
SYCLRegisterNumAttr *RegAttr = D.getAttr<SYCLRegisterNumAttr>();
109+
if (!RegAttr)
110+
return false;
111+
auto *GlobVar = cast<llvm::GlobalVariable>(Addr);
112+
GlobVar->addAttribute(ATTR_GENX_VOLATILE);
113+
GlobVar->addAttribute(ATTR_GENX_BYTE_OFFSET,
114+
Twine(RegAttr->getNumber()).str());
115+
// TODO consider reversing the error/success return values
116+
return true;
117+
}
118+
104119
bool Util::matchQualifiedTypeName(const CXXRecordDecl *RecTy,
105120
ArrayRef<Util::DeclContextDesc> Scopes) {
106121
// The idea: check the declaration context chain starting from the type

clang/lib/CodeGen/CGSYCLRuntime.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ class CGSYCLRuntime {
3434
void emitWorkGroupLocalVarDecl(CodeGenFunction &CGF, const VarDecl &D);
3535
bool actOnAutoVarEmit(CodeGenFunction &CGF, const VarDecl &D,
3636
llvm::Value *Addr);
37+
bool actOnGlobalVarEmit(CodeGenModule &CGM, const VarDecl &D,
38+
llvm::Value *Addr);
3739
};
3840

3941
} // namespace CodeGen

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3885,6 +3885,13 @@ LangAS CodeGenModule::GetGlobalVarAddressSpace(const VarDecl *D) {
38853885
if (!D || D->getType().getAddressSpace() == LangAS::Default) {
38863886
return LangAS::opencl_global;
38873887
}
3888+
if (D)
3889+
AddrSpace = D->getType().getAddressSpace();
3890+
if (AddrSpace == LangAS::opencl_private ||
3891+
AddrSpace == LangAS::opencl_local)
3892+
// SYCL explicit SIMD path: recognize globals in private or local address
3893+
// space.
3894+
return AddrSpace;
38883895
}
38893896

38903897
if (LangOpts.CUDA && LangOpts.CUDAIsDevice) {
@@ -4415,8 +4422,12 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
44154422
if (getCodeGenOpts().hasReducedDebugInfo())
44164423
DI->EmitGlobalVariable(GV, D);
44174424

4418-
if (LangOpts.SYCLIsDevice)
4425+
if (LangOpts.SYCLIsDevice) {
44194426
maybeEmitPipeStorageMetadata(D, GV, *this);
4427+
// Notify SYCL code generation infrastructure that a global variable is
4428+
// being generated.
4429+
getSYCLRuntime().actOnGlobalVarEmit(*this, *D, GV);
4430+
}
44204431
}
44214432

44224433
void CodeGenModule::EmitExternalVarDeclaration(const VarDecl *D) {

clang/lib/Sema/SemaDecl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12578,6 +12578,14 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
1257812578
if (getLangOpts().OpenCL &&
1257912579
Var->getType().getAddressSpace() == LangAS::opencl_local)
1258012580
return;
12581+
// In SYCL ESIMD device code non-constant file scope variables can't be
12582+
// initialized.
12583+
// TODO add proper diagnostics for both SYCL and OpenCL paths
12584+
if (getLangOpts().SYCLExplicitSIMD && getLangOpts().SYCLIsDevice &&
12585+
Var->isFileVarDecl() && Var->hasGlobalStorage() &&
12586+
(Var->getType().getAddressSpace() != LangAS::opencl_constant))
12587+
return;
12588+
1258112589
// C++03 [dcl.init]p9:
1258212590
// If no initializer is specified for an object, and the
1258312591
// object is of (possibly cv-qualified) non-POD class type (or

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4497,6 +4497,21 @@ static void handleSYCLDeviceIndirectlyCallableAttr(Sema &S, Decl *D,
44974497
handleSimpleAttribute<SYCLDeviceIndirectlyCallableAttr>(S, D, AL);
44984498
}
44994499

4500+
static void handleSYCLRegisterNumAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
4501+
auto *VD = cast<VarDecl>(D);
4502+
if (!VD->hasGlobalStorage()) {
4503+
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here)
4504+
<< AL << 0;
4505+
return;
4506+
}
4507+
assert(AL.getNumArgs() == 1);
4508+
uint32_t RegNo = 0;
4509+
const Expr *E = AL.getArgAsExpr(0);
4510+
if (!checkUInt32Argument(S, AL, E, RegNo, 0, /*StrictlyUnsigned=*/true))
4511+
return;
4512+
D->addAttr(::new (S.Context) SYCLRegisterNumAttr(S.Context, AL, RegNo));
4513+
}
4514+
45004515
static void handleConstantAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
45014516
if (checkAttrMutualExclusion<CUDASharedAttr>(S, D, AL))
45024517
return;
@@ -7536,6 +7551,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
75367551
case ParsedAttr::AT_SYCLDeviceIndirectlyCallable:
75377552
handleSYCLDeviceIndirectlyCallableAttr(S, D, AL);
75387553
break;
7554+
case ParsedAttr::AT_SYCLRegisterNum:
7555+
handleSYCLRegisterNumAttr(S, D, AL);
7556+
break;
75397557
case ParsedAttr::AT_Format:
75407558
handleFormatAttr(S, D, AL);
75417559
break;

clang/lib/Sema/SemaExpr.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,9 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
221221
if (!IsConst && VD->getStorageClass() == SC_Static)
222222
SYCLDiagIfDeviceCode(*Locs.begin(), diag::err_sycl_restrict)
223223
<< Sema::KernelNonConstStaticDataVariable;
224-
else if (!IsConst && VD->hasGlobalStorage() && !isa<ParmVarDecl>(VD))
224+
// Non-const globals are allowed for CM.
225+
else if (!getLangOpts().SYCLExplicitSIMD && !IsConst &&
226+
VD->hasGlobalStorage() && !isa<ParmVarDecl>(VD))
225227
SYCLDiagIfDeviceCode(*Locs.begin(), diag::err_sycl_restrict)
226228
<< Sema::KernelGlobalVariable;
227229
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %clang_cc1 -disable-llvm-passes -triple spir64-unknown-unknown-sycldevice \
2+
// RUN: -fsycl -fsycl-is-device -fsycl-explicit-simd -emit-llvm %s -o - | \
3+
// RUN: FileCheck %s
4+
5+
// This test checks that FE allows globals with register_num attribute in ESIMD mode.
6+
7+
__attribute__((opencl_private)) __attribute__((register_num(17))) int vc;
8+
9+
// CHECK-DAG: @vc = {{.+}} i32 0, align 4 #0
10+
// CHECK-DAG: attributes #0 = { "genx_byte_offset"="17" "genx_volatile" }
11+
12+
SYCL_EXTERNAL void init_vc(int x) {
13+
vc = x;
14+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %clang_cc1 -fsycl -fsycl-is-device -fsycl-explicit-simd -fsyntax-only -verify -pedantic %s
2+
3+
#define ESIMD_PRIVATE __attribute__((opencl_private))
4+
#define ESIMD_REGISTER_NUM(n) __attribute__((register_num(n)))
5+
6+
// no error expected
7+
ESIMD_PRIVATE ESIMD_REGISTER_NUM(17) int privGlob;
8+
9+
void foo() {
10+
// expected-warning@+1{{'register_num' attribute only applies to global variables}}
11+
ESIMD_REGISTER_NUM(17)
12+
int privLoc;
13+
}

0 commit comments

Comments
 (0)