Skip to content

Commit 7b09222

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 090a81f commit 7b09222

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
@@ -619,11 +619,51 @@ CGHLSLRuntime::handleScalarSemanticLoad(IRBuilder<> &B, llvm::Type *Type,
619619
return emitSystemSemanticLoad(B, Type, Decl, ActiveSemantic);
620620
}
621621

622+
llvm::Value *
623+
CGHLSLRuntime::handleStructSemanticLoad(IRBuilder<> &B, llvm::Type *Type,
624+
const clang::DeclaratorDecl *Decl,
625+
SemanticInfo &ActiveSemantic) {
626+
const llvm::StructType *ST = cast<StructType>(Type);
627+
const clang::RecordDecl *RD = Decl->getType()->getAsRecordDecl();
628+
629+
assert(std::distance(RD->field_begin(), RD->field_end()) ==
630+
ST->getNumElements());
631+
632+
if (!ActiveSemantic.Semantic) {
633+
ActiveSemantic.Semantic = Decl->getAttr<HLSLSemanticAttr>();
634+
ActiveSemantic.Index = ActiveSemantic.Semantic
635+
? ActiveSemantic.Semantic->getSemanticIndex()
636+
: 0;
637+
}
638+
639+
llvm::Value *Aggregate = llvm::PoisonValue::get(Type);
640+
auto FieldDecl = RD->field_begin();
641+
for (unsigned I = 0; I < ST->getNumElements(); ++I) {
642+
SemanticInfo Info = ActiveSemantic;
643+
llvm::Value *ChildValue =
644+
handleSemanticLoad(B, ST->getElementType(I), *FieldDecl, Info);
645+
if (!ChildValue) {
646+
CGM.getDiags().Report(Decl->getInnerLocStart(),
647+
diag::note_hlsl_semantic_used_here)
648+
<< Decl;
649+
return nullptr;
650+
}
651+
if (ActiveSemantic.Semantic)
652+
ActiveSemantic = Info;
653+
654+
Aggregate = B.CreateInsertValue(Aggregate, ChildValue, I);
655+
++FieldDecl;
656+
}
657+
658+
return Aggregate;
659+
}
660+
622661
llvm::Value *
623662
CGHLSLRuntime::handleSemanticLoad(IRBuilder<> &B, llvm::Type *Type,
624663
const clang::DeclaratorDecl *Decl,
625664
SemanticInfo &ActiveSemantic) {
626-
assert(!Type->isStructTy());
665+
if (Type->isStructTy())
666+
return handleStructSemanticLoad(B, Type, Decl, ActiveSemantic);
627667
return handleScalarSemanticLoad(B, Type, Decl, ActiveSemantic);
628668
}
629669

@@ -671,8 +711,25 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD,
671711
}
672712

673713
const ParmVarDecl *PD = FD->getParamDecl(Param.getArgNo() - SRetOffset);
674-
SemanticInfo ActiveSemantic = {nullptr, 0};
675-
Args.push_back(handleSemanticLoad(B, Param.getType(), PD, ActiveSemantic));
714+
llvm::Value *SemanticValue = nullptr;
715+
if (HLSLParamModifierAttr *MA = PD->getAttr<HLSLParamModifierAttr>()) {
716+
llvm_unreachable("Not handled yet");
717+
} else {
718+
llvm::Type *ParamType =
719+
Param.hasByValAttr() ? Param.getParamByValType() : Param.getType();
720+
SemanticInfo ActiveSemantic = {nullptr, 0};
721+
SemanticValue = handleSemanticLoad(B, ParamType, PD, ActiveSemantic);
722+
if (!SemanticValue)
723+
return;
724+
if (Param.hasByValAttr()) {
725+
llvm::Value *Var = B.CreateAlloca(Param.getParamByValType());
726+
B.CreateStore(SemanticValue, Var);
727+
SemanticValue = Var;
728+
}
729+
}
730+
731+
assert(SemanticValue);
732+
Args.push_back(SemanticValue);
676733
}
677734

678735
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
@@ -156,6 +156,10 @@ class CGHLSLRuntime {
156156
const clang::DeclaratorDecl *Decl,
157157
SemanticInfo &ActiveSemantic);
158158

159+
llvm::Value *handleStructSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
160+
const clang::DeclaratorDecl *Decl,
161+
SemanticInfo &ActiveSemantic);
162+
159163
llvm::Value *handleSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
160164
const clang::DeclaratorDecl *Decl,
161165
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)