Skip to content

Commit 20d7019

Browse files
Keenutsbogner
andauthored
[HLSL][SPIR-V] Implement vk::ext_builtin_input attribute (#138530)
This variable attribute is used in HLSL to add Vulkan specific builtins in a shader. The attribute is documented here: https://github.com/microsoft/hlsl-specs/blob/17727e88fd1cb09013cb3a144110826af05f4dd5/proposals/0011-inline-spirv.md Those variable, even if marked as `static` are externally initialized by the pipeline/driver/GPU. This is handled by moving them to a specific address space `hlsl_input`, also added by this commit. The design for input variables in Clang can be found here: https://github.com/llvm/wg-hlsl/blob/355771361ef69259fef39a65caef8bff9cb4046d/proposals/0019-spirv-input-builtin.md Co-authored-by: Justin Bogner <[email protected]>
1 parent d7c7c46 commit 20d7019

28 files changed

+187
-4
lines changed

clang/include/clang/Basic/AddressSpaces.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ enum class LangAS : unsigned {
6161
hlsl_constant,
6262
hlsl_private,
6363
hlsl_device,
64+
hlsl_input,
6465

6566
// Wasm specific address spaces.
6667
wasm_funcref,

clang/include/clang/Basic/Attr.td

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,12 @@ def SharedVar : SubsetSubject<Var,
140140
[{S->hasGlobalStorage() && !S->getTLSKind()}],
141141
"global variables">;
142142

143+
def HLSLInputBuiltin
144+
: SubsetSubject<Var, [{S->hasGlobalStorage() &&
145+
S->getStorageClass() == StorageClass::SC_Static &&
146+
S->getType().isConstQualified()}],
147+
"static const globals">;
148+
143149
def GlobalVar : SubsetSubject<Var,
144150
[{S->hasGlobalStorage()}], "global variables">;
145151

@@ -4951,6 +4957,14 @@ def HLSLWaveSize: InheritableAttr {
49514957
let Documentation = [WaveSizeDocs];
49524958
}
49534959

4960+
def HLSLVkExtBuiltinInput : InheritableAttr {
4961+
let Spellings = [CXX11<"vk", "ext_builtin_input">];
4962+
let Args = [UnsignedArgument<"BuiltIn">];
4963+
let Subjects = SubjectList<[HLSLInputBuiltin], ErrorDiag>;
4964+
let LangOpts = [HLSL];
4965+
let Documentation = [HLSLVkExtBuiltinInputDocs];
4966+
}
4967+
49544968
def RandomizeLayout : InheritableAttr {
49554969
let Spellings = [GCC<"randomize_layout">];
49564970
let Subjects = SubjectList<[Record]>;

clang/include/clang/Basic/AttrDocs.td

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8508,6 +8508,29 @@ and copied back to the argument after the callee returns.
85088508
}];
85098509
}
85108510

8511+
def HLSLVkExtBuiltinInputDocs : Documentation {
8512+
let Category = DocCatVariable;
8513+
let Content = [{
8514+
Vulkan shaders have `Input` builtins. Those variables are externally
8515+
initialized by the driver/pipeline, but each copy is private to the current
8516+
lane.
8517+
8518+
Those builtins can be declared using the `[[vk::ext_builtin_input]]` attribute
8519+
like follows:
8520+
8521+
.. code-block:: c++
8522+
8523+
[[vk::ext_builtin_input(/* WorkgroupId */ 26)]]
8524+
static const uint3 groupid;
8525+
8526+
This variable will be lowered into a module-level variable, with the `Input`
8527+
storage class, and the `BuiltIn 26` decoration.
8528+
8529+
The full documentation for this inline SPIR-V attribute can be found here:
8530+
https://github.com/microsoft/hlsl-specs/blob/main/proposals/0011-inline-spirv.md
8531+
}];
8532+
}
8533+
85118534
def AnnotateTypeDocs : Documentation {
85128535
let Category = DocCatType;
85138536
let Heading = "annotate_type";

clang/include/clang/Basic/AttributeCommonInfo.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class AttributeCommonInfo {
6969
IgnoredAttribute,
7070
UnknownAttribute,
7171
};
72-
enum class Scope { NONE, CLANG, GNU, MSVC, OMP, HLSL, GSL, RISCV };
72+
enum class Scope { NONE, CLANG, GNU, MSVC, OMP, HLSL, VK, GSL, RISCV };
7373
enum class AttrArgsInfo {
7474
None,
7575
Optional,

clang/include/clang/Sema/SemaHLSL.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ class SemaHLSL : public SemaBase {
131131
void handleParamModifierAttr(Decl *D, const ParsedAttr &AL);
132132
bool handleResourceTypeAttr(QualType T, const ParsedAttr &AL);
133133

134+
void handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL);
135+
134136
bool CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
135137
QualType ProcessResourceTypeAttributes(QualType Wrapped);
136138
HLSLAttributedResourceLocInfo

clang/lib/AST/Type.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ bool Qualifiers::isTargetAddressSpaceSupersetOf(LangAS A, LangAS B,
100100
// address spaces to default to work around this problem.
101101
(A == LangAS::Default && B == LangAS::hlsl_private) ||
102102
(A == LangAS::Default && B == LangAS::hlsl_device) ||
103+
(A == LangAS::Default && B == LangAS::hlsl_input) ||
103104
// Conversions from target specific address spaces may be legal
104105
// depending on the target information.
105106
Ctx.getTargetInfo().isAddressSpaceSupersetOf(A, B);

clang/lib/AST/TypePrinter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2667,6 +2667,8 @@ std::string Qualifiers::getAddrSpaceAsString(LangAS AS) {
26672667
return "hlsl_private";
26682668
case LangAS::hlsl_device:
26692669
return "hlsl_device";
2670+
case LangAS::hlsl_input:
2671+
return "hlsl_input";
26702672
case LangAS::wasm_funcref:
26712673
return "__funcref";
26722674
default:

clang/lib/Basic/Attributes.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ getScopeFromNormalizedScopeName(StringRef ScopeName) {
210210
.Case("gnu", AttributeCommonInfo::Scope::GNU)
211211
.Case("gsl", AttributeCommonInfo::Scope::GSL)
212212
.Case("hlsl", AttributeCommonInfo::Scope::HLSL)
213+
.Case("vk", AttributeCommonInfo::Scope::VK)
213214
.Case("msvc", AttributeCommonInfo::Scope::MSVC)
214215
.Case("omp", AttributeCommonInfo::Scope::OMP)
215216
.Case("riscv", AttributeCommonInfo::Scope::RISCV);

clang/lib/Basic/TargetInfo.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ static const LangASMap FakeAddrSpaceMap = {
4949
13, // hlsl_groupshared
5050
14, // hlsl_constant
5151
15, // hlsl_private
52+
16, // hlsl_device
53+
17, // hlsl_input
5254
20, // wasm_funcref
5355
};
5456

clang/lib/Basic/Targets/AArch64.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ static const unsigned ARM64AddrSpaceMap[] = {
4747
0, // hlsl_constant
4848
0, // hlsl_private
4949
0, // hlsl_device
50+
0, // hlsl_input
5051
// Wasm address space values for this target are dummy values,
5152
// as it is only enabled for Wasm targets.
5253
20, // wasm_funcref

clang/lib/Basic/Targets/AMDGPU.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const LangASMap AMDGPUTargetInfo::AMDGPUDefIsGenMap = {
6262
// will break loudly.
6363
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_private
6464
llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_device
65+
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_input
6566
};
6667

6768
const LangASMap AMDGPUTargetInfo::AMDGPUDefIsPrivMap = {
@@ -89,6 +90,7 @@ const LangASMap AMDGPUTargetInfo::AMDGPUDefIsPrivMap = {
8990
llvm::AMDGPUAS::CONSTANT_ADDRESS, // hlsl_constant
9091
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_private
9192
llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_device
93+
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_input
9294
};
9395
} // namespace targets
9496
} // namespace clang

clang/lib/Basic/Targets/DirectX.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ static const unsigned DirectXAddrSpaceMap[] = {
4545
2, // hlsl_constant
4646
0, // hlsl_private
4747
0, // hlsl_device
48+
0, // hlsl_input
4849
// Wasm address space values for this target are dummy values,
4950
// as it is only enabled for Wasm targets.
5051
20, // wasm_funcref

clang/lib/Basic/Targets/NVPTX.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ static const unsigned NVPTXAddrSpaceMap[] = {
4949
0, // hlsl_constant
5050
0, // hlsl_private
5151
0, // hlsl_device
52+
0, // hlsl_input
5253
// Wasm address space values for this target are dummy values,
5354
// as it is only enabled for Wasm targets.
5455
20, // wasm_funcref

clang/lib/Basic/Targets/SPIR.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ static const unsigned SPIRDefIsPrivMap[] = {
5050
12, // hlsl_constant
5151
10, // hlsl_private
5252
11, // hlsl_device
53+
7, // hlsl_input
5354
// Wasm address space values for this target are dummy values,
5455
// as it is only enabled for Wasm targets.
5556
20, // wasm_funcref
@@ -85,6 +86,7 @@ static const unsigned SPIRDefIsGenMap[] = {
8586
0, // hlsl_constant
8687
10, // hlsl_private
8788
11, // hlsl_device
89+
7, // hlsl_input
8890
// Wasm address space values for this target are dummy values,
8991
// as it is only enabled for Wasm targets.
9092
20, // wasm_funcref

clang/lib/Basic/Targets/SystemZ.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ static const unsigned ZOSAddressMap[] = {
4545
0, // hlsl_constant
4646
0, // hlsl_private
4747
0, // hlsl_device
48+
0, // hlsl_input
4849
0 // wasm_funcref
4950
};
5051

clang/lib/Basic/Targets/TCE.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ static const unsigned TCEOpenCLAddrSpaceMap[] = {
5454
0, // hlsl_constant
5555
0, // hlsl_private
5656
0, // hlsl_device
57+
0, // hlsl_input
5758
// Wasm address space values for this target are dummy values,
5859
// as it is only enabled for Wasm targets.
5960
20, // wasm_funcref

clang/lib/Basic/Targets/WebAssembly.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ static const unsigned WebAssemblyAddrSpaceMap[] = {
4545
0, // hlsl_constant
4646
0, // hlsl_private
4747
0, // hlsl_device
48+
0, // hlsl_input
4849
20, // wasm_funcref
4950
};
5051

clang/lib/Basic/Targets/X86.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ static const unsigned X86AddrSpaceMap[] = {
4949
0, // hlsl_constant
5050
0, // hlsl_private
5151
0, // hlsl_device
52+
0, // hlsl_input
5253
// Wasm address space values for this target are dummy values,
5354
// as it is only enabled for Wasm targets.
5455
20, // wasm_funcref

clang/lib/CodeGen/CGHLSLRuntime.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,20 @@ void CGHLSLRuntime::initializeBufferFromBinding(const HLSLBufferDecl *BufDecl,
624624
}
625625
}
626626

627+
void CGHLSLRuntime::handleGlobalVarDefinition(const VarDecl *VD,
628+
llvm::GlobalVariable *GV) {
629+
if (auto Attr = VD->getAttr<HLSLVkExtBuiltinInputAttr>()) {
630+
LLVMContext &Ctx = GV->getContext();
631+
IRBuilder<> B(GV->getContext());
632+
MDNode *Operands = MDNode::get(
633+
Ctx, {ConstantAsMetadata::get(
634+
B.getInt32(/* Spirv::Decoration::BuiltIn */ 11)),
635+
ConstantAsMetadata::get(B.getInt32(Attr->getBuiltIn()))});
636+
MDNode *Decoration = MDNode::get(Ctx, {Operands});
637+
GV->addMetadata("spirv.Decorations", *Decoration);
638+
}
639+
}
640+
627641
llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) {
628642
if (!CGM.shouldEmitConvergenceTokens())
629643
return nullptr;

clang/lib/CodeGen/CGHLSLRuntime.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ class CGHLSLRuntime {
157157

158158
void emitEntryFunction(const FunctionDecl *FD, llvm::Function *Fn);
159159
void setHLSLFunctionAttributes(const FunctionDecl *FD, llvm::Function *Fn);
160+
void handleGlobalVarDefinition(const VarDecl *VD, llvm::GlobalVariable *Var);
160161

161162
llvm::Instruction *getConvergenceToken(llvm::BasicBlock &BB);
162163

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5763,7 +5763,17 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
57635763
getCUDARuntime().handleVarRegistration(D, *GV);
57645764
}
57655765

5766-
GV->setInitializer(Init);
5766+
if (LangOpts.HLSL && GetGlobalVarAddressSpace(D) == LangAS::hlsl_input) {
5767+
// HLSL Input variables are considered to be set by the driver/pipeline, but
5768+
// only visible to a single thread/wave.
5769+
GV->setExternallyInitialized(true);
5770+
} else {
5771+
GV->setInitializer(Init);
5772+
}
5773+
5774+
if (LangOpts.HLSL)
5775+
getHLSLRuntime().handleGlobalVarDefinition(D, GV);
5776+
57675777
if (emitter)
57685778
emitter->finalize(GV);
57695779

@@ -5806,6 +5816,12 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
58065816
!D->hasAttr<ConstInitAttr>())
58075817
Linkage = llvm::GlobalValue::InternalLinkage;
58085818

5819+
// HLSL variables in the input address space maps like memory-mapped
5820+
// variables. Even if they are 'static', they are externally initialized and
5821+
// read/write by the hardware/driver/pipeline.
5822+
if (LangOpts.HLSL && GetGlobalVarAddressSpace(D) == LangAS::hlsl_input)
5823+
Linkage = llvm::GlobalValue::ExternalLinkage;
5824+
58095825
GV->setLinkage(Linkage);
58105826
if (D->hasAttr<DLLImportAttr>())
58115827
GV->setDLLStorageClass(llvm::GlobalVariable::DLLImportStorageClass);

clang/lib/Sema/SemaDecl.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14419,6 +14419,12 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
1441914419
if (getLangOpts().HLSL && HLSL().ActOnUninitializedVarDecl(Var))
1442014420
return;
1442114421

14422+
// HLSL input variables are expected to be externally initialized, even
14423+
// when marked `static`.
14424+
if (getLangOpts().HLSL &&
14425+
Var->getType().getAddressSpace() == LangAS::hlsl_input)
14426+
return;
14427+
1442214428
// C++03 [dcl.init]p9:
1442314429
// If no initializer is specified for an object, and the
1442414430
// object is of (possibly cv-qualified) non-POD class type (or

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7510,6 +7510,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
75107510
case ParsedAttr::AT_HLSLWaveSize:
75117511
S.HLSL().handleWaveSizeAttr(D, AL);
75127512
break;
7513+
case ParsedAttr::AT_HLSLVkExtBuiltinInput:
7514+
S.HLSL().handleVkExtBuiltinInputAttr(D, AL);
7515+
break;
75137516
case ParsedAttr::AT_HLSLSV_GroupThreadID:
75147517
S.HLSL().handleSV_GroupThreadIDAttr(D, AL);
75157518
break;

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,14 @@ void SemaHLSL::handleWaveSizeAttr(Decl *D, const ParsedAttr &AL) {
11171117
D->addAttr(NewAttr);
11181118
}
11191119

1120+
void SemaHLSL::handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL) {
1121+
uint32_t ID;
1122+
if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), ID))
1123+
return;
1124+
D->addAttr(::new (getASTContext())
1125+
HLSLVkExtBuiltinInputAttr(getASTContext(), AL, ID));
1126+
}
1127+
11201128
bool SemaHLSL::diagnoseInputIDType(QualType T, const ParsedAttr &AL) {
11211129
const auto *VT = T->getAs<VectorType>();
11221130

@@ -3158,6 +3166,14 @@ void SemaHLSL::deduceAddressSpace(VarDecl *Decl) {
31583166
return;
31593167

31603168
QualType Type = Decl->getType();
3169+
3170+
if (Decl->hasAttr<HLSLVkExtBuiltinInputAttr>()) {
3171+
LangAS ImplAS = LangAS::hlsl_input;
3172+
Type = SemaRef.getASTContext().getAddrSpaceQualType(Type, ImplAS);
3173+
Decl->setType(Type);
3174+
return;
3175+
}
3176+
31613177
if (Type->isSamplerT() || Type->isVoidType())
31623178
return;
31633179

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
2+
// RUN: spirv-unknown-vulkan1.3-compute %s -emit-llvm -O3 -o - | FileCheck %s
3+
4+
[[vk::ext_builtin_input(/* WorkgroupId */ 26)]]
5+
static const uint3 groupid;
6+
// CHECK: @_ZL7groupid = external local_unnamed_addr addrspace(7) externally_initialized constant <3 x i32>, align 16, !spirv.Decorations [[META0:![0-9]+]]
7+
8+
RWStructuredBuffer<int> output : register(u1, space0);
9+
10+
[numthreads(1, 1, 1)]
11+
void main() {
12+
output[0] = groupid;
13+
}
14+
// CHECK: [[META0]] = !{[[META1:![0-9]+]]}
15+
// CHECK: [[META1]] = !{i32 11, i32 26}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %clang_cc1 -triple spirv-unkown-vulkan1.3-compute -x hlsl -hlsl-entry foo -finclude-default-header -o - %s -verify
2+
3+
// expected-error@+1 {{'ext_builtin_input' attribute only applies to static const globals}}
4+
[[vk::ext_builtin_input(/* WorkgroupId */ 26)]]
5+
const uint3 groupid1;
6+
7+
// expected-error@+1 {{'ext_builtin_input' attribute only applies to static const globals}}
8+
[[vk::ext_builtin_input(/* WorkgroupId */ 26)]]
9+
static uint3 groupid2;
10+
11+
// expected-error@+1 {{'ext_builtin_input' attribute takes one argument}}
12+
[[vk::ext_builtin_input()]]
13+
// expected-error@+1 {{default initialization of an object of const type 'const hlsl_private uint3' (aka 'const hlsl_private vector<uint, 3>')}}
14+
static const uint3 groupid3;
15+
16+
// expected-error@+1 {{'ext_builtin_input' attribute requires an integer constant}}
17+
[[vk::ext_builtin_input(0.4f)]]
18+
// expected-error@+1 {{default initialization of an object of const type 'const hlsl_private uint3' (aka 'const hlsl_private vector<uint, 3>')}}
19+
static const uint3 groupid4;
20+
21+
// expected-error@+1 {{'ext_builtin_input' attribute only applies to static const globals}}
22+
[[vk::ext_builtin_input(1)]]
23+
void some_function() {
24+
}
25+
26+
[numthreads(1,1,1)]
27+
void foo() {
28+
}
29+

clang/test/SemaTemplate/address_space-dependent.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ void neg() {
4343

4444
template <long int I>
4545
void tooBig() {
46-
__attribute__((address_space(I))) int *bounds; // expected-error {{address space is larger than the maximum supported (8388583)}}
46+
__attribute__((address_space(I))) int *bounds; // expected-error {{address space is larger than the maximum supported (8388582)}}
4747
}
4848

4949
template <long int I>
@@ -101,7 +101,7 @@ int main() {
101101
car<1, 2, 3>(); // expected-note {{in instantiation of function template specialization 'car<1, 2, 3>' requested here}}
102102
HasASTemplateFields<1> HASTF;
103103
neg<-1>(); // expected-note {{in instantiation of function template specialization 'neg<-1>' requested here}}
104-
correct<0x7FFFE7>();
104+
correct<0x7FFFE6>();
105105
tooBig<8388650>(); // expected-note {{in instantiation of function template specialization 'tooBig<8388650L>' requested here}}
106106

107107
__attribute__((address_space(1))) char *x;

0 commit comments

Comments
 (0)