1//===- SemanticSignatures.cpp - HLSL Semantic Signature helpers -----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file This file implements a library for working with HLSL shader input and
10/// output semantic signatures and their DirectX metadata representation.
11///
12//===----------------------------------------------------------------------===//
13
14#include "llvm/Frontend/HLSL/SemanticSignatures.h"
15#include "llvm/ADT/Enum.h"
16#include "llvm/ADT/STLForwardCompat.h"
17#include "llvm/ADT/bit.h"
18#include "llvm/IR/Constants.h"
19#include "llvm/IR/Metadata.h"
20#include "llvm/IR/Type.h"
21#include "llvm/Support/ErrorHandling.h"
22#include <cassert>
23
24using namespace llvm;
25using namespace llvm::hlsl;
26
27namespace {
28
29// Inclusive upper bounds of the operand enums
30constexpr uint32_t MaxCompType =
31 static_cast<uint32_t>(dxil::ElementType::LastEntry);
32constexpr uint32_t MaxSemanticKind =
33 static_cast<uint32_t>(dxbc::PSV::SemanticKind::Invalid);
34constexpr uint32_t MaxInterpMode =
35 static_cast<uint32_t>(dxbc::PSV::InterpolationMode::Invalid);
36
37Error makeError(const Twine &Msg) {
38 return createStringError(EC: inconvertibleErrorCode(), S: Msg);
39}
40
41Expected<uint64_t> extractInt(const MDNode *Node, unsigned OpId) {
42 auto *CI = mdconst::dyn_extract_or_null<ConstantInt>(MD: Node->getOperand(I: OpId));
43 if (!CI)
44 return makeError(Msg: "expected integer operand " + Twine(OpId));
45 return CI->getZExtValue();
46}
47} // namespace
48
49dxbc::PSV::SemanticKind hlsl::getSemanticKind(StringRef SemanticName) {
50 if (!SemanticName.consume_front_insensitive(Prefix: "SV_"))
51 return dxbc::PSV::SemanticKind::Arbitrary;
52
53 for (const auto &Kind : dxbc::PSV::getSemanticKinds())
54 if (SemanticName.equals_insensitive(RHS: Kind.name()))
55 return Kind.value();
56
57 return dxbc::PSV::SemanticKind::Invalid;
58}
59
60ArrayRef<SemanticStageInfo>
61hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
62 switch (SemanticKind) {
63 case dxbc::PSV::SemanticKind::Arbitrary: {
64 static constexpr IOType OutOrPatchConstant =
65 IOType::Out | IOType::PatchConstantOrPrimitive;
66 static constexpr SemanticStageInfo Stages[] = {
67 {.Stage: Triple::Vertex, .AllowedIOTypesMask: IOType::InOut, .Interpretation: SemanticInterpretation::Arbitrary},
68 {.Stage: Triple::Geometry, .AllowedIOTypesMask: IOType::InOut, .Interpretation: SemanticInterpretation::Arbitrary},
69 {.Stage: Triple::Hull, .AllowedIOTypesMask: IOType::All, .Interpretation: SemanticInterpretation::Arbitrary},
70 {.Stage: Triple::Domain, .AllowedIOTypesMask: IOType::All, .Interpretation: SemanticInterpretation::Arbitrary},
71 {.Stage: Triple::Pixel, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::Arbitrary},
72 {.Stage: Triple::Mesh, .AllowedIOTypesMask: OutOrPatchConstant, .Interpretation: SemanticInterpretation::Arbitrary},
73 };
74 return Stages;
75 }
76 case dxbc::PSV::SemanticKind::DispatchThreadID:
77 case dxbc::PSV::SemanticKind::GroupID:
78 case dxbc::PSV::SemanticKind::GroupIndex:
79 case dxbc::PSV::SemanticKind::GroupThreadID: {
80 static constexpr SemanticStageInfo Stages[] = {
81 {.Stage: Triple::Compute, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::NotAllocated},
82 {.Stage: Triple::Mesh, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::NotAllocated},
83 {.Stage: Triple::Amplification, .AllowedIOTypesMask: IOType::In,
84 .Interpretation: SemanticInterpretation::NotAllocated},
85 };
86 return Stages;
87 }
88 case dxbc::PSV::SemanticKind::ViewID: {
89 static constexpr IOType InOrPatchConstant =
90 IOType::In | IOType::PatchConstantOrPrimitive;
91 static constexpr SemanticStageInfo Stages[] = {
92 {.Stage: Triple::Vertex, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::NotAllocated},
93 {.Stage: Triple::Hull, .AllowedIOTypesMask: InOrPatchConstant, .Interpretation: SemanticInterpretation::NotAllocated},
94 {.Stage: Triple::Domain, .AllowedIOTypesMask: InOrPatchConstant,
95 .Interpretation: SemanticInterpretation::NotAllocated},
96 {.Stage: Triple::Geometry, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::NotAllocated},
97 {.Stage: Triple::Pixel, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::NotAllocated},
98 {.Stage: Triple::Mesh, .AllowedIOTypesMask: InOrPatchConstant, .Interpretation: SemanticInterpretation::NotAllocated},
99 {.Stage: Triple::Amplification, .AllowedIOTypesMask: IOType::In,
100 .Interpretation: SemanticInterpretation::NotAllocated},
101 };
102 return Stages;
103 }
104 case dxbc::PSV::SemanticKind::Target: {
105 static constexpr SemanticStageInfo Stages[] = {
106 {.Stage: Triple::Pixel, .AllowedIOTypesMask: IOType::Out, .Interpretation: SemanticInterpretation::Target}};
107 return Stages;
108 }
109 case dxbc::PSV::SemanticKind::VertexID: {
110 static constexpr SemanticStageInfo Stages[] = {
111 {.Stage: Triple::Vertex, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::SV}};
112 return Stages;
113 }
114 case dxbc::PSV::SemanticKind::IsFrontFace: {
115 static constexpr SemanticStageInfo Stages[] = {
116 {.Stage: Triple::Geometry, .AllowedIOTypesMask: IOType::Out, .Interpretation: SemanticInterpretation::SGV},
117 {.Stage: Triple::Pixel, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::SGV}};
118 return Stages;
119 }
120 case dxbc::PSV::SemanticKind::Position: {
121 static constexpr SemanticStageInfo Stages[] = {
122 {.Stage: Triple::Vertex, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::Arbitrary},
123 {.Stage: Triple::Vertex, .AllowedIOTypesMask: IOType::Out, .Interpretation: SemanticInterpretation::SV},
124 {.Stage: Triple::Hull, .AllowedIOTypesMask: IOType::InOut, .Interpretation: SemanticInterpretation::SV},
125 {.Stage: Triple::Hull, .AllowedIOTypesMask: IOType::PatchConstantOrPrimitive,
126 .Interpretation: SemanticInterpretation::Arbitrary},
127 {.Stage: Triple::Domain, .AllowedIOTypesMask: IOType::InOut, .Interpretation: SemanticInterpretation::SV},
128 {.Stage: Triple::Domain, .AllowedIOTypesMask: IOType::PatchConstantOrPrimitive,
129 .Interpretation: SemanticInterpretation::Arbitrary},
130 {.Stage: Triple::Geometry, .AllowedIOTypesMask: IOType::InOut, .Interpretation: SemanticInterpretation::SV},
131 {.Stage: Triple::Pixel, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::SV},
132 {.Stage: Triple::Mesh, .AllowedIOTypesMask: IOType::Out, .Interpretation: SemanticInterpretation::SV},
133 };
134 return Stages;
135 }
136 case dxbc::PSV::SemanticKind::ClipDistance:
137 case dxbc::PSV::SemanticKind::CullDistance: {
138 static constexpr SemanticStageInfo Stages[] = {
139 {.Stage: Triple::Vertex, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::Arbitrary},
140 {.Stage: Triple::Vertex, .AllowedIOTypesMask: IOType::Out, .Interpretation: SemanticInterpretation::ClipCull},
141 {.Stage: Triple::Hull, .AllowedIOTypesMask: IOType::InOut, .Interpretation: SemanticInterpretation::ClipCull},
142 {.Stage: Triple::Hull, .AllowedIOTypesMask: IOType::PatchConstantOrPrimitive,
143 .Interpretation: SemanticInterpretation::Arbitrary},
144 {.Stage: Triple::Domain, .AllowedIOTypesMask: IOType::InOut, .Interpretation: SemanticInterpretation::ClipCull},
145 {.Stage: Triple::Domain, .AllowedIOTypesMask: IOType::PatchConstantOrPrimitive,
146 .Interpretation: SemanticInterpretation::Arbitrary},
147 {.Stage: Triple::Geometry, .AllowedIOTypesMask: IOType::InOut, .Interpretation: SemanticInterpretation::ClipCull},
148 {.Stage: Triple::Pixel, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::ClipCull},
149 {.Stage: Triple::Mesh, .AllowedIOTypesMask: IOType::Out, .Interpretation: SemanticInterpretation::ClipCull},
150 };
151 return Stages;
152 }
153 case dxbc::PSV::SemanticKind::TessFactor:
154 case dxbc::PSV::SemanticKind::InsideTessFactor: {
155 static constexpr SemanticStageInfo Stages[] = {
156 {.Stage: Triple::Hull, .AllowedIOTypesMask: IOType::PatchConstantOrPrimitive,
157 .Interpretation: SemanticInterpretation::TessFactor},
158 {.Stage: Triple::Domain, .AllowedIOTypesMask: IOType::PatchConstantOrPrimitive,
159 .Interpretation: SemanticInterpretation::TessFactor},
160 };
161 return Stages;
162 }
163 default:
164 return {};
165 }
166}
167
168SemanticInterpretation
169hlsl::getInterpretationKind(dxbc::PSV::SemanticKind SemanticKind,
170 Triple::EnvironmentType ShaderStage, IOType IOTy) {
171 assert(llvm::has_single_bit(static_cast<unsigned>(IOTy)) &&
172 "a single IOType is expected, not a mask of IOTypes");
173 for (const SemanticStageInfo &Info : getAvailableStages(SemanticKind))
174 if (Info.Stage == ShaderStage && any(Val: Info.AllowedIOTypesMask & IOTy))
175 return Info.Interpretation;
176 return SemanticInterpretation::Invalid;
177}
178
179Expected<SemanticSignatureElement>
180SemanticSignatureElement::fromMetadata(const MDNode *Node) {
181 // Operand positions within a signature element metadata node.
182 enum class OpIdx : unsigned {
183 SigId,
184 SemanticName,
185 CompType,
186 SemanticKind,
187 SemanticIndices,
188 InterpMode,
189 Rows,
190 Cols,
191 StartRow,
192 StartCol,
193 UsageMask,
194 DynIndexMask,
195 GSStream,
196 LastEntry = GSStream,
197 };
198 const unsigned NumElementOperands = to_underlying(E: OpIdx::LastEntry) + 1;
199
200 if (!Node)
201 return makeError(Msg: "signature element node is null");
202 if (Node->getNumOperands() != NumElementOperands)
203 return makeError(Msg: "signature element node has wrong number of operands");
204
205 SemanticSignatureElement Elem;
206
207 Expected<uint64_t> SigId = extractInt(Node, OpId: to_underlying(E: OpIdx::SigId));
208 if (!SigId)
209 return SigId.takeError();
210 Elem.SigId = *SigId;
211
212 auto *Name =
213 dyn_cast<MDString>(Val: Node->getOperand(I: to_underlying(E: OpIdx::SemanticName)));
214 if (!Name)
215 return makeError(Msg: "expected semantic name string");
216 Elem.SemanticName = Name->getString();
217
218 Expected<uint64_t> CompType =
219 extractInt(Node, OpId: to_underlying(E: OpIdx::CompType));
220 if (!CompType)
221 return CompType.takeError();
222 if (*CompType > MaxCompType)
223 return makeError(Msg: "invalid component type");
224 Elem.CompType = static_cast<dxil::ElementType>(*CompType);
225
226 Expected<uint64_t> SemanticKind =
227 extractInt(Node, OpId: to_underlying(E: OpIdx::SemanticKind));
228 if (!SemanticKind)
229 return SemanticKind.takeError();
230 if (*SemanticKind > MaxSemanticKind)
231 return makeError(Msg: "invalid semantic kind");
232 Elem.SemanticKind = static_cast<dxbc::PSV::SemanticKind>(*SemanticKind);
233
234 auto *Indices =
235 dyn_cast<MDNode>(Val: Node->getOperand(I: to_underlying(E: OpIdx::SemanticIndices)));
236 if (!Indices)
237 return makeError(Msg: "expected semantic indices node");
238 for (unsigned I = 0, E = Indices->getNumOperands(); I != E; ++I) {
239 Expected<uint64_t> Index = extractInt(Node: Indices, OpId: I);
240 if (!Index)
241 return Index.takeError();
242 Elem.SemanticIndices.push_back(Elt: *Index);
243 }
244
245 Expected<uint64_t> InterpMode =
246 extractInt(Node, OpId: to_underlying(E: OpIdx::InterpMode));
247 if (!InterpMode)
248 return InterpMode.takeError();
249 if (*InterpMode > MaxInterpMode)
250 return makeError(Msg: "invalid interpolation mode");
251 Elem.InterpMode = static_cast<dxbc::PSV::InterpolationMode>(*InterpMode);
252
253 Expected<uint64_t> Rows = extractInt(Node, OpId: to_underlying(E: OpIdx::Rows));
254 if (!Rows)
255 return Rows.takeError();
256 Elem.Rows = *Rows;
257
258 Expected<uint64_t> Cols = extractInt(Node, OpId: to_underlying(E: OpIdx::Cols));
259 if (!Cols)
260 return Cols.takeError();
261 if (*Cols < 1 || *Cols > 4)
262 return makeError(Msg: "number of components per row must be within 1-4");
263 Elem.Cols = *Cols;
264
265 Expected<uint64_t> StartRow =
266 extractInt(Node, OpId: to_underlying(E: OpIdx::StartRow));
267 if (!StartRow)
268 return StartRow.takeError();
269 Elem.StartRow = *StartRow;
270
271 Expected<uint64_t> StartCol =
272 extractInt(Node, OpId: to_underlying(E: OpIdx::StartCol));
273 if (!StartCol)
274 return StartCol.takeError();
275 if (*StartCol > 3 && *StartCol != UnallocatedCol)
276 return makeError(Msg: "start column must be within 0-3 or unallocated");
277 Elem.StartCol = *StartCol;
278
279 // The row/col sentinels are always set together
280 if ((Elem.StartRow == UnallocatedRow) != (Elem.StartCol == UnallocatedCol))
281 return makeError(Msg: "start row and column sentinels must be set together");
282
283 Expected<uint64_t> UsageMask =
284 extractInt(Node, OpId: to_underlying(E: OpIdx::UsageMask));
285 if (!UsageMask)
286 return UsageMask.takeError();
287 if (*UsageMask > 0xF)
288 return makeError(Msg: "usage mask must be a 4-bit value");
289 Elem.UsageMask = *UsageMask;
290
291 Expected<uint64_t> DynIndexMask =
292 extractInt(Node, OpId: to_underlying(E: OpIdx::DynIndexMask));
293 if (!DynIndexMask)
294 return DynIndexMask.takeError();
295 if (*DynIndexMask > 0xF)
296 return makeError(Msg: "dynamic index mask must be a 4-bit value");
297 Elem.DynIndexMask = *DynIndexMask;
298
299 Expected<uint64_t> GSStream =
300 extractInt(Node, OpId: to_underlying(E: OpIdx::GSStream));
301 if (!GSStream)
302 return GSStream.takeError();
303 if (*GSStream > 3)
304 return makeError(Msg: "geometry shader stream index must be within 0-3");
305 Elem.GSStream = *GSStream;
306
307 if (Elem.SemanticIndices.size() != Elem.Rows)
308 return makeError(
309 Msg: "number of semantic indices must equal the number of rows");
310
311 return Elem;
312}
313
314MDNode *SemanticSignatureElement::toMetadata(LLVMContext &Ctx) const {
315 Type *I32Ty = Type::getInt32Ty(C&: Ctx);
316 Type *I8Ty = Type::getInt8Ty(C&: Ctx);
317 auto GetI32 = [&](uint32_t Val) -> Metadata * {
318 return ConstantAsMetadata::get(C: ConstantInt::get(Ty: I32Ty, V: Val));
319 };
320 auto GetI8 = [&](uint8_t Val) -> Metadata * {
321 return ConstantAsMetadata::get(C: ConstantInt::get(Ty: I8Ty, V: Val));
322 };
323
324 SmallVector<Metadata *> IndexOps;
325 for (uint32_t Index : SemanticIndices)
326 IndexOps.push_back(Elt: GetI32(Index));
327
328 return MDNode::get(Context&: Ctx,
329 MDs: {GetI32(SigId), MDString::get(Context&: Ctx, Str: SemanticName),
330 GetI32(static_cast<uint32_t>(CompType)),
331 GetI32(static_cast<uint32_t>(SemanticKind)),
332 MDNode::get(Context&: Ctx, MDs: IndexOps),
333 GetI32(static_cast<uint32_t>(InterpMode)), GetI32(Rows),
334 GetI8(Cols), GetI32(StartRow), GetI8(StartCol),
335 GetI8(UsageMask), GetI8(DynIndexMask), GetI32(GSStream)});
336}
337