diff --git a/clang/include/clang/Basic/AddressSpaces.h b/clang/include/clang/Basic/AddressSpaces.h --- a/clang/include/clang/Basic/AddressSpaces.h +++ b/clang/include/clang/Basic/AddressSpaces.h @@ -56,6 +56,9 @@ ptr32_uptr, ptr64, + // HLSL specific address spaces. + hlsl_groupshared, + // This denotes the count of language-specific address spaces and also // the offset added to the target-specific address spaces, which are usually // specified by address space attributes __attribute__(address_space(n))). diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -4041,6 +4041,11 @@ let Documentation = [InternalOnly]; } +def HLSLGroupSharedAddressSpace : TypeAttr { + let Spellings = [Keyword<"groupshared">, Clang<"groupshared">]; + let Documentation = [HLSLGroupSharedAddressSpaceDocs]; +} + def RandomizeLayout : InheritableAttr { let Spellings = [GCC<"randomize_layout">]; let Subjects = SubjectList<[Record]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -6618,6 +6618,24 @@ }]; } +def HLSLGroupSharedAddressSpaceDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +HLSL enables threads of a compute shader to exchange values via shared memory. +HLSL provides barrier primitives such as GroupMemoryBarrierWithGroupSync, +and so on to ensure the correct ordering of reads and writes to shared memory +in the shader and to avoid data races. +Here's an example to declare a groupshared variable. +.. code-block:: c++ + + groupshared GSData data[5*5*1]; + + + +The full documentation is available here: https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-variable-syntax#group-shared + }]; +} + def AnnotateTypeDocs : Documentation { let Category = DocCatType; let Heading = "annotate_type"; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11700,7 +11700,8 @@ def err_hlsl_operator_unsupported : Error< "the '%select{&|*|->}0' operator is unsupported in HLSL">; - +def err_hlsl_attribute_address_function_return_type : Error< + "function return type may not be qualified with an address space">; // Layout randomization diagnostics. def err_non_designated_init_used : Error< "a randomized struct can only be initialized with a designated initializer">; diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -611,6 +611,7 @@ // HLSL keywords. KEYWORD(cbuffer , KEYHLSL) KEYWORD(tbuffer , KEYHLSL) +KEYWORD(groupshared , KEYHLSL) // OpenMP Type Traits UNARY_EXPR_OR_TYPE_TRAIT(__builtin_omp_required_simd_align, OpenMPRequiredSimdAlign, KEYALL) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2932,6 +2932,7 @@ void ParseOpenCLQualifiers(ParsedAttributes &Attrs); void ParseNullabilityTypeSpecifiers(ParsedAttributes &attrs); void ParseCUDAFunctionAttributes(ParsedAttributes &attrs); + void ParseHLSLQualifiers(ParsedAttributes &Attrs); VersionTuple ParseVersionTuple(SourceRange &Range); void ParseAvailabilityAttribute(IdentifierInfo &Availability, diff --git a/clang/include/clang/Sema/ParsedAttr.h b/clang/include/clang/Sema/ParsedAttr.h --- a/clang/include/clang/Sema/ParsedAttr.h +++ b/clang/include/clang/Sema/ParsedAttr.h @@ -712,6 +712,17 @@ } } + /// If this is an HLSL address space attribute, returns its representation + /// in LangAS, otherwise returns default address space. + LangAS asHLSLLangAS() const { + switch (getParsedKind()) { + case ParsedAttr::AT_HLSLGroupSharedAddressSpace: + return LangAS::hlsl_groupshared; + default: + return LangAS::Default; + } + } + AttributeCommonInfo::Kind getKind() const { return AttributeCommonInfo::Kind(Info.AttrKind); } diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -949,7 +949,8 @@ 0, // sycl_private 10, // ptr32_sptr 11, // ptr32_uptr - 12 // ptr64 + 12, // ptr64 + 13, // hlsl_groupshared }; return &FakeAddrSpaceMap; } else { diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1727,6 +1727,7 @@ case attr::OpenCLLocalAddressSpace: case attr::OpenCLConstantAddressSpace: case attr::OpenCLGenericAddressSpace: + case attr::HLSLGroupSharedAddressSpace: // FIXME: Update printAttributedBefore to print these once we generate // AttributedType nodes for them. break; @@ -2222,6 +2223,8 @@ return "__uptr __ptr32"; case LangAS::ptr64: return "__ptr64"; + case LangAS::hlsl_groupshared: + return "groupshared"; default: return std::to_string(toTargetAddressSpace(AS)); } diff --git a/clang/lib/Basic/Targets/AMDGPU.cpp b/clang/lib/Basic/Targets/AMDGPU.cpp --- a/clang/lib/Basic/Targets/AMDGPU.cpp +++ b/clang/lib/Basic/Targets/AMDGPU.cpp @@ -55,7 +55,8 @@ Private, // sycl_private Generic, // ptr32_sptr Generic, // ptr32_uptr - Generic // ptr64 + Generic, // ptr64 + Generic, // hlsl_gourpshared }; const LangASMap AMDGPUTargetInfo::AMDGPUDefIsPrivMap = { @@ -71,14 +72,15 @@ Constant, // cuda_constant Local, // cuda_shared // SYCL address space values for this map are dummy - Generic, // sycl_global - Generic, // sycl_global_device - Generic, // sycl_global_host - Generic, // sycl_local - Generic, // sycl_private - Generic, // ptr32_sptr - Generic, // ptr32_uptr - Generic // ptr64 + Generic, // sycl_global + Generic, // sycl_global_device + Generic, // sycl_global_host + Generic, // sycl_local + Generic, // sycl_private + Generic, // ptr32_sptr + Generic, // ptr32_uptr + Generic, // ptr64 + Generic, // hlsl_gourpshared }; } // namespace targets diff --git a/clang/lib/Basic/Targets/DirectX.h b/clang/lib/Basic/Targets/DirectX.h --- a/clang/lib/Basic/Targets/DirectX.h +++ b/clang/lib/Basic/Targets/DirectX.h @@ -40,7 +40,8 @@ 0, // sycl_private 0, // ptr32_sptr 0, // ptr32_uptr - 0 // ptr64 + 0, // ptr64 + 3, // hlsl_gourpshared }; class LLVM_LIBRARY_VISIBILITY DirectXTargetInfo : public TargetInfo { diff --git a/clang/lib/Basic/Targets/NVPTX.h b/clang/lib/Basic/Targets/NVPTX.h --- a/clang/lib/Basic/Targets/NVPTX.h +++ b/clang/lib/Basic/Targets/NVPTX.h @@ -42,7 +42,8 @@ 0, // sycl_private 0, // ptr32_sptr 0, // ptr32_uptr - 0 // ptr64 + 0, // ptr64 + 0, // hlsl_gourpshared }; /// The DWARF address class. Taken from diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h --- a/clang/lib/Basic/Targets/SPIR.h +++ b/clang/lib/Basic/Targets/SPIR.h @@ -42,7 +42,8 @@ 0, // sycl_private 0, // ptr32_sptr 0, // ptr32_uptr - 0 // ptr64 + 0, // ptr64 + 0, // hlsl_gourpshared }; // Used by both the SPIR and SPIR-V targets. @@ -71,7 +72,8 @@ 0, // sycl_private 0, // ptr32_sptr 0, // ptr32_uptr - 0 // ptr64 + 0, // ptr64 + 0, // hlsl_gourpshared }; // Base class for SPIR and SPIR-V target info. diff --git a/clang/lib/Basic/Targets/TCE.h b/clang/lib/Basic/Targets/TCE.h --- a/clang/lib/Basic/Targets/TCE.h +++ b/clang/lib/Basic/Targets/TCE.h @@ -50,6 +50,7 @@ 0, // ptr32_sptr 0, // ptr32_uptr 0, // ptr64 + 0, // hlsl_gourpshared }; class LLVM_LIBRARY_VISIBILITY TCETargetInfo : public TargetInfo { diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -43,7 +43,8 @@ 0, // sycl_private 270, // ptr32_sptr 271, // ptr32_uptr - 272 // ptr64 + 272, // ptr64 + 0, // hlsl_gourpshared }; // X86 target abstract base class; x86-32 and x86-64 are very close, so diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -914,6 +914,13 @@ ParsedAttr::AS_Keyword); } +void Parser::ParseHLSLQualifiers(ParsedAttributes &Attrs) { + IdentifierInfo *AttrName = Tok.getIdentifierInfo(); + SourceLocation AttrNameLoc = Tok.getLocation(); + Attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0, + ParsedAttr::AS_Keyword); +} + void Parser::ParseNullabilityTypeSpecifiers(ParsedAttributes &attrs) { // Treat these like attributes, even though they're type specifiers. while (true) { @@ -4329,6 +4336,10 @@ ParseOpenCLQualifiers(DS.getAttributes()); break; + case tok::kw_groupshared: + ParseHLSLQualifiers(DS.getAttributes()); + break; + case tok::less: // GCC ObjC supports types like "" as a synonym for // "id". This is hopelessly old fashioned and dangerous, @@ -5352,6 +5363,8 @@ case tok::kw___read_only: case tok::kw___read_write: case tok::kw___write_only: + + case tok::kw_groupshared: return true; case tok::kw_private: @@ -5592,6 +5605,7 @@ #define GENERIC_IMAGE_TYPE(ImgType, Id) case tok::kw_##ImgType##_t: #include "clang/Basic/OpenCLImageTypes.def" + case tok::kw_groupshared: return true; case tok::kw_private: @@ -5819,6 +5833,10 @@ ParseOpenCLQualifiers(DS.getAttributes()); break; + case tok::kw_groupshared: + ParseHLSLQualifiers(DS.getAttributes()); + break; + case tok::kw___unaligned: isInvalid = DS.SetTypeQual(DeclSpec::TQ_unaligned, Loc, PrevSpec, DiagID, getLangOpts()); diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -1404,6 +1404,12 @@ ConsumeToken(); } + // Parse HLSL addr space attribute. + if (Tok.is(tok::kw_groupshared)) { + ParseHLSLQualifiers(DS.getAttributes()); + ConsumeToken(); + } + SourceLocation FunLocalRangeEnd = DeclEndLoc; // Parse trailing-return-type[opt]. @@ -1474,7 +1480,8 @@ tok::kw_constexpr, tok::kw_consteval, tok::kw_static, tok::kw___private, tok::kw___global, tok::kw___local, tok::kw___constant, tok::kw___generic, - tok::kw_requires, tok::kw_noexcept) || + tok::kw_groupshared, tok::kw_requires, + tok::kw_noexcept) || (Tok.is(tok::l_square) && NextToken().is(tok::l_square))) { if (!getLangOpts().CPlusPlus2b) // It's common to forget that one needs '()' before 'mutable', an diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -1423,6 +1423,9 @@ // OpenCL pipe case tok::kw_pipe: + // HLSL address space qualifiers + case tok::kw_groupshared: + // GNU case tok::kw_restrict: case tok::kw__Complex: diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14924,6 +14924,12 @@ diag::err_func_def_incomplete_result)) FD->setInvalidDecl(); + if (ResultType.getAddressSpace() != LangAS::Default && getLangOpts().HLSL) { + Diag(FD->getLocation(), + diag::err_hlsl_attribute_address_function_return_type); + FD->setInvalidDecl(); + } + if (FnBodyScope) PushDeclContext(FnBodyScope, FD); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -2898,6 +2898,8 @@ if (T.isVolatileQualified() && getLangOpts().CPlusPlus20) Diag(Loc, diag::warn_deprecated_volatile_return) << T; + if (T.getAddressSpace() != LangAS::Default && getLangOpts().HLSL) + return true; return false; } @@ -6775,6 +6777,8 @@ // The keyword-based type attributes imply which address space to use. ASIdx = S.getLangOpts().SYCLIsDevice ? Attr.asSYCLLangAS() : Attr.asOpenCLLangAS(); + if (S.getLangOpts().HLSL) + ASIdx = Attr.asHLSLLangAS(); if (ASIdx == LangAS::Default) llvm_unreachable("Invalid address space"); @@ -8388,6 +8392,7 @@ case ParsedAttr::AT_OpenCLLocalAddressSpace: case ParsedAttr::AT_OpenCLConstantAddressSpace: case ParsedAttr::AT_OpenCLGenericAddressSpace: + case ParsedAttr::AT_HLSLGroupSharedAddressSpace: case ParsedAttr::AT_AddressSpace: HandleAddressSpaceTypeAttribute(type, attr, state); attr.setUsedAsTypeAttr(); diff --git a/clang/test/AST/HLSL/group_shared.hlsl b/clang/test/AST/HLSL/group_shared.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/AST/HLSL/group_shared.hlsl @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -ast-dump -o - %s | FileCheck %s + +//CHECK:VarDecl 0x[[A:[0-9a-f]+]] <{{.*}} col:24> col:20 used a 'groupshared float[10]' + groupshared float a[10]; + +// CHECK:FunctionDecl 0x{{[0-9a-f]+}} <{{.*}}> line:[[@LINE+2]]:7 main 'void ()' + [numthreads(8,8,1)] + void main() { +// CHECK:BinaryOperator 0x{{[0-9a-f]+}} <{{.*}}> 'groupshared float' lvalue '=' +// CHECK:ArraySubscriptExpr 0x{{[0-9a-f]+}} 'groupshared float' lvalue +// CHECK:ImplicitCastExpr 0x{{[0-9a-f]+}} 'groupshared float *' +// CHECK:DeclRefExpr 0x{{[0-9a-f]+}} 'groupshared float[10]' lvalue Var 0x[[A]] 'a' 'groupshared float[10]' +// CHECK:IntegerLiteral 0x{{[0-9a-f]+}} 'int' 0 +// CHECK:ImplicitCastExpr 0x{{[0-9a-f]+}} 'float' +// CHECK:IntegerLiteral 0x{{[0-9a-f]+}} 'int' 1 + a[0] = 1; + } + + +// CHECK:HLSLNumThreadsAttr 0x{{[0-9a-f]+}} <{{.*}}> 8 8 1 diff --git a/clang/test/CodeGenHLSL/group_shared.hlsl b/clang/test/CodeGenHLSL/group_shared.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenHLSL/group_shared.hlsl @@ -0,0 +1,15 @@ + +// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \ +// RUN: dxil-pc-shadermodel6.3-library %s \ +// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s + +// Make sure groupshared translated into address space 3. +// CHECK:@"?a@@3PAMA" = addrspace(3) global [10 x float] + + groupshared float a[10]; + + [numthreads(8,8,1)] + void main() { + a[0] = 1; + } + diff --git a/clang/test/SemaHLSL/group_shared.hlsl b/clang/test/SemaHLSL/group_shared.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/SemaHLSL/group_shared.hlsl @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify + + groupshared float a[10]; + + [numthreads(8,8,1)] + void main() { + a[0] = 1; + // expected-error@+1 {{automatic variable qualified with an address space}} + groupshared float b; + } + +// expected-error@+1 {{function return type may not be qualified with an address space}} + groupshared float foo() { + static groupshared float foo0; + return 1; + } +// expected-error@+1 {{function return type may not be qualified with an address space}} + groupshared void bar() { + extern groupshared float bar0; + } + + struct S { + // expected-error@+1 {{field may not be qualified with an address space}} + groupshared float f; + static groupshared float g; + }; + + // expected-error@+1 {{parameter may not be qualified with an address space}} + float foo2(groupshared float a) { + return a; + } + +// expected-note@+2 {{parameter may not be qualified with an address space}} +template + T tfoo(T t) { + return t; + } + // expected-warning@+1 {{alias declarations are a C++11 extension}} + using GSF = groupshared float; + GSF gs; + // expected-error@+1 {{no matching function for call to 'tfoo'}} + GSF gs2 = tfoo(gs); diff --git a/clang/test/SemaTemplate/address_space-dependent.cpp b/clang/test/SemaTemplate/address_space-dependent.cpp --- a/clang/test/SemaTemplate/address_space-dependent.cpp +++ b/clang/test/SemaTemplate/address_space-dependent.cpp @@ -43,7 +43,7 @@ template void tooBig() { - __attribute__((address_space(I))) int *bounds; // expected-error {{address space is larger than the maximum supported (8388588)}} + __attribute__((address_space(I))) int *bounds; // expected-error {{address space is larger than the maximum supported (8388587)}} } template