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 <algorithm>
23#include <cassert>
24
25using namespace llvm;
26using namespace llvm::hlsl;
27using llvm::dxbc::PSV::InterpolationMode;
28
29namespace {
30
31// Inclusive upper bounds of the operand enums
32constexpr uint32_t MaxCompType =
33 static_cast<uint32_t>(dxil::ElementType::LastEntry);
34constexpr uint32_t MaxSemanticKind =
35 static_cast<uint32_t>(dxbc::PSV::SemanticKind::Invalid);
36constexpr uint32_t MaxInterpMode =
37 static_cast<uint32_t>(dxbc::PSV::InterpolationMode::Invalid);
38
39Error makeError(const Twine &Msg) {
40 return createStringError(EC: inconvertibleErrorCode(), S: Msg);
41}
42
43Expected<uint64_t> extractInt(const MDNode *Node, unsigned OpId) {
44 auto *CI = mdconst::dyn_extract_or_null<ConstantInt>(MD: Node->getOperand(I: OpId));
45 if (!CI)
46 return makeError(Msg: "expected integer operand " + Twine(OpId));
47 return CI->getZExtValue();
48}
49} // namespace
50
51InterpolationModifier
52hlsl::getInterpolationSamplingLocation(InterpolationModifier Modifiers) {
53 return std::max(l: {Modifiers & InterpolationModifier::Center,
54 Modifiers & InterpolationModifier::Centroid,
55 Modifiers & InterpolationModifier::Sample});
56}
57
58dxbc::PSV::InterpolationMode
59hlsl::getInterpolationMode(InterpolationModifier Modifiers) {
60 if (Modifiers == InterpolationModifier::None)
61 return InterpolationMode::Undefined;
62 if (any(Val: Modifiers & InterpolationModifier::NoInterpolation))
63 return Modifiers == InterpolationModifier::NoInterpolation
64 ? InterpolationMode::Constant
65 : InterpolationMode::Invalid;
66
67 bool NoPerspective = any(Val: Modifiers & InterpolationModifier::NoPerspective);
68 switch (getInterpolationSamplingLocation(Modifiers)) {
69 case InterpolationModifier::Sample:
70 return NoPerspective ? InterpolationMode::LinearNoperspectiveSample
71 : InterpolationMode::LinearSample;
72 case InterpolationModifier::Centroid:
73 return NoPerspective ? InterpolationMode::LinearNoperspectiveCentroid
74 : InterpolationMode::LinearCentroid;
75 case InterpolationModifier::Center:
76 case InterpolationModifier::None:
77 return NoPerspective ? InterpolationMode::LinearNoperspective
78 : InterpolationMode::Linear;
79 default:
80 llvm_unreachable("invalid interpolation sampling location");
81 }
82}
83
84dxbc::PSV::InterpolationMode hlsl::normalizeInterpolationMode(
85 dxbc::PSV::InterpolationMode Mode, dxil::ElementType CompType,
86 dxbc::PSV::SemanticKind Kind, Triple::EnvironmentType Stage, IOType IO) {
87 if (!((Stage == Triple::Pixel && IO == IOType::In) ||
88 (Stage == Triple::Vertex && IO == IOType::Out)))
89 return InterpolationMode::Undefined;
90
91 if (Mode == InterpolationMode::Undefined) {
92 switch (CompType) {
93 case dxil::ElementType::F16:
94 case dxil::ElementType::F32:
95 case dxil::ElementType::SNormF16:
96 case dxil::ElementType::UNormF16:
97 case dxil::ElementType::SNormF32:
98 case dxil::ElementType::UNormF32:
99 Mode = InterpolationMode::Linear;
100 break;
101 default:
102 Mode = InterpolationMode::Constant;
103 break;
104 }
105 }
106
107 if (Kind == dxbc::PSV::SemanticKind::Position) {
108 switch (Mode) {
109 case InterpolationMode::Linear:
110 return InterpolationMode::LinearNoperspective;
111 case InterpolationMode::LinearCentroid:
112 return InterpolationMode::LinearNoperspectiveCentroid;
113 case InterpolationMode::LinearSample:
114 return InterpolationMode::LinearNoperspectiveSample;
115 default:
116 break;
117 }
118 }
119 return Mode;
120}
121
122dxbc::PSV::SemanticKind hlsl::getSemanticKind(StringRef SemanticName) {
123 if (!SemanticName.consume_front_insensitive(Prefix: "SV_"))
124 return dxbc::PSV::SemanticKind::Arbitrary;
125
126 for (const auto &Kind : dxbc::PSV::getSemanticKinds())
127 if (SemanticName.equals_insensitive(RHS: Kind.name()))
128 return Kind.value();
129
130 return dxbc::PSV::SemanticKind::Invalid;
131}
132
133ArrayRef<SemanticStageInfo>
134hlsl::getAvailableStages(dxbc::PSV::SemanticKind SemanticKind) {
135 switch (SemanticKind) {
136 case dxbc::PSV::SemanticKind::Arbitrary: {
137 static constexpr IOType OutOrPatchConstant =
138 IOType::Out | IOType::PatchConstantOrPrimitive;
139 static constexpr SemanticStageInfo Stages[] = {
140 {.Stage: Triple::Vertex, .AllowedIOTypesMask: IOType::InOut, .Interpretation: SemanticInterpretation::Arbitrary},
141 {.Stage: Triple::Geometry, .AllowedIOTypesMask: IOType::InOut, .Interpretation: SemanticInterpretation::Arbitrary},
142 {.Stage: Triple::Hull, .AllowedIOTypesMask: IOType::All, .Interpretation: SemanticInterpretation::Arbitrary},
143 {.Stage: Triple::Domain, .AllowedIOTypesMask: IOType::All, .Interpretation: SemanticInterpretation::Arbitrary},
144 {.Stage: Triple::Pixel, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::Arbitrary},
145 {.Stage: Triple::Mesh, .AllowedIOTypesMask: OutOrPatchConstant, .Interpretation: SemanticInterpretation::Arbitrary},
146 };
147 return Stages;
148 }
149 case dxbc::PSV::SemanticKind::DispatchThreadID:
150 case dxbc::PSV::SemanticKind::GroupID:
151 case dxbc::PSV::SemanticKind::GroupIndex:
152 case dxbc::PSV::SemanticKind::GroupThreadID: {
153 static constexpr SemanticStageInfo Stages[] = {
154 {.Stage: Triple::Compute, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::NotAllocated},
155 {.Stage: Triple::Mesh, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::NotAllocated},
156 {.Stage: Triple::Amplification, .AllowedIOTypesMask: IOType::In,
157 .Interpretation: SemanticInterpretation::NotAllocated},
158 };
159 return Stages;
160 }
161 case dxbc::PSV::SemanticKind::ViewID: {
162 static constexpr IOType InOrPatchConstant =
163 IOType::In | IOType::PatchConstantOrPrimitive;
164 static constexpr SemanticStageInfo Stages[] = {
165 {.Stage: Triple::Vertex, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::NotAllocated},
166 {.Stage: Triple::Hull, .AllowedIOTypesMask: InOrPatchConstant, .Interpretation: SemanticInterpretation::NotAllocated},
167 {.Stage: Triple::Domain, .AllowedIOTypesMask: InOrPatchConstant,
168 .Interpretation: SemanticInterpretation::NotAllocated},
169 {.Stage: Triple::Geometry, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::NotAllocated},
170 {.Stage: Triple::Pixel, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::NotAllocated},
171 {.Stage: Triple::Mesh, .AllowedIOTypesMask: InOrPatchConstant, .Interpretation: SemanticInterpretation::NotAllocated},
172 {.Stage: Triple::Amplification, .AllowedIOTypesMask: IOType::In,
173 .Interpretation: SemanticInterpretation::NotAllocated},
174 };
175 return Stages;
176 }
177 case dxbc::PSV::SemanticKind::Target: {
178 static constexpr SemanticStageInfo Stages[] = {
179 {.Stage: Triple::Pixel, .AllowedIOTypesMask: IOType::Out, .Interpretation: SemanticInterpretation::Target}};
180 return Stages;
181 }
182 case dxbc::PSV::SemanticKind::VertexID:
183 case dxbc::PSV::SemanticKind::InstanceID: {
184 static constexpr SemanticStageInfo Stages[] = {
185 {.Stage: Triple::Vertex, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::SV}};
186 return Stages;
187 }
188 case dxbc::PSV::SemanticKind::IsFrontFace: {
189 static constexpr SemanticStageInfo Stages[] = {
190 {.Stage: Triple::Geometry, .AllowedIOTypesMask: IOType::Out, .Interpretation: SemanticInterpretation::SGV},
191 {.Stage: Triple::Pixel, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::SGV}};
192 return Stages;
193 }
194 case dxbc::PSV::SemanticKind::Position: {
195 static constexpr SemanticStageInfo Stages[] = {
196 {.Stage: Triple::Vertex, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::Arbitrary},
197 {.Stage: Triple::Vertex, .AllowedIOTypesMask: IOType::Out, .Interpretation: SemanticInterpretation::SV},
198 {.Stage: Triple::Hull, .AllowedIOTypesMask: IOType::InOut, .Interpretation: SemanticInterpretation::SV},
199 {.Stage: Triple::Hull, .AllowedIOTypesMask: IOType::PatchConstantOrPrimitive,
200 .Interpretation: SemanticInterpretation::Arbitrary},
201 {.Stage: Triple::Domain, .AllowedIOTypesMask: IOType::InOut, .Interpretation: SemanticInterpretation::SV},
202 {.Stage: Triple::Domain, .AllowedIOTypesMask: IOType::PatchConstantOrPrimitive,
203 .Interpretation: SemanticInterpretation::Arbitrary},
204 {.Stage: Triple::Geometry, .AllowedIOTypesMask: IOType::InOut, .Interpretation: SemanticInterpretation::SV},
205 {.Stage: Triple::Pixel, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::SV},
206 {.Stage: Triple::Mesh, .AllowedIOTypesMask: IOType::Out, .Interpretation: SemanticInterpretation::SV},
207 };
208 return Stages;
209 }
210 case dxbc::PSV::SemanticKind::ClipDistance:
211 case dxbc::PSV::SemanticKind::CullDistance: {
212 static constexpr SemanticStageInfo Stages[] = {
213 {.Stage: Triple::Vertex, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::Arbitrary},
214 {.Stage: Triple::Vertex, .AllowedIOTypesMask: IOType::Out, .Interpretation: SemanticInterpretation::ClipCull},
215 {.Stage: Triple::Hull, .AllowedIOTypesMask: IOType::InOut, .Interpretation: SemanticInterpretation::ClipCull},
216 {.Stage: Triple::Hull, .AllowedIOTypesMask: IOType::PatchConstantOrPrimitive,
217 .Interpretation: SemanticInterpretation::Arbitrary},
218 {.Stage: Triple::Domain, .AllowedIOTypesMask: IOType::InOut, .Interpretation: SemanticInterpretation::ClipCull},
219 {.Stage: Triple::Domain, .AllowedIOTypesMask: IOType::PatchConstantOrPrimitive,
220 .Interpretation: SemanticInterpretation::Arbitrary},
221 {.Stage: Triple::Geometry, .AllowedIOTypesMask: IOType::InOut, .Interpretation: SemanticInterpretation::ClipCull},
222 {.Stage: Triple::Pixel, .AllowedIOTypesMask: IOType::In, .Interpretation: SemanticInterpretation::ClipCull},
223 {.Stage: Triple::Mesh, .AllowedIOTypesMask: IOType::Out, .Interpretation: SemanticInterpretation::ClipCull},
224 };
225 return Stages;
226 }
227 case dxbc::PSV::SemanticKind::TessFactor:
228 case dxbc::PSV::SemanticKind::InsideTessFactor: {
229 static constexpr SemanticStageInfo Stages[] = {
230 {.Stage: Triple::Hull, .AllowedIOTypesMask: IOType::PatchConstantOrPrimitive,
231 .Interpretation: SemanticInterpretation::TessFactor},
232 {.Stage: Triple::Domain, .AllowedIOTypesMask: IOType::PatchConstantOrPrimitive,
233 .Interpretation: SemanticInterpretation::TessFactor},
234 };
235 return Stages;
236 }
237 default:
238 return {};
239 }
240}
241
242SemanticInterpretation
243hlsl::getInterpretationKind(dxbc::PSV::SemanticKind SemanticKind,
244 Triple::EnvironmentType ShaderStage, IOType IOTy) {
245 assert(llvm::has_single_bit(static_cast<unsigned>(IOTy)) &&
246 "a single IOType is expected, not a mask of IOTypes");
247 for (const SemanticStageInfo &Info : getAvailableStages(SemanticKind))
248 if (Info.Stage == ShaderStage && any(Val: Info.AllowedIOTypesMask & IOTy))
249 return Info.Interpretation;
250 return SemanticInterpretation::Invalid;
251}
252
253Expected<SemanticSignatureElement>
254SemanticSignatureElement::fromMetadata(const MDNode *Node) {
255 // Operand positions within a signature element metadata node.
256 enum class OpIdx : unsigned {
257 SigId,
258 SemanticName,
259 CompType,
260 SemanticKind,
261 SemanticIndices,
262 InterpMode,
263 Rows,
264 Cols,
265 StartRow,
266 StartCol,
267 UsageMask,
268 DynIndexMask,
269 GSStream,
270 LastEntry = GSStream,
271 };
272 const unsigned NumElementOperands = to_underlying(E: OpIdx::LastEntry) + 1;
273
274 if (!Node)
275 return makeError(Msg: "signature element node is null");
276 if (Node->getNumOperands() != NumElementOperands)
277 return makeError(Msg: "signature element node has wrong number of operands");
278
279 SemanticSignatureElement Elem;
280
281 Expected<uint64_t> SigId = extractInt(Node, OpId: to_underlying(E: OpIdx::SigId));
282 if (!SigId)
283 return SigId.takeError();
284 Elem.SigId = *SigId;
285
286 auto *Name =
287 dyn_cast<MDString>(Val: Node->getOperand(I: to_underlying(E: OpIdx::SemanticName)));
288 if (!Name)
289 return makeError(Msg: "expected semantic name string");
290 Elem.SemanticName = Name->getString();
291
292 Expected<uint64_t> CompType =
293 extractInt(Node, OpId: to_underlying(E: OpIdx::CompType));
294 if (!CompType)
295 return CompType.takeError();
296 if (*CompType > MaxCompType)
297 return makeError(Msg: "invalid component type");
298 Elem.CompType = static_cast<dxil::ElementType>(*CompType);
299
300 Expected<uint64_t> SemanticKind =
301 extractInt(Node, OpId: to_underlying(E: OpIdx::SemanticKind));
302 if (!SemanticKind)
303 return SemanticKind.takeError();
304 if (*SemanticKind > MaxSemanticKind)
305 return makeError(Msg: "invalid semantic kind");
306 Elem.SemanticKind = static_cast<dxbc::PSV::SemanticKind>(*SemanticKind);
307
308 auto *Indices =
309 dyn_cast<MDNode>(Val: Node->getOperand(I: to_underlying(E: OpIdx::SemanticIndices)));
310 if (!Indices)
311 return makeError(Msg: "expected semantic indices node");
312 for (unsigned I = 0, E = Indices->getNumOperands(); I != E; ++I) {
313 Expected<uint64_t> Index = extractInt(Node: Indices, OpId: I);
314 if (!Index)
315 return Index.takeError();
316 Elem.SemanticIndices.push_back(Elt: *Index);
317 }
318
319 Expected<uint64_t> InterpMode =
320 extractInt(Node, OpId: to_underlying(E: OpIdx::InterpMode));
321 if (!InterpMode)
322 return InterpMode.takeError();
323 if (*InterpMode > MaxInterpMode)
324 return makeError(Msg: "invalid interpolation mode");
325 Elem.InterpMode = static_cast<dxbc::PSV::InterpolationMode>(*InterpMode);
326
327 Expected<uint64_t> Rows = extractInt(Node, OpId: to_underlying(E: OpIdx::Rows));
328 if (!Rows)
329 return Rows.takeError();
330 Elem.Rows = *Rows;
331
332 Expected<uint64_t> Cols = extractInt(Node, OpId: to_underlying(E: OpIdx::Cols));
333 if (!Cols)
334 return Cols.takeError();
335 if (*Cols < 1 || *Cols > 4)
336 return makeError(Msg: "number of components per row must be within 1-4");
337 Elem.Cols = *Cols;
338
339 Expected<uint64_t> StartRow =
340 extractInt(Node, OpId: to_underlying(E: OpIdx::StartRow));
341 if (!StartRow)
342 return StartRow.takeError();
343 Elem.StartRow = *StartRow;
344
345 Expected<uint64_t> StartCol =
346 extractInt(Node, OpId: to_underlying(E: OpIdx::StartCol));
347 if (!StartCol)
348 return StartCol.takeError();
349 if (*StartCol > 3 && *StartCol != UnallocatedCol)
350 return makeError(Msg: "start column must be within 0-3 or unallocated");
351 Elem.StartCol = *StartCol;
352
353 // The row/col sentinels are always set together
354 if ((Elem.StartRow == UnallocatedRow) != (Elem.StartCol == UnallocatedCol))
355 return makeError(Msg: "start row and column sentinels must be set together");
356
357 Expected<uint64_t> UsageMask =
358 extractInt(Node, OpId: to_underlying(E: OpIdx::UsageMask));
359 if (!UsageMask)
360 return UsageMask.takeError();
361 if (*UsageMask > 0xF)
362 return makeError(Msg: "usage mask must be a 4-bit value");
363 Elem.UsageMask = *UsageMask;
364
365 Expected<uint64_t> DynIndexMask =
366 extractInt(Node, OpId: to_underlying(E: OpIdx::DynIndexMask));
367 if (!DynIndexMask)
368 return DynIndexMask.takeError();
369 if (*DynIndexMask > 0xF)
370 return makeError(Msg: "dynamic index mask must be a 4-bit value");
371 Elem.DynIndexMask = *DynIndexMask;
372
373 Expected<uint64_t> GSStream =
374 extractInt(Node, OpId: to_underlying(E: OpIdx::GSStream));
375 if (!GSStream)
376 return GSStream.takeError();
377 if (*GSStream > 3)
378 return makeError(Msg: "geometry shader stream index must be within 0-3");
379 Elem.GSStream = *GSStream;
380
381 if (Elem.SemanticIndices.size() != Elem.Rows)
382 return makeError(
383 Msg: "number of semantic indices must equal the number of rows");
384
385 return Elem;
386}
387
388MDNode *SemanticSignatureElement::toMetadata(LLVMContext &Ctx) const {
389 Type *I32Ty = Type::getInt32Ty(C&: Ctx);
390 Type *I8Ty = Type::getInt8Ty(C&: Ctx);
391 auto GetI32 = [&](uint32_t Val) -> Metadata * {
392 return ConstantAsMetadata::get(C: ConstantInt::get(Ty: I32Ty, V: Val));
393 };
394 auto GetI8 = [&](uint8_t Val) -> Metadata * {
395 return ConstantAsMetadata::get(C: ConstantInt::get(Ty: I8Ty, V: Val));
396 };
397
398 SmallVector<Metadata *> IndexOps;
399 for (uint32_t Index : SemanticIndices)
400 IndexOps.push_back(Elt: GetI32(Index));
401
402 return MDNode::get(Context&: Ctx,
403 MDs: {GetI32(SigId), MDString::get(Context&: Ctx, Str: SemanticName),
404 GetI32(static_cast<uint32_t>(CompType)),
405 GetI32(static_cast<uint32_t>(SemanticKind)),
406 MDNode::get(Context&: Ctx, MDs: IndexOps),
407 GetI32(static_cast<uint32_t>(InterpMode)), GetI32(Rows),
408 GetI8(Cols), GetI32(StartRow), GetI8(StartCol),
409 GetI8(UsageMask), GetI8(DynIndexMask), GetI32(GSStream)});
410}
411