Skip to content

Commit 2fc42c7

Browse files
authored
[HLSL] Add initial support for output semantics (#168095)
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 7b8eee6 commit 2fc42c7

26 files changed

+427
-75
lines changed

clang/lib/CodeGen/CGHLSLRuntime.cpp

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

602+
static void createSPIRVLocationStore(IRBuilder<> &B, llvm::Module &M,
603+
llvm::Value *Source, unsigned Location,
604+
StringRef Name) {
605+
auto *GV = new llvm::GlobalVariable(
606+
M, Source->getType(), /* isConstant= */ false,
607+
llvm::GlobalValue::ExternalLinkage,
608+
/* Initializer= */ nullptr, /* Name= */ Name, /* insertBefore= */ nullptr,
609+
llvm::GlobalVariable::GeneralDynamicTLSModel,
610+
/* AddressSpace */ 8, /* isExternallyInitialized= */ false);
611+
GV->setVisibility(llvm::GlobalValue::HiddenVisibility);
612+
addLocationDecoration(GV, Location);
613+
B.CreateStore(Source, GV);
614+
}
615+
616+
void CGHLSLRuntime::emitSPIRVUserSemanticStore(
617+
llvm::IRBuilder<> &B, llvm::Value *Source,
618+
HLSLAppliedSemanticAttr *Semantic, std::optional<unsigned> Index) {
619+
Twine BaseName = Twine(Semantic->getAttrName()->getName());
620+
Twine VariableName = BaseName.concat(Twine(Index.value_or(0)));
621+
unsigned Location = SPIRVLastAssignedOutputSemanticLocation;
622+
623+
// DXC completely ignores the semantic/index pair. Location are assigned from
624+
// the first semantic to the last.
625+
llvm::ArrayType *AT = dyn_cast<llvm::ArrayType>(Source->getType());
626+
unsigned ElementCount = AT ? AT->getNumElements() : 1;
627+
SPIRVLastAssignedOutputSemanticLocation += ElementCount;
628+
createSPIRVLocationStore(B, CGM.getModule(), Source, Location,
629+
VariableName.str());
630+
}
631+
602632
llvm::Value *
603633
CGHLSLRuntime::emitDXILUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
604634
HLSLAppliedSemanticAttr *Semantic,
@@ -619,6 +649,23 @@ CGHLSLRuntime::emitDXILUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
619649
return Value;
620650
}
621651

652+
void CGHLSLRuntime::emitDXILUserSemanticStore(llvm::IRBuilder<> &B,
653+
llvm::Value *Source,
654+
HLSLAppliedSemanticAttr *Semantic,
655+
std::optional<unsigned> Index) {
656+
// DXIL packing rules etc shall be handled here.
657+
// FIXME: generate proper sigpoint, index, col, row values.
658+
SmallVector<Value *> Args{B.getInt32(4),
659+
B.getInt32(0),
660+
B.getInt32(0),
661+
B.getInt8(0),
662+
llvm::PoisonValue::get(B.getInt32Ty()),
663+
Source};
664+
665+
llvm::Intrinsic::ID IntrinsicID = llvm::Intrinsic::dx_store_output;
666+
B.CreateIntrinsic(/*ReturnType=*/CGM.VoidTy, IntrinsicID, Args, nullptr);
667+
}
668+
622669
llvm::Value *CGHLSLRuntime::emitUserSemanticLoad(
623670
IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
624671
HLSLAppliedSemanticAttr *Semantic, std::optional<unsigned> Index) {
@@ -631,6 +678,19 @@ llvm::Value *CGHLSLRuntime::emitUserSemanticLoad(
631678
llvm_unreachable("Unsupported target for user-semantic load.");
632679
}
633680

681+
void CGHLSLRuntime::emitUserSemanticStore(IRBuilder<> &B, llvm::Value *Source,
682+
const clang::DeclaratorDecl *Decl,
683+
HLSLAppliedSemanticAttr *Semantic,
684+
std::optional<unsigned> Index) {
685+
if (CGM.getTarget().getTriple().isSPIRV())
686+
return emitSPIRVUserSemanticStore(B, Source, Semantic, Index);
687+
688+
if (CGM.getTarget().getTriple().isDXIL())
689+
return emitDXILUserSemanticStore(B, Source, Semantic, Index);
690+
691+
llvm_unreachable("Unsupported target for user-semantic load.");
692+
}
693+
634694
llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
635695
IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
636696
HLSLAppliedSemanticAttr *Semantic, std::optional<unsigned> Index) {
@@ -679,6 +739,34 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
679739
llvm_unreachable("non-handled system semantic. FIXME.");
680740
}
681741

742+
static void createSPIRVBuiltinStore(IRBuilder<> &B, llvm::Module &M,
743+
llvm::Value *Source, const Twine &Name,
744+
unsigned BuiltInID) {
745+
auto *GV = new llvm::GlobalVariable(
746+
M, Source->getType(), /* isConstant= */ false,
747+
llvm::GlobalValue::ExternalLinkage,
748+
/* Initializer= */ nullptr, Name, /* insertBefore= */ nullptr,
749+
llvm::GlobalVariable::GeneralDynamicTLSModel,
750+
/* AddressSpace */ 8, /* isExternallyInitialized= */ false);
751+
addSPIRVBuiltinDecoration(GV, BuiltInID);
752+
GV->setVisibility(llvm::GlobalValue::HiddenVisibility);
753+
B.CreateStore(Source, GV);
754+
}
755+
756+
void CGHLSLRuntime::emitSystemSemanticStore(IRBuilder<> &B, llvm::Value *Source,
757+
const clang::DeclaratorDecl *Decl,
758+
HLSLAppliedSemanticAttr *Semantic,
759+
std::optional<unsigned> Index) {
760+
761+
std::string SemanticName = Semantic->getAttrName()->getName().upper();
762+
if (SemanticName == "SV_POSITION")
763+
createSPIRVBuiltinStore(B, CGM.getModule(), Source,
764+
Semantic->getAttrName()->getName(),
765+
/* BuiltIn::Position */ 0);
766+
else
767+
llvm_unreachable("non-handled system semantic. FIXME.");
768+
}
769+
682770
llvm::Value *CGHLSLRuntime::handleScalarSemanticLoad(
683771
IRBuilder<> &B, const FunctionDecl *FD, llvm::Type *Type,
684772
const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic) {
@@ -689,6 +777,16 @@ llvm::Value *CGHLSLRuntime::handleScalarSemanticLoad(
689777
return emitUserSemanticLoad(B, Type, Decl, Semantic, Index);
690778
}
691779

780+
void CGHLSLRuntime::handleScalarSemanticStore(
781+
IRBuilder<> &B, const FunctionDecl *FD, llvm::Value *Source,
782+
const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic) {
783+
std::optional<unsigned> Index = Semantic->getSemanticIndex();
784+
if (Semantic->getAttrName()->getName().starts_with_insensitive("SV_"))
785+
emitSystemSemanticStore(B, Source, Decl, Semantic, Index);
786+
else
787+
emitUserSemanticStore(B, Source, Decl, Semantic, Index);
788+
}
789+
692790
std::pair<llvm::Value *, specific_attr_iterator<HLSLAppliedSemanticAttr>>
693791
CGHLSLRuntime::handleStructSemanticLoad(
694792
IRBuilder<> &B, const FunctionDecl *FD, llvm::Type *Type,
@@ -715,6 +813,35 @@ CGHLSLRuntime::handleStructSemanticLoad(
715813
return std::make_pair(Aggregate, AttrBegin);
716814
}
717815

816+
specific_attr_iterator<HLSLAppliedSemanticAttr>
817+
CGHLSLRuntime::handleStructSemanticStore(
818+
IRBuilder<> &B, const FunctionDecl *FD, llvm::Value *Source,
819+
const clang::DeclaratorDecl *Decl,
820+
specific_attr_iterator<HLSLAppliedSemanticAttr> AttrBegin,
821+
specific_attr_iterator<HLSLAppliedSemanticAttr> AttrEnd) {
822+
823+
const llvm::StructType *ST = cast<StructType>(Source->getType());
824+
825+
const clang::RecordDecl *RD = nullptr;
826+
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(Decl))
827+
RD = FD->getDeclaredReturnType()->getAsRecordDecl();
828+
else
829+
RD = Decl->getType()->getAsRecordDecl();
830+
assert(RD);
831+
832+
assert(std::distance(RD->field_begin(), RD->field_end()) ==
833+
ST->getNumElements());
834+
835+
auto FieldDecl = RD->field_begin();
836+
for (unsigned I = 0; I < ST->getNumElements(); ++I) {
837+
llvm::Value *Extract = B.CreateExtractValue(Source, I);
838+
AttrBegin =
839+
handleSemanticStore(B, FD, Extract, *FieldDecl, AttrBegin, AttrEnd);
840+
}
841+
842+
return AttrBegin;
843+
}
844+
718845
std::pair<llvm::Value *, specific_attr_iterator<HLSLAppliedSemanticAttr>>
719846
CGHLSLRuntime::handleSemanticLoad(
720847
IRBuilder<> &B, const FunctionDecl *FD, llvm::Type *Type,
@@ -731,6 +858,22 @@ CGHLSLRuntime::handleSemanticLoad(
731858
AttrBegin);
732859
}
733860

861+
specific_attr_iterator<HLSLAppliedSemanticAttr>
862+
CGHLSLRuntime::handleSemanticStore(
863+
IRBuilder<> &B, const FunctionDecl *FD, llvm::Value *Source,
864+
const clang::DeclaratorDecl *Decl,
865+
specific_attr_iterator<HLSLAppliedSemanticAttr> AttrBegin,
866+
specific_attr_iterator<HLSLAppliedSemanticAttr> AttrEnd) {
867+
assert(AttrBegin != AttrEnd);
868+
if (Source->getType()->isStructTy())
869+
return handleStructSemanticStore(B, FD, Source, Decl, AttrBegin, AttrEnd);
870+
871+
HLSLAppliedSemanticAttr *Attr = *AttrBegin;
872+
++AttrBegin;
873+
handleScalarSemanticStore(B, FD, Source, Decl, Attr);
874+
return AttrBegin;
875+
}
876+
734877
void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD,
735878
llvm::Function *Fn) {
736879
llvm::Module &M = CGM.getModule();
@@ -762,20 +905,22 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD,
762905
OB.emplace_back("convergencectrl", bundleArgs);
763906
}
764907

765-
// FIXME: support struct parameters where semantics are on members.
766-
// See: https://github.com/llvm/llvm-project/issues/57874
908+
std::unordered_map<const DeclaratorDecl *, llvm::Value *> OutputSemantic;
909+
767910
unsigned SRetOffset = 0;
768911
for (const auto &Param : Fn->args()) {
769912
if (Param.hasStructRetAttr()) {
770-
// FIXME: support output.
771-
// See: https://github.com/llvm/llvm-project/issues/57874
772913
SRetOffset = 1;
773-
Args.emplace_back(PoisonValue::get(Param.getType()));
914+
llvm::Type *VarType = Param.getParamStructRetType();
915+
llvm::Value *Var = B.CreateAlloca(VarType);
916+
OutputSemantic.emplace(FD, Var);
917+
Args.push_back(Var);
774918
continue;
775919
}
776920

777921
const ParmVarDecl *PD = FD->getParamDecl(Param.getArgNo() - SRetOffset);
778922
llvm::Value *SemanticValue = nullptr;
923+
// FIXME: support inout/out parameters for semantics.
779924
if ([[maybe_unused]] HLSLParamModifierAttr *MA =
780925
PD->getAttr<HLSLParamModifierAttr>()) {
781926
llvm_unreachable("Not handled yet");
@@ -802,8 +947,20 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD,
802947

803948
CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB);
804949
CI->setCallingConv(Fn->getCallingConv());
805-
// FIXME: Handle codegen for return type semantics.
806-
// See: https://github.com/llvm/llvm-project/issues/57875
950+
951+
if (Fn->getReturnType() != CGM.VoidTy)
952+
OutputSemantic.emplace(FD, CI);
953+
954+
for (auto &[Decl, Source] : OutputSemantic) {
955+
AllocaInst *AI = dyn_cast<AllocaInst>(Source);
956+
llvm::Value *SourceValue =
957+
AI ? B.CreateLoad(AI->getAllocatedType(), Source) : Source;
958+
959+
auto AttrBegin = Decl->specific_attr_begin<HLSLAppliedSemanticAttr>();
960+
auto AttrEnd = Decl->specific_attr_end<HLSLAppliedSemanticAttr>();
961+
handleSemanticStore(B, FD, SourceValue, Decl, AttrBegin, AttrEnd);
962+
}
963+
807964
B.CreateRetVoid();
808965

809966
// 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
@@ -184,25 +184,47 @@ class CGHLSLRuntime {
184184
HLSLAppliedSemanticAttr *Semantic,
185185
std::optional<unsigned> Index);
186186

187+
void emitSystemSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Source,
188+
const clang::DeclaratorDecl *Decl,
189+
HLSLAppliedSemanticAttr *Semantic,
190+
std::optional<unsigned> Index);
191+
187192
llvm::Value *handleScalarSemanticLoad(llvm::IRBuilder<> &B,
188193
const FunctionDecl *FD,
189194
llvm::Type *Type,
190195
const clang::DeclaratorDecl *Decl,
191196
HLSLAppliedSemanticAttr *Semantic);
192197

198+
void handleScalarSemanticStore(llvm::IRBuilder<> &B, const FunctionDecl *FD,
199+
llvm::Value *Source,
200+
const clang::DeclaratorDecl *Decl,
201+
HLSLAppliedSemanticAttr *Semantic);
202+
193203
std::pair<llvm::Value *, specific_attr_iterator<HLSLAppliedSemanticAttr>>
194204
handleStructSemanticLoad(
195205
llvm::IRBuilder<> &B, const FunctionDecl *FD, llvm::Type *Type,
196206
const clang::DeclaratorDecl *Decl,
197207
specific_attr_iterator<HLSLAppliedSemanticAttr> begin,
198208
specific_attr_iterator<HLSLAppliedSemanticAttr> end);
199209

210+
specific_attr_iterator<HLSLAppliedSemanticAttr> handleStructSemanticStore(
211+
llvm::IRBuilder<> &B, const FunctionDecl *FD, llvm::Value *Source,
212+
const clang::DeclaratorDecl *Decl,
213+
specific_attr_iterator<HLSLAppliedSemanticAttr> AttrBegin,
214+
specific_attr_iterator<HLSLAppliedSemanticAttr> AttrEnd);
215+
200216
std::pair<llvm::Value *, specific_attr_iterator<HLSLAppliedSemanticAttr>>
201217
handleSemanticLoad(llvm::IRBuilder<> &B, const FunctionDecl *FD,
202218
llvm::Type *Type, const clang::DeclaratorDecl *Decl,
203219
specific_attr_iterator<HLSLAppliedSemanticAttr> begin,
204220
specific_attr_iterator<HLSLAppliedSemanticAttr> end);
205221

222+
specific_attr_iterator<HLSLAppliedSemanticAttr>
223+
handleSemanticStore(llvm::IRBuilder<> &B, const FunctionDecl *FD,
224+
llvm::Value *Source, const clang::DeclaratorDecl *Decl,
225+
specific_attr_iterator<HLSLAppliedSemanticAttr> AttrBegin,
226+
specific_attr_iterator<HLSLAppliedSemanticAttr> AttrEnd);
227+
206228
public:
207229
CGHLSLRuntime(CodeGenModule &CGM) : CGM(CGM) {}
208230
virtual ~CGHLSLRuntime() {}
@@ -266,10 +288,22 @@ class CGHLSLRuntime {
266288
HLSLAppliedSemanticAttr *Semantic,
267289
std::optional<unsigned> Index);
268290

291+
void emitSPIRVUserSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Source,
292+
HLSLAppliedSemanticAttr *Semantic,
293+
std::optional<unsigned> Index);
294+
void emitDXILUserSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Source,
295+
HLSLAppliedSemanticAttr *Semantic,
296+
std::optional<unsigned> Index);
297+
void emitUserSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Source,
298+
const clang::DeclaratorDecl *Decl,
299+
HLSLAppliedSemanticAttr *Semantic,
300+
std::optional<unsigned> Index);
301+
269302
llvm::Triple::ArchType getArch();
270303

271304
llvm::DenseMap<const clang::RecordType *, llvm::StructType *> LayoutTypes;
272305
unsigned SPIRVLastAssignedInputSemanticLocation = 0;
306+
unsigned SPIRVLastAssignedOutputSemanticLocation = 0;
273307
};
274308

275309
} // namespace CodeGen

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -773,7 +773,7 @@ void SemaHLSL::ActOnTopLevelFunction(FunctionDecl *FD) {
773773

774774
bool SemaHLSL::determineActiveSemanticOnScalar(
775775
FunctionDecl *FD, DeclaratorDecl *OutputDecl, DeclaratorDecl *D,
776-
SemanticInfo &ActiveSemantic, llvm::StringSet<> &ActiveInputSemantics) {
776+
SemanticInfo &ActiveSemantic, llvm::StringSet<> &UsedSemantics) {
777777
if (ActiveSemantic.Semantic == nullptr) {
778778
ActiveSemantic.Semantic = D->getAttr<HLSLParsedSemanticAttr>();
779779
if (ActiveSemantic.Semantic)
@@ -805,7 +805,7 @@ bool SemaHLSL::determineActiveSemanticOnScalar(
805805
for (unsigned I = 0; I < ElementCount; ++I) {
806806
Twine VariableName = BaseName.concat(Twine(Location + I));
807807

808-
auto [_, Inserted] = ActiveInputSemantics.insert(VariableName.str());
808+
auto [_, Inserted] = UsedSemantics.insert(VariableName.str());
809809
if (!Inserted) {
810810
Diag(D->getLocation(), diag::err_hlsl_semantic_index_overlap)
811811
<< VariableName.str();
@@ -816,26 +816,29 @@ bool SemaHLSL::determineActiveSemanticOnScalar(
816816
return true;
817817
}
818818

819-
bool SemaHLSL::determineActiveSemantic(
820-
FunctionDecl *FD, DeclaratorDecl *OutputDecl, DeclaratorDecl *D,
821-
SemanticInfo &ActiveSemantic, llvm::StringSet<> &ActiveInputSemantics) {
819+
bool SemaHLSL::determineActiveSemantic(FunctionDecl *FD,
820+
DeclaratorDecl *OutputDecl,
821+
DeclaratorDecl *D,
822+
SemanticInfo &ActiveSemantic,
823+
llvm::StringSet<> &UsedSemantics) {
822824
if (ActiveSemantic.Semantic == nullptr) {
823825
ActiveSemantic.Semantic = D->getAttr<HLSLParsedSemanticAttr>();
824826
if (ActiveSemantic.Semantic)
825827
ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex();
826828
}
827829

828-
const Type *T = D->getType()->getUnqualifiedDesugaredType();
830+
const Type *T = D == FD ? &*FD->getReturnType() : &*D->getType();
831+
T = T->getUnqualifiedDesugaredType();
832+
829833
const RecordType *RT = dyn_cast<RecordType>(T);
830834
if (!RT)
831835
return determineActiveSemanticOnScalar(FD, OutputDecl, D, ActiveSemantic,
832-
ActiveInputSemantics);
836+
UsedSemantics);
833837

834838
const RecordDecl *RD = RT->getDecl();
835839
for (FieldDecl *Field : RD->fields()) {
836840
SemanticInfo Info = ActiveSemantic;
837-
if (!determineActiveSemantic(FD, OutputDecl, Field, Info,
838-
ActiveInputSemantics)) {
841+
if (!determineActiveSemantic(FD, OutputDecl, Field, Info, UsedSemantics)) {
839842
Diag(Field->getLocation(), diag::note_hlsl_semantic_used_here) << Field;
840843
return false;
841844
}
@@ -915,13 +918,21 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) {
915918
if (ActiveSemantic.Semantic)
916919
ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex();
917920

921+
// FIXME: Verify output semantics in parameters.
918922
if (!determineActiveSemantic(FD, Param, Param, ActiveSemantic,
919923
ActiveInputSemantics)) {
920924
Diag(Param->getLocation(), diag::note_previous_decl) << Param;
921925
FD->setInvalidDecl();
922926
}
923927
}
924-
// FIXME: Verify return type semantic annotation.
928+
929+
SemanticInfo ActiveSemantic;
930+
llvm::StringSet<> ActiveOutputSemantics;
931+
ActiveSemantic.Semantic = FD->getAttr<HLSLParsedSemanticAttr>();
932+
if (ActiveSemantic.Semantic)
933+
ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex();
934+
if (!FD->getReturnType()->isVoidType())
935+
determineActiveSemantic(FD, FD, FD, ActiveSemantic, ActiveOutputSemantics);
925936
}
926937

927938
void SemaHLSL::checkSemanticAnnotation(

0 commit comments

Comments
 (0)