Skip to content

Commit 445bdaf

Browse files
committed
[HLSL] Add support for input semantics in structs
This commit adds the support for semantics annotations on structs, but only for inputs. Due to the current semantics implemented, we cannot test much more than nesting/shadowing. Once user semantics are implemented, we'll be able to test arrays in structs and more complex cases. As-is, this commit has one weakness vs DXC: semantics type validation is not looking at the inner-most type, but the outermost type: ```hlsl struct Inner { uint tid; }; Inner inner : SV_GroupID ``` This sample would fail today because `SV_GroupID` require the type to be an integer. This works in DXC as the inner type is a integer. Because GroupIndex is not correctly validated, I uses this semantic to test the inheritance/shadowing. But this will need to be fixed in a later commit. Requires #152537
1 parent 641ed9f commit 445bdaf

File tree

8 files changed

+204
-3
lines changed

8 files changed

+204
-3
lines changed

clang/include/clang/Basic/DiagnosticFrontendKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,8 @@ def err_hlsl_semantic_missing : Error<"semantic annotations must be present "
404404
"for all input and outputs of an entry "
405405
"function or patch constant function">;
406406

407+
def note_hlsl_semantic_used_here : Note<"%0 used here">;
408+
407409
// ClangIR frontend errors
408410
def err_cir_to_cir_transform_failed : Error<
409411
"CIR-to-CIR transformation failed">, DefaultFatal;

clang/lib/CodeGen/CGHLSLRuntime.cpp

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -632,11 +632,51 @@ CGHLSLRuntime::handleScalarSemanticLoad(IRBuilder<> &B, llvm::Type *Type,
632632
return emitSystemSemanticLoad(B, Type, Decl, ActiveSemantic);
633633
}
634634

635+
llvm::Value *
636+
CGHLSLRuntime::handleStructSemanticLoad(IRBuilder<> &B, llvm::Type *Type,
637+
const clang::DeclaratorDecl *Decl,
638+
SemanticInfo &ActiveSemantic) {
639+
const llvm::StructType *ST = cast<StructType>(Type);
640+
const clang::RecordDecl *RD = Decl->getType()->getAsRecordDecl();
641+
642+
assert(std::distance(RD->field_begin(), RD->field_end()) ==
643+
ST->getNumElements());
644+
645+
if (!ActiveSemantic.Semantic) {
646+
ActiveSemantic.Semantic = Decl->getAttr<HLSLSemanticAttr>();
647+
ActiveSemantic.Index = ActiveSemantic.Semantic
648+
? ActiveSemantic.Semantic->getSemanticIndex()
649+
: 0;
650+
}
651+
652+
llvm::Value *Aggregate = llvm::PoisonValue::get(Type);
653+
auto FieldDecl = RD->field_begin();
654+
for (unsigned I = 0; I < ST->getNumElements(); ++I) {
655+
SemanticInfo Info = ActiveSemantic;
656+
llvm::Value *ChildValue =
657+
handleSemanticLoad(B, ST->getElementType(I), *FieldDecl, Info);
658+
if (!ChildValue) {
659+
CGM.getDiags().Report(Decl->getInnerLocStart(),
660+
diag::note_hlsl_semantic_used_here)
661+
<< Decl;
662+
return nullptr;
663+
}
664+
if (ActiveSemantic.Semantic)
665+
ActiveSemantic = Info;
666+
667+
Aggregate = B.CreateInsertValue(Aggregate, ChildValue, I);
668+
++FieldDecl;
669+
}
670+
671+
return Aggregate;
672+
}
673+
635674
llvm::Value *
636675
CGHLSLRuntime::handleSemanticLoad(IRBuilder<> &B, llvm::Type *Type,
637676
const clang::DeclaratorDecl *Decl,
638677
SemanticInfo &ActiveSemantic) {
639-
assert(!Type->isStructTy());
678+
if (Type->isStructTy())
679+
return handleStructSemanticLoad(B, Type, Decl, ActiveSemantic);
640680
return handleScalarSemanticLoad(B, Type, Decl, ActiveSemantic);
641681
}
642682

@@ -684,8 +724,25 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD,
684724
}
685725

686726
const ParmVarDecl *PD = FD->getParamDecl(Param.getArgNo() - SRetOffset);
687-
SemanticInfo ActiveSemantic = {nullptr, 0};
688-
Args.push_back(handleSemanticLoad(B, Param.getType(), PD, ActiveSemantic));
727+
llvm::Value *SemanticValue = nullptr;
728+
if (HLSLParamModifierAttr *MA = PD->getAttr<HLSLParamModifierAttr>()) {
729+
llvm_unreachable("Not handled yet");
730+
} else {
731+
llvm::Type *ParamType =
732+
Param.hasByValAttr() ? Param.getParamByValType() : Param.getType();
733+
SemanticInfo ActiveSemantic = {nullptr, 0};
734+
SemanticValue = handleSemanticLoad(B, ParamType, PD, ActiveSemantic);
735+
if (!SemanticValue)
736+
return;
737+
if (Param.hasByValAttr()) {
738+
llvm::Value *Var = B.CreateAlloca(Param.getParamByValType());
739+
B.CreateStore(SemanticValue, Var);
740+
SemanticValue = Var;
741+
}
742+
}
743+
744+
assert(SemanticValue);
745+
Args.push_back(SemanticValue);
689746
}
690747

691748
CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB);

clang/lib/CodeGen/CGHLSLRuntime.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ class CGHLSLRuntime {
157157
const clang::DeclaratorDecl *Decl,
158158
SemanticInfo &ActiveSemantic);
159159

160+
llvm::Value *handleStructSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
161+
const clang::DeclaratorDecl *Decl,
162+
SemanticInfo &ActiveSemantic);
163+
160164
llvm::Value *handleSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
161165
const clang::DeclaratorDecl *Decl,
162166
SemanticInfo &ActiveSemantic);
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -DTARGET=dx
2+
// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV -DTARGET=spv
3+
4+
5+
struct Input {
6+
uint Idx : SV_DispatchThreadID;
7+
8+
};
9+
10+
// Make sure SV_DispatchThreadID translated into dx.thread.id.
11+
12+
// CHECK: define void @foo()
13+
// CHECK-DXIL: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0)
14+
// CHECK-SPIRV: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0)
15+
// CHECK: %[[#TMP:]] = insertvalue %struct.Input poison, i32 %[[#ID]], 0
16+
// CHECK: %[[#VAR:]] = alloca %struct.Input, align 8
17+
// CHECK: store %struct.Input %[[#TMP]], ptr %[[#VAR]], align 4
18+
// CHECK-DXIL: call void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
19+
// CHECK-SPIRV: call spir_func void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
20+
[shader("compute")]
21+
[numthreads(8,8,1)]
22+
void foo(Input input) {}
23+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -DTARGET=dx
2+
// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV -DTARGET=spv
3+
4+
5+
struct Input {
6+
uint Idx : SV_DispatchThreadID;
7+
uint Gid : SV_GroupID;
8+
};
9+
10+
// Make sure SV_DispatchThreadID translated into dx.thread.id.
11+
12+
// CHECK: define void @foo()
13+
// CHECK-DXIL: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0)
14+
// CHECK-SPIRV: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0)
15+
// CHECK: %[[#TMP1:]] = insertvalue %struct.Input poison, i32 %[[#ID]], 0
16+
// CHECK-DXIL: %[[#GID:]] = call i32 @llvm.[[TARGET]].group.id(i32 0)
17+
// CHECK-SPIRV:%[[#GID:]] = call i32 @llvm.[[TARGET]].group.id.i32(i32 0)
18+
// CHECK: %[[#TMP2:]] = insertvalue %struct.Input %[[#TMP1]], i32 %[[#GID]], 1
19+
// CHECK: %[[#VAR:]] = alloca %struct.Input, align 8
20+
// CHECK: store %struct.Input %[[#TMP2]], ptr %[[#VAR]], align 4
21+
// CHECK-DXIL: call void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
22+
// CHECK-SPIRV: call spir_func void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
23+
[shader("compute")]
24+
[numthreads(8,8,1)]
25+
void foo(Input input) {}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -DTARGET=dx
2+
// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV -DTARGET=spv
3+
4+
5+
struct Inner {
6+
uint Gid;
7+
};
8+
9+
struct Input {
10+
uint Idx : SV_DispatchThreadID;
11+
Inner inner : SV_GroupIndex;
12+
};
13+
14+
// Make sure SV_DispatchThreadID translated into dx.thread.id.
15+
16+
// CHECK: define void @foo()
17+
// CHECK-DXIL: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0)
18+
// CHECK-SPIRV: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0)
19+
// CHECK: %[[#TMP1:]] = insertvalue %struct.Input poison, i32 %[[#ID]], 0
20+
// CHECK-DXIL: %[[#GID:]] = call i32 @llvm.dx.flattened.thread.id.in.group()
21+
// CHECK-SPIRV:%[[#GID:]] = call i32 @llvm.spv.flattened.thread.id.in.group()
22+
// CHECK: %[[#TMP2:]] = insertvalue %struct.Inner poison, i32 %[[#GID]], 0
23+
// CHECK: %[[#TMP3:]] = insertvalue %struct.Input %[[#TMP1]], %struct.Inner %[[#TMP2]], 1
24+
// CHECK: %[[#VAR:]] = alloca %struct.Input, align 8
25+
// CHECK: store %struct.Input %[[#TMP3]], ptr %[[#VAR]], align 4
26+
// CHECK-DXIL: call void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
27+
// CHECK-SPIRV: call spir_func void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
28+
[shader("compute")]
29+
[numthreads(8,8,1)]
30+
void foo(Input input) {}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -DTARGET=dx
2+
// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV -DTARGET=spv
3+
4+
5+
struct Inner {
6+
uint Gid : SV_GroupID;
7+
};
8+
9+
struct Input {
10+
uint Idx : SV_DispatchThreadID;
11+
Inner inner : SV_GroupIndex;
12+
};
13+
14+
// Make sure SV_DispatchThreadID translated into dx.thread.id.
15+
16+
// CHECK: define void @foo()
17+
// CHECK-DXIL: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0)
18+
// CHECK-SPIRV: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0)
19+
// CHECK: %[[#TMP1:]] = insertvalue %struct.Input poison, i32 %[[#ID]], 0
20+
// CHECK-DXIL: %[[#GID:]] = call i32 @llvm.dx.flattened.thread.id.in.group()
21+
// CHECK-SPIRV:%[[#GID:]] = call i32 @llvm.spv.flattened.thread.id.in.group()
22+
// CHECK: %[[#TMP2:]] = insertvalue %struct.Inner poison, i32 %[[#GID]], 0
23+
// CHECK: %[[#TMP3:]] = insertvalue %struct.Input %[[#TMP1]], %struct.Inner %[[#TMP2]], 1
24+
// CHECK: %[[#VAR:]] = alloca %struct.Input, align 8
25+
// CHECK: store %struct.Input %[[#TMP3]], ptr %[[#VAR]], align 4
26+
// CHECK-DXIL: call void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
27+
// CHECK-SPIRV: call spir_func void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
28+
[shader("compute")]
29+
[numthreads(8,8,1)]
30+
void foo(Input input) {}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -DTARGET=dx
2+
// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV -DTARGET=spv
3+
4+
5+
struct Inner {
6+
uint Gid : SV_GroupID;
7+
};
8+
9+
struct Input {
10+
uint Idx : SV_DispatchThreadID;
11+
Inner inner;
12+
};
13+
14+
// Make sure SV_DispatchThreadID translated into dx.thread.id.
15+
16+
// CHECK: define void @foo()
17+
// CHECK-DXIL: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0)
18+
// CHECK-SPIRV: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0)
19+
// CHECK: %[[#TMP1:]] = insertvalue %struct.Input poison, i32 %[[#ID]], 0
20+
// CHECK-DXIL: %[[#GID:]] = call i32 @llvm.[[TARGET]].group.id(i32 0)
21+
// CHECK-SPIRV:%[[#GID:]] = call i32 @llvm.[[TARGET]].group.id.i32(i32 0)
22+
// CHECK: %[[#TMP2:]] = insertvalue %struct.Inner poison, i32 %[[#GID]], 0
23+
// CHECK: %[[#TMP3:]] = insertvalue %struct.Input %[[#TMP1]], %struct.Inner %[[#TMP2]], 1
24+
// CHECK: %[[#VAR:]] = alloca %struct.Input, align 8
25+
// CHECK: store %struct.Input %[[#TMP3]], ptr %[[#VAR]], align 4
26+
// CHECK-DXIL: call void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
27+
// CHECK-SPIRV: call spir_func void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
28+
[shader("compute")]
29+
[numthreads(8,8,1)]
30+
void foo(Input input) {}

0 commit comments

Comments
 (0)