diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index b65c2697dd1a1..c8858f821acdb 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -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 = []; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 1a7bca252912b..07f40b0cfe69a 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -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 = [{ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d4dbe93fe0f30..0af416ec759a6 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index c1297cf19b688..d527dc65ccb06 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -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 diff --git a/clang/lib/CodeGen/CGSYCLRuntime.cpp b/clang/lib/CodeGen/CGSYCLRuntime.cpp index a827f6fb82f91..40b5c61ad581b 100644 --- a/clang/lib/CodeGen/CGSYCLRuntime.cpp +++ b/clang/lib/CodeGen/CGSYCLRuntime.cpp @@ -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 @@ -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(); + if (!RegAttr) + return false; + auto *GlobVar = cast(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 Scopes) { // The idea: check the declaration context chain starting from the type diff --git a/clang/lib/CodeGen/CGSYCLRuntime.h b/clang/lib/CodeGen/CGSYCLRuntime.h index 12885f42aae85..71bc45599516f 100644 --- a/clang/lib/CodeGen/CGSYCLRuntime.h +++ b/clang/lib/CodeGen/CGSYCLRuntime.h @@ -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 diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index c656e2d10f21e..3a429bdebec01 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -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) { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index b3944d3560754..efea9a9dc5049 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -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()) { @@ -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 diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 08dfc66729f96..c56d4148adbe9 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4497,6 +4497,22 @@ static void handleSYCLDeviceIndirectlyCallableAttr(Sema &S, Decl *D, handleSimpleAttribute(S, D, AL); } +static void handleSYCLRegisterNumAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + auto *VD = cast(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(S, D, AL)) return; @@ -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; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 4f61ebdb828e0..2cb1ac15fdeb7 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -221,7 +221,9 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef Locs, if (!IsConst && VD->getStorageClass() == SC_Static) SYCLDiagIfDeviceCode(*Locs.begin(), diag::err_sycl_restrict) << Sema::KernelNonConstStaticDataVariable; - else if (!IsConst && VD->hasGlobalStorage() && !isa(VD)) + // Non-const globals are allowed for SYCL explicit SIMD. + else if (!isSYCLEsimdPrivateGlobal(VD) && !IsConst && + VD->hasGlobalStorage() && !isa(VD)) SYCLDiagIfDeviceCode(*Locs.begin(), diag::err_sycl_restrict) << Sema::KernelGlobalVariable; } diff --git a/clang/test/CodeGenSYCL/esimd-private-global.cpp b/clang/test/CodeGenSYCL/esimd-private-global.cpp new file mode 100644 index 0000000000000..d5c475b5656af --- /dev/null +++ b/clang/test/CodeGenSYCL/esimd-private-global.cpp @@ -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" } diff --git a/clang/test/SemaSYCL/esimd-private-global.cpp b/clang/test/SemaSYCL/esimd-private-global.cpp new file mode 100644 index 0000000000000..3023385e48e8d --- /dev/null +++ b/clang/test/SemaSYCL/esimd-private-global.cpp @@ -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; +}