diff --git a/clang/include/clang/Basic/AddressSpaces.h b/clang/include/clang/Basic/AddressSpaces.h index 7280b8fc923d2..a941805423bca 100644 --- a/clang/include/clang/Basic/AddressSpaces.h +++ b/clang/include/clang/Basic/AddressSpaces.h @@ -62,6 +62,7 @@ enum class LangAS : unsigned { hlsl_private, hlsl_device, hlsl_input, + hlsl_output, hlsl_push_constant, // Wasm specific address spaces. diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index f697262d603bf..5b0215f466e62 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -146,6 +146,12 @@ def HLSLInputBuiltin S->getType().isConstQualified()}], "static const globals">; +def HLSLOutputBuiltin + : SubsetSubjecthasGlobalStorage() && + S->getStorageClass() == StorageClass::SC_Static && + !S->getType().isConstQualified()}], + "non-const static globals">; + def GlobalVar : SubsetSubjecthasGlobalStorage()}], "global variables">; @@ -5217,6 +5223,14 @@ def HLSLVkExtBuiltinInput : InheritableAttr { let Documentation = [HLSLVkExtBuiltinInputDocs]; } +def HLSLVkExtBuiltinOutput : InheritableAttr { + let Spellings = [CXX11<"vk", "ext_builtin_output">]; + let Args = [UnsignedArgument<"BuiltIn">]; + let Subjects = SubjectList<[HLSLOutputBuiltin], ErrorDiag>; + let LangOpts = [HLSL]; + let Documentation = [HLSLVkExtBuiltinOutputDocs]; +} + def HLSLVkPushConstant : InheritableAttr { let Spellings = [CXX11<"vk", "push_constant">]; let Args = []; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 555a54fd51a89..d93dcf5e9b2fd 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -8978,6 +8978,29 @@ https://github.com/microsoft/hlsl-specs/blob/main/proposals/0011-inline-spirv.md }]; } +def HLSLVkExtBuiltinOutputDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +Vulkan shaders have `Output` builtins. Those variables are externally +visible to the driver/pipeline, but each copy is private to the current +lane. + +Those builtins can be declared using the `[[vk::ext_builtin_output]]` +attribute like follows: + +.. code-block:: c++ + + [[vk::ext_builtin_output(/* Position */ 0)]] + static float4 position; + +This variable will be lowered into a module-level variable, with the `Output` +storage class, and the `BuiltIn 0` decoration. + +The full documentation for this inline SPIR-V attribute can be found here: +https://github.com/microsoft/hlsl-specs/blob/main/proposals/0011-inline-spirv.md + }]; +} + def HLSLVkPushConstantDocs : Documentation { let Category = DocCatVariable; let Content = [{ diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index cf9b8c39b109e..6f62fbc635d9c 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -190,6 +190,7 @@ class SemaHLSL : public SemaBase { void handleSemanticAttr(Decl *D, const ParsedAttr &AL); void handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL); + void handleVkExtBuiltinOutputAttr(Decl *D, const ParsedAttr &AL); void handleVkPushConstantAttr(Decl *D, const ParsedAttr &AL); bool CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index a351bc34af798..78524c205eb3d 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -101,6 +101,7 @@ bool Qualifiers::isTargetAddressSpaceSupersetOf(LangAS A, LangAS B, (A == LangAS::Default && B == LangAS::hlsl_private) || (A == LangAS::Default && B == LangAS::hlsl_device) || (A == LangAS::Default && B == LangAS::hlsl_input) || + (A == LangAS::Default && B == LangAS::hlsl_output) || (A == LangAS::Default && B == LangAS::hlsl_push_constant) || // Conversions from target specific address spaces may be legal // depending on the target information. diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 3375630827501..80f5b90ba35c4 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -2707,6 +2707,8 @@ std::string Qualifiers::getAddrSpaceAsString(LangAS AS) { return "hlsl_device"; case LangAS::hlsl_input: return "hlsl_input"; + case LangAS::hlsl_output: + return "hlsl_output"; case LangAS::hlsl_push_constant: return "hlsl_push_constant"; case LangAS::wasm_funcref: diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index 38edc65801384..e6ae89e0948c5 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -52,7 +52,8 @@ static const LangASMap FakeAddrSpaceMap = { 15, // hlsl_private 16, // hlsl_device 17, // hlsl_input - 18, // hlsl_push_constant + 18, // hlsl_output + 19, // hlsl_push_constant 20, // wasm_funcref }; diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h index f9dffed8769ef..0a29bad81939b 100644 --- a/clang/lib/Basic/Targets/AArch64.h +++ b/clang/lib/Basic/Targets/AArch64.h @@ -49,6 +49,7 @@ static const unsigned ARM64AddrSpaceMap[] = { 0, // hlsl_private 0, // hlsl_device 0, // hlsl_input + 0, // hlsl_output 0, // hlsl_push_constant // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. diff --git a/clang/lib/Basic/Targets/AMDGPU.cpp b/clang/lib/Basic/Targets/AMDGPU.cpp index c9cd01080fd0c..2c05b3c6512fb 100644 --- a/clang/lib/Basic/Targets/AMDGPU.cpp +++ b/clang/lib/Basic/Targets/AMDGPU.cpp @@ -53,6 +53,7 @@ const LangASMap AMDGPUTargetInfo::AMDGPUDefIsGenMap = { llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_private llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_device llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_input + llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_output llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_push_constant }; @@ -82,6 +83,7 @@ const LangASMap AMDGPUTargetInfo::AMDGPUDefIsPrivMap = { llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_private llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_device llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_input + llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_output llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_push_constant }; } // namespace targets diff --git a/clang/lib/Basic/Targets/DirectX.h b/clang/lib/Basic/Targets/DirectX.h index 3976238bede4a..8b21b86bac264 100644 --- a/clang/lib/Basic/Targets/DirectX.h +++ b/clang/lib/Basic/Targets/DirectX.h @@ -46,6 +46,7 @@ static const unsigned DirectXAddrSpaceMap[] = { 0, // hlsl_private 0, // hlsl_device 0, // hlsl_input + 0, // hlsl_output 0, // hlsl_push_constant // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. diff --git a/clang/lib/Basic/Targets/NVPTX.h b/clang/lib/Basic/Targets/NVPTX.h index 2e142cc95f9bb..69ee20f38343b 100644 --- a/clang/lib/Basic/Targets/NVPTX.h +++ b/clang/lib/Basic/Targets/NVPTX.h @@ -50,6 +50,7 @@ static const unsigned NVPTXAddrSpaceMap[] = { 0, // hlsl_private 0, // hlsl_device 0, // hlsl_input + 0, // hlsl_output 0, // hlsl_push_constant // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h index 11a45d17e4aa4..6f4154fcf311f 100644 --- a/clang/lib/Basic/Targets/SPIR.h +++ b/clang/lib/Basic/Targets/SPIR.h @@ -52,6 +52,7 @@ static const unsigned SPIRDefIsPrivMap[] = { 10, // hlsl_private 11, // hlsl_device 7, // hlsl_input + 8, // hlsl_output 13, // hlsl_push_constant // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. @@ -89,6 +90,7 @@ static const unsigned SPIRDefIsGenMap[] = { 10, // hlsl_private 11, // hlsl_device 7, // hlsl_input + 8, // hlsl_output 13, // hlsl_push_constant // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h index 2bcf75deb0a91..00f7d7a055b24 100644 --- a/clang/lib/Basic/Targets/SystemZ.h +++ b/clang/lib/Basic/Targets/SystemZ.h @@ -46,6 +46,7 @@ static const unsigned ZOSAddressMap[] = { 0, // hlsl_private 0, // hlsl_device 0, // hlsl_input + 0, // hlsl_output 0, // hlsl_push_constant 0 // wasm_funcref }; diff --git a/clang/lib/Basic/Targets/TCE.h b/clang/lib/Basic/Targets/TCE.h index 161025378c471..2b22f4c4ec724 100644 --- a/clang/lib/Basic/Targets/TCE.h +++ b/clang/lib/Basic/Targets/TCE.h @@ -55,6 +55,7 @@ static const unsigned TCEOpenCLAddrSpaceMap[] = { 0, // hlsl_private 0, // hlsl_device 0, // hlsl_input + 0, // hlsl_output 0, // hlsl_push_constant // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h index 8128dd96068a0..808342485cad0 100644 --- a/clang/lib/Basic/Targets/WebAssembly.h +++ b/clang/lib/Basic/Targets/WebAssembly.h @@ -46,6 +46,7 @@ static const unsigned WebAssemblyAddrSpaceMap[] = { 0, // hlsl_private 0, // hlsl_device 0, // hlsl_input + 0, // hlsl_output 0, // hlsl_push_constant 20, // wasm_funcref }; diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h index bd9127b2ec8b5..c7afcc7c86053 100644 --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -50,6 +50,7 @@ static const unsigned X86AddrSpaceMap[] = { 0, // hlsl_private 0, // hlsl_device 0, // hlsl_input + 0, // hlsl_output 0, // hlsl_push_constant // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index c8a0ab34ae848..4e6f853890c83 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -1163,6 +1163,8 @@ void CGHLSLRuntime::handleGlobalVarDefinition(const VarDecl *VD, llvm::GlobalVariable *GV) { if (auto Attr = VD->getAttr()) addSPIRVBuiltinDecoration(GV, Attr->getBuiltIn()); + if (auto Attr = VD->getAttr()) + addSPIRVBuiltinDecoration(GV, Attr->getBuiltIn()); } llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) { diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index daaa846bf42bc..2be7417e8b391 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -4439,6 +4439,17 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) { return; } } + + // HLSL extern globals can be read/written to by the pipeline. Those + // are declared, but never defined. + if (LangOpts.HLSL) { + if (VD->getStorageClass() == SC_Extern) { + auto GV = cast(GetAddrOfGlobalVar(VD)); + getHLSLRuntime().handleGlobalVarDefinition(VD, GV); + return; + } + } + // If this declaration may have caused an inline variable definition to // change linkage, make sure that it's emitted. if (Context.getInlineVariableDefinitionKind(VD) == diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 3c1e00c206dc3..131559f2cf786 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7953,6 +7953,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_HLSLVkExtBuiltinInput: S.HLSL().handleVkExtBuiltinInputAttr(D, AL); break; + case ParsedAttr::AT_HLSLVkExtBuiltinOutput: + S.HLSL().handleVkExtBuiltinOutputAttr(D, AL); + break; case ParsedAttr::AT_HLSLVkPushConstant: S.HLSL().handleVkPushConstantAttr(D, AL); break; diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 823d312df296e..1fe5b62afc073 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -1842,6 +1842,14 @@ void SemaHLSL::handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL) { HLSLVkExtBuiltinInputAttr(getASTContext(), AL, ID)); } +void SemaHLSL::handleVkExtBuiltinOutputAttr(Decl *D, const ParsedAttr &AL) { + uint32_t ID; + if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), ID)) + return; + D->addAttr(::new (getASTContext()) + HLSLVkExtBuiltinOutputAttr(getASTContext(), AL, ID)); +} + void SemaHLSL::handleVkPushConstantAttr(Decl *D, const ParsedAttr &AL) { D->addAttr(::new (getASTContext()) HLSLVkPushConstantAttr(getASTContext(), AL)); @@ -4857,6 +4865,19 @@ void SemaHLSL::deduceAddressSpace(VarDecl *Decl) { return; } + if (Decl->hasAttr()) { + LangAS ImplAS = LangAS::hlsl_output; + Type = SemaRef.getASTContext().getAddrSpaceQualType(Type, ImplAS); + Decl->setType(Type); + + // HLSL uses `static` differently than C++. For BuiltIn output, the static + // does not imply private to the module scope. + // Marking it as external to reflect the semantic this attribute brings. + // See https://github.com/microsoft/hlsl-specs/issues/350 + Decl->setStorageClass(SC_Extern); + return; + } + bool IsVulkan = getASTContext().getTargetInfo().getTriple().getOS() == llvm::Triple::Vulkan; if (IsVulkan && Decl->hasAttr()) { diff --git a/clang/test/CodeGenHLSL/vk-output-builtin.hlsl b/clang/test/CodeGenHLSL/vk-output-builtin.hlsl new file mode 100644 index 0000000000000..df521113476eb --- /dev/null +++ b/clang/test/CodeGenHLSL/vk-output-builtin.hlsl @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ +// RUN: spirv-unknown-vulkan1.3-vertex %s -emit-llvm -O3 -o - | FileCheck %s + +[[vk::ext_builtin_output(/* Position */ 0)]] +static float4 position; +// CHECK: @position = external hidden local_unnamed_addr addrspace(8) global <4 x float>, align 16, !spirv.Decorations [[META0:![0-9]+]] + +RWStructuredBuffer input : register(u1, space0); + +void main() { + position = input[0]; + // CHECK: store <4 x float> %[[#]], ptr addrspace(8) @position, align 16 +} +// CHECK: [[META0]] = !{[[META1:![0-9]+]]} +// CHECK: [[META1]] = !{i32 11, i32 0} diff --git a/clang/test/SemaHLSL/vk-ext-output-builtin.hlsl b/clang/test/SemaHLSL/vk-ext-output-builtin.hlsl new file mode 100644 index 0000000000000..bfe0ce3067043 --- /dev/null +++ b/clang/test/SemaHLSL/vk-ext-output-builtin.hlsl @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-compute -x hlsl -hlsl-entry foo -finclude-default-header -o - %s -verify + +// expected-error@+1 {{'vk::ext_builtin_output' attribute only applies to non-const static globals}} +[[vk::ext_builtin_output(/* Position */ 0)]] +float4 position0; + +// expected-error@+1 {{'vk::ext_builtin_output' attribute only applies to non-const static globals}} +[[vk::ext_builtin_output(/* Position */ 0)]] +// expected-error@+1 {{default initialization of an object of const type 'const hlsl_private float4' (aka 'const hlsl_private vector')}} +static const float4 position1; + +// expected-error@+1 {{'vk::ext_builtin_output' attribute takes one argument}} +[[vk::ext_builtin_output()]] +static float4 position2; + +// expected-error@+1 {{'vk::ext_builtin_output' attribute requires an integer constant}} +[[vk::ext_builtin_output(0.4f)]] +static float4 position3; + +// expected-error@+1 {{'vk::ext_builtin_output' attribute only applies to non-const static globals}} +[[vk::ext_builtin_output(0)]] +void some_function() { +} + +[numthreads(1,1,1)] +void foo() { +} diff --git a/clang/test/SemaTemplate/address_space-dependent.cpp b/clang/test/SemaTemplate/address_space-dependent.cpp index cba21b416bb48..3fdccb2c71a76 100644 --- a/clang/test/SemaTemplate/address_space-dependent.cpp +++ b/clang/test/SemaTemplate/address_space-dependent.cpp @@ -43,7 +43,7 @@ void neg() { template void tooBig() { - __attribute__((address_space(I))) int *bounds; // expected-error {{address space is larger than the maximum supported (8388581)}} + __attribute__((address_space(I))) int *bounds; // expected-error {{address space is larger than the maximum supported (8388580)}} } template @@ -101,7 +101,7 @@ int main() { car<1, 2, 3>(); // expected-note {{in instantiation of function template specialization 'car<1, 2, 3>' requested here}} HasASTemplateFields<1> HASTF; neg<-1>(); // expected-note {{in instantiation of function template specialization 'neg<-1>' requested here}} - correct<0x7FFFE5>(); + correct<0x7FFFE4>(); tooBig<8388650>(); // expected-note {{in instantiation of function template specialization 'tooBig<8388650L>' requested here}} __attribute__((address_space(1))) char *x;