[HLSL][SPIR-V] Add vk::ext_builtin_output attribute (#188268)
This attribute is similar to the already implemented ext_builtin_input attribute. One important bit is the `static` storage class: HLSL uses static differently than C/C++. This is a known weirdness: See https://github.com/microsoft/hlsl-specs/issues/350 In C/C++, when we declare a variable as 'extern', we often expect another module to declare the symbole. In HLSL, the pipeline will 'declare' the symbol. Hence in this case, we need to emit the global variable. Related WG-HLSL: https://github.com/llvm/wg-hlsl/blob/main/proposals/0031-semantics.md --------- Co-authored-by: Steven Perron <stevenperron@google.com>
This commit is contained in:
parent
414f0c3362
commit
18250fd47b
@ -62,6 +62,7 @@ enum class LangAS : unsigned {
|
||||
hlsl_private,
|
||||
hlsl_device,
|
||||
hlsl_input,
|
||||
hlsl_output,
|
||||
hlsl_push_constant,
|
||||
|
||||
// Wasm specific address spaces.
|
||||
|
||||
@ -146,6 +146,12 @@ def HLSLInputBuiltin
|
||||
S->getType().isConstQualified()}],
|
||||
"static const globals">;
|
||||
|
||||
def HLSLOutputBuiltin
|
||||
: SubsetSubject<Var, [{S->hasGlobalStorage() &&
|
||||
S->getStorageClass() == StorageClass::SC_Static &&
|
||||
!S->getType().isConstQualified()}],
|
||||
"non-const static globals">;
|
||||
|
||||
def GlobalVar : SubsetSubject<Var,
|
||||
[{S->hasGlobalStorage()}], "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 = [];
|
||||
|
||||
@ -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 = [{
|
||||
|
||||
@ -190,6 +190,7 @@ public:
|
||||
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);
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
};
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
};
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
};
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -1163,6 +1163,8 @@ void CGHLSLRuntime::handleGlobalVarDefinition(const VarDecl *VD,
|
||||
llvm::GlobalVariable *GV) {
|
||||
if (auto Attr = VD->getAttr<HLSLVkExtBuiltinInputAttr>())
|
||||
addSPIRVBuiltinDecoration(GV, Attr->getBuiltIn());
|
||||
if (auto Attr = VD->getAttr<HLSLVkExtBuiltinOutputAttr>())
|
||||
addSPIRVBuiltinDecoration(GV, Attr->getBuiltIn());
|
||||
}
|
||||
|
||||
llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) {
|
||||
|
||||
@ -4441,6 +4441,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<llvm::GlobalVariable>(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) ==
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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<HLSLVkExtBuiltinOutputAttr>()) {
|
||||
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<HLSLVkPushConstantAttr>()) {
|
||||
|
||||
15
clang/test/CodeGenHLSL/vk-output-builtin.hlsl
Normal file
15
clang/test/CodeGenHLSL/vk-output-builtin.hlsl
Normal file
@ -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<float4> 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}
|
||||
27
clang/test/SemaHLSL/vk-ext-output-builtin.hlsl
Normal file
27
clang/test/SemaHLSL/vk-ext-output-builtin.hlsl
Normal file
@ -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<float, 4>')}}
|
||||
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() {
|
||||
}
|
||||
@ -43,7 +43,7 @@ void neg() {
|
||||
|
||||
template <long int I>
|
||||
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 <long int I>
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user