Skip to content

Commit f011a0f

Browse files
committed
[HLSL] Add initial support for output semantics
This commits adds the first part of the output semantics. It only considers return values (and sret), but does not handle `inout` or `out` parameters yet. Those missing bits will reuse the same code, but will require additional testing & some fixups, so planning on adding them separately.
1 parent c243406 commit f011a0f

26 files changed

+383
-67
lines changed

clang/lib/CodeGen/CGHLSLRuntime.cpp

Lines changed: 164 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,36 @@ CGHLSLRuntime::emitSPIRVUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
589589
VariableName.str());
590590
}
591591

592+
static void createSPIRVLocationStore(IRBuilder<> &B, llvm::Module &M,
593+
llvm::Value *Source, unsigned Location,
594+
StringRef Name) {
595+
auto *GV = new llvm::GlobalVariable(
596+
M, Source->getType(), /* isConstant= */ false,
597+
llvm::GlobalValue::ExternalLinkage,
598+
/* Initializer= */ nullptr, /* Name= */ Name, /* insertBefore= */ nullptr,
599+
llvm::GlobalVariable::GeneralDynamicTLSModel,
600+
/* AddressSpace */ 8, /* isExternallyInitialized= */ false);
601+
GV->setVisibility(llvm::GlobalValue::HiddenVisibility);
602+
addLocationDecoration(GV, Location);
603+
B.CreateStore(Source, GV);
604+
}
605+
606+
void CGHLSLRuntime::emitSPIRVUserSemanticStore(
607+
llvm::IRBuilder<> &B, llvm::Value *Source,
608+
HLSLAppliedSemanticAttr *Semantic, std::optional<unsigned> Index) {
609+
Twine BaseName = Twine(Semantic->getAttrName()->getName());
610+
Twine VariableName = BaseName.concat(Twine(Index.value_or(0)));
611+
unsigned Location = SPIRVLastAssignedOutputSemanticLocation;
612+
613+
// DXC completely ignores the semantic/index pair. Location are assigned from
614+
// the first semantic to the last.
615+
llvm::ArrayType *AT = dyn_cast<llvm::ArrayType>(Source->getType());
616+
unsigned ElementCount = AT ? AT->getNumElements() : 1;
617+
SPIRVLastAssignedOutputSemanticLocation += ElementCount;
618+
createSPIRVLocationStore(B, CGM.getModule(), Source, Location,
619+
VariableName.str());
620+
}
621+
592622
llvm::Value *
593623
CGHLSLRuntime::emitDXILUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
594624
HLSLAppliedSemanticAttr *Semantic,
@@ -609,6 +639,23 @@ CGHLSLRuntime::emitDXILUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
609639
return Value;
610640
}
611641

642+
void CGHLSLRuntime::emitDXILUserSemanticStore(llvm::IRBuilder<> &B,
643+
llvm::Value *Source,
644+
HLSLAppliedSemanticAttr *Semantic,
645+
std::optional<unsigned> Index) {
646+
// DXIL packing rules etc shall be handled here.
647+
// FIXME: generate proper sigpoint, index, col, row values.
648+
SmallVector<Value *> Args{B.getInt32(4),
649+
B.getInt32(0),
650+
B.getInt32(0),
651+
B.getInt8(0),
652+
llvm::PoisonValue::get(B.getInt32Ty()),
653+
Source};
654+
655+
llvm::Intrinsic::ID IntrinsicID = llvm::Intrinsic::dx_store_output;
656+
B.CreateIntrinsic(/*ReturnType=*/CGM.VoidTy, IntrinsicID, Args, nullptr);
657+
}
658+
612659
llvm::Value *CGHLSLRuntime::emitUserSemanticLoad(
613660
IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
614661
HLSLAppliedSemanticAttr *Semantic, std::optional<unsigned> Index) {
@@ -621,6 +668,19 @@ llvm::Value *CGHLSLRuntime::emitUserSemanticLoad(
621668
llvm_unreachable("Unsupported target for user-semantic load.");
622669
}
623670

671+
void CGHLSLRuntime::emitUserSemanticStore(IRBuilder<> &B, llvm::Value *Source,
672+
const clang::DeclaratorDecl *Decl,
673+
HLSLAppliedSemanticAttr *Semantic,
674+
std::optional<unsigned> Index) {
675+
if (CGM.getTarget().getTriple().isSPIRV())
676+
return emitSPIRVUserSemanticStore(B, Source, Semantic, Index);
677+
678+
if (CGM.getTarget().getTriple().isDXIL())
679+
return emitDXILUserSemanticStore(B, Source, Semantic, Index);
680+
681+
llvm_unreachable("Unsupported target for user-semantic load.");
682+
}
683+
624684
llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
625685
IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
626686
HLSLAppliedSemanticAttr *Semantic, std::optional<unsigned> Index) {
@@ -669,6 +729,34 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
669729
llvm_unreachable("non-handled system semantic. FIXME.");
670730
}
671731

732+
static void createSPIRVBuiltinStore(IRBuilder<> &B, llvm::Module &M,
733+
llvm::Value *Source, const Twine &Name,
734+
unsigned BuiltInID) {
735+
auto *GV = new llvm::GlobalVariable(
736+
M, Source->getType(), /* isConstant= */ false,
737+
llvm::GlobalValue::ExternalLinkage,
738+
/* Initializer= */ nullptr, Name, /* insertBefore= */ nullptr,
739+
llvm::GlobalVariable::GeneralDynamicTLSModel,
740+
/* AddressSpace */ 8, /* isExternallyInitialized= */ false);
741+
addSPIRVBuiltinDecoration(GV, BuiltInID);
742+
GV->setVisibility(llvm::GlobalValue::HiddenVisibility);
743+
B.CreateStore(Source, GV);
744+
}
745+
746+
void CGHLSLRuntime::emitSystemSemanticStore(IRBuilder<> &B, llvm::Value *Source,
747+
const clang::DeclaratorDecl *Decl,
748+
HLSLAppliedSemanticAttr *Semantic,
749+
std::optional<unsigned> Index) {
750+
751+
std::string SemanticName = Semantic->getAttrName()->getName().upper();
752+
if (SemanticName == "SV_POSITION")
753+
createSPIRVBuiltinStore(B, CGM.getModule(), Source,
754+
Semantic->getAttrName()->getName(),
755+
/* BuiltIn::Position */ 0);
756+
else
757+
llvm_unreachable("non-handled system semantic. FIXME.");
758+
}
759+
672760
llvm::Value *CGHLSLRuntime::handleScalarSemanticLoad(
673761
IRBuilder<> &B, const FunctionDecl *FD, llvm::Type *Type,
674762
const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic) {
@@ -679,6 +767,16 @@ llvm::Value *CGHLSLRuntime::handleScalarSemanticLoad(
679767
return emitUserSemanticLoad(B, Type, Decl, Semantic, Index);
680768
}
681769

770+
void CGHLSLRuntime::handleScalarSemanticStore(
771+
IRBuilder<> &B, const FunctionDecl *FD, llvm::Value *Source,
772+
const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic) {
773+
std::optional<unsigned> Index = Semantic->getSemanticIndex();
774+
if (Semantic->getAttrName()->getName().starts_with_insensitive("SV_"))
775+
emitSystemSemanticStore(B, Source, Decl, Semantic, Index);
776+
else
777+
emitUserSemanticStore(B, Source, Decl, Semantic, Index);
778+
}
779+
682780
std::pair<llvm::Value *, specific_attr_iterator<HLSLAppliedSemanticAttr>>
683781
CGHLSLRuntime::handleStructSemanticLoad(
684782
IRBuilder<> &B, const FunctionDecl *FD, llvm::Type *Type,
@@ -705,6 +803,35 @@ CGHLSLRuntime::handleStructSemanticLoad(
705803
return std::make_pair(Aggregate, AttrBegin);
706804
}
707805

806+
specific_attr_iterator<HLSLAppliedSemanticAttr>
807+
CGHLSLRuntime::handleStructSemanticStore(
808+
IRBuilder<> &B, const FunctionDecl *FD, llvm::Value *Source,
809+
const clang::DeclaratorDecl *Decl,
810+
specific_attr_iterator<HLSLAppliedSemanticAttr> AttrBegin,
811+
specific_attr_iterator<HLSLAppliedSemanticAttr> AttrEnd) {
812+
813+
const llvm::StructType *ST = cast<StructType>(Source->getType());
814+
815+
const clang::RecordDecl *RD = nullptr;
816+
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(Decl))
817+
RD = FD->getDeclaredReturnType()->getAsRecordDecl();
818+
else
819+
RD = Decl->getType()->getAsRecordDecl();
820+
assert(RD);
821+
822+
assert(std::distance(RD->field_begin(), RD->field_end()) ==
823+
ST->getNumElements());
824+
825+
auto FieldDecl = RD->field_begin();
826+
for (unsigned I = 0; I < ST->getNumElements(); ++I) {
827+
llvm::Value *Extract = B.CreateExtractValue(Source, I);
828+
AttrBegin =
829+
handleSemanticStore(B, FD, Extract, *FieldDecl, AttrBegin, AttrEnd);
830+
}
831+
832+
return AttrBegin;
833+
}
834+
708835
std::pair<llvm::Value *, specific_attr_iterator<HLSLAppliedSemanticAttr>>
709836
CGHLSLRuntime::handleSemanticLoad(
710837
IRBuilder<> &B, const FunctionDecl *FD, llvm::Type *Type,
@@ -721,6 +848,22 @@ CGHLSLRuntime::handleSemanticLoad(
721848
AttrBegin);
722849
}
723850

851+
specific_attr_iterator<HLSLAppliedSemanticAttr>
852+
CGHLSLRuntime::handleSemanticStore(
853+
IRBuilder<> &B, const FunctionDecl *FD, llvm::Value *Source,
854+
const clang::DeclaratorDecl *Decl,
855+
specific_attr_iterator<HLSLAppliedSemanticAttr> AttrBegin,
856+
specific_attr_iterator<HLSLAppliedSemanticAttr> AttrEnd) {
857+
assert(AttrBegin != AttrEnd);
858+
if (Source->getType()->isStructTy())
859+
return handleStructSemanticStore(B, FD, Source, Decl, AttrBegin, AttrEnd);
860+
861+
HLSLAppliedSemanticAttr *Attr = *AttrBegin;
862+
++AttrBegin;
863+
handleScalarSemanticStore(B, FD, Source, Decl, Attr);
864+
return AttrBegin;
865+
}
866+
724867
void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD,
725868
llvm::Function *Fn) {
726869
llvm::Module &M = CGM.getModule();
@@ -752,20 +895,22 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD,
752895
OB.emplace_back("convergencectrl", bundleArgs);
753896
}
754897

755-
// FIXME: support struct parameters where semantics are on members.
756-
// See: https://github.com/llvm/llvm-project/issues/57874
898+
std::unordered_map<const DeclaratorDecl *, llvm::Value *> OutputSemantic;
899+
757900
unsigned SRetOffset = 0;
758901
for (const auto &Param : Fn->args()) {
759902
if (Param.hasStructRetAttr()) {
760-
// FIXME: support output.
761-
// See: https://github.com/llvm/llvm-project/issues/57874
762903
SRetOffset = 1;
763-
Args.emplace_back(PoisonValue::get(Param.getType()));
904+
llvm::Type *VarType = Param.getParamStructRetType();
905+
llvm::Value *Var = B.CreateAlloca(VarType);
906+
OutputSemantic.emplace(FD, Var);
907+
Args.push_back(Var);
764908
continue;
765909
}
766910

767911
const ParmVarDecl *PD = FD->getParamDecl(Param.getArgNo() - SRetOffset);
768912
llvm::Value *SemanticValue = nullptr;
913+
// FIXME: support inout/out parameters for semantics.
769914
if ([[maybe_unused]] HLSLParamModifierAttr *MA =
770915
PD->getAttr<HLSLParamModifierAttr>()) {
771916
llvm_unreachable("Not handled yet");
@@ -792,8 +937,20 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD,
792937

793938
CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB);
794939
CI->setCallingConv(Fn->getCallingConv());
795-
// FIXME: Handle codegen for return type semantics.
796-
// See: https://github.com/llvm/llvm-project/issues/57875
940+
941+
if (Fn->getReturnType() != CGM.VoidTy)
942+
OutputSemantic.emplace(FD, CI);
943+
944+
for (auto &[Decl, Source] : OutputSemantic) {
945+
AllocaInst *AI = dyn_cast<AllocaInst>(Source);
946+
llvm::Value *SourceValue =
947+
AI ? B.CreateLoad(AI->getAllocatedType(), Source) : Source;
948+
949+
auto AttrBegin = Decl->specific_attr_begin<HLSLAppliedSemanticAttr>();
950+
auto AttrEnd = Decl->specific_attr_end<HLSLAppliedSemanticAttr>();
951+
handleSemanticStore(B, FD, SourceValue, Decl, AttrBegin, AttrEnd);
952+
}
953+
797954
B.CreateRetVoid();
798955

799956
// Add and identify root signature to function, if applicable

clang/lib/CodeGen/CGHLSLRuntime.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,25 +176,47 @@ class CGHLSLRuntime {
176176
HLSLAppliedSemanticAttr *Semantic,
177177
std::optional<unsigned> Index);
178178

179+
void emitSystemSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Source,
180+
const clang::DeclaratorDecl *Decl,
181+
HLSLAppliedSemanticAttr *Semantic,
182+
std::optional<unsigned> Index);
183+
179184
llvm::Value *handleScalarSemanticLoad(llvm::IRBuilder<> &B,
180185
const FunctionDecl *FD,
181186
llvm::Type *Type,
182187
const clang::DeclaratorDecl *Decl,
183188
HLSLAppliedSemanticAttr *Semantic);
184189

190+
void handleScalarSemanticStore(llvm::IRBuilder<> &B, const FunctionDecl *FD,
191+
llvm::Value *Source,
192+
const clang::DeclaratorDecl *Decl,
193+
HLSLAppliedSemanticAttr *Semantic);
194+
185195
std::pair<llvm::Value *, specific_attr_iterator<HLSLAppliedSemanticAttr>>
186196
handleStructSemanticLoad(
187197
llvm::IRBuilder<> &B, const FunctionDecl *FD, llvm::Type *Type,
188198
const clang::DeclaratorDecl *Decl,
189199
specific_attr_iterator<HLSLAppliedSemanticAttr> begin,
190200
specific_attr_iterator<HLSLAppliedSemanticAttr> end);
191201

202+
specific_attr_iterator<HLSLAppliedSemanticAttr> handleStructSemanticStore(
203+
llvm::IRBuilder<> &B, const FunctionDecl *FD, llvm::Value *Source,
204+
const clang::DeclaratorDecl *Decl,
205+
specific_attr_iterator<HLSLAppliedSemanticAttr> AttrBegin,
206+
specific_attr_iterator<HLSLAppliedSemanticAttr> AttrEnd);
207+
192208
std::pair<llvm::Value *, specific_attr_iterator<HLSLAppliedSemanticAttr>>
193209
handleSemanticLoad(llvm::IRBuilder<> &B, const FunctionDecl *FD,
194210
llvm::Type *Type, const clang::DeclaratorDecl *Decl,
195211
specific_attr_iterator<HLSLAppliedSemanticAttr> begin,
196212
specific_attr_iterator<HLSLAppliedSemanticAttr> end);
197213

214+
specific_attr_iterator<HLSLAppliedSemanticAttr>
215+
handleSemanticStore(llvm::IRBuilder<> &B, const FunctionDecl *FD,
216+
llvm::Value *Source, const clang::DeclaratorDecl *Decl,
217+
specific_attr_iterator<HLSLAppliedSemanticAttr> AttrBegin,
218+
specific_attr_iterator<HLSLAppliedSemanticAttr> AttrEnd);
219+
198220
public:
199221
CGHLSLRuntime(CodeGenModule &CGM) : CGM(CGM) {}
200222
virtual ~CGHLSLRuntime() {}
@@ -249,10 +271,22 @@ class CGHLSLRuntime {
249271
HLSLAppliedSemanticAttr *Semantic,
250272
std::optional<unsigned> Index);
251273

274+
void emitSPIRVUserSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Source,
275+
HLSLAppliedSemanticAttr *Semantic,
276+
std::optional<unsigned> Index);
277+
void emitDXILUserSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Source,
278+
HLSLAppliedSemanticAttr *Semantic,
279+
std::optional<unsigned> Index);
280+
void emitUserSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Source,
281+
const clang::DeclaratorDecl *Decl,
282+
HLSLAppliedSemanticAttr *Semantic,
283+
std::optional<unsigned> Index);
284+
252285
llvm::Triple::ArchType getArch();
253286

254287
llvm::DenseMap<const clang::RecordType *, llvm::TargetExtType *> LayoutTypes;
255288
unsigned SPIRVLastAssignedInputSemanticLocation = 0;
289+
unsigned SPIRVLastAssignedOutputSemanticLocation = 0;
256290
};
257291

258292
} // namespace CodeGen

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,9 @@ bool SemaHLSL::determineActiveSemantic(
825825
ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex();
826826
}
827827

828-
const Type *T = D->getType()->getUnqualifiedDesugaredType();
828+
const Type *T = D == FD ? &*FD->getReturnType() : &*D->getType();
829+
T = T->getUnqualifiedDesugaredType();
830+
829831
const RecordType *RT = dyn_cast<RecordType>(T);
830832
if (!RT)
831833
return determineActiveSemanticOnScalar(FD, OutputDecl, D, ActiveSemantic,
@@ -915,13 +917,21 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) {
915917
if (ActiveSemantic.Semantic)
916918
ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex();
917919

920+
// FIXME: Verify output semantics in parameters.
918921
if (!determineActiveSemantic(FD, Param, Param, ActiveSemantic,
919922
ActiveInputSemantics)) {
920923
Diag(Param->getLocation(), diag::note_previous_decl) << Param;
921924
FD->setInvalidDecl();
922925
}
923926
}
924-
// FIXME: Verify return type semantic annotation.
927+
928+
SemanticInfo ActiveSemantic;
929+
llvm::StringSet<> ActiveOutputSemantics;
930+
ActiveSemantic.Semantic = FD->getAttr<HLSLParsedSemanticAttr>();
931+
if (ActiveSemantic.Semantic)
932+
ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex();
933+
if (!FD->getReturnType()->isVoidType())
934+
determineActiveSemantic(FD, FD, FD, ActiveSemantic, ActiveOutputSemantics);
925935
}
926936

927937
void SemaHLSL::checkSemanticAnnotation(

clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
// CHECK: @SV_Position = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations !0
44

55
// CHECK: define void @main() {{.*}} {
6-
float4 main(float4 p : SV_Position) {
6+
float4 main(float4 p : SV_Position) : A {
77
// CHECK: %[[#P:]] = load <4 x float>, ptr addrspace(7) @SV_Position, align 16
88
// CHECK: %[[#R:]] = call spir_func <4 x float> @_Z4mainDv4_f(<4 x float> %[[#P]])
9+
// CHECK: store <4 x float> %[[#R]], ptr addrspace(8) @A0, align 16
910
return p;
1011
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-vertex -x hlsl -emit-llvm -disable-llvm-passes -o - -hlsl-entry main %s -verify -verify-ignore-unexpected=note
2+
// RUN: %clang_cc1 -triple spirv-unknown-vulkan-vertex -x hlsl -emit-llvm -disable-llvm-passes -o - -hlsl-entry main %s -verify -verify-ignore-unexpected=note
3+
4+
float main(unsigned GI : A) {
5+
// expected-error@-1 {{semantic annotations must be present for all parameters of an entry function or patch constant function}}
6+
}

0 commit comments

Comments
 (0)