| 1 | //===- DXILSignatureAnalysis.cpp - Semantic signatures --------------------===// |
| 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 | #include "DXILSignatureAnalysis.h" |
| 10 | #include "DirectX.h" |
| 11 | #include "llvm/ADT/STLExtras.h" |
| 12 | #include "llvm/ADT/StringExtras.h" |
| 13 | #include "llvm/Analysis/DXILMetadataAnalysis.h" |
| 14 | #include "llvm/Frontend/HLSL/SemanticSignaturePacking.h" |
| 15 | #include "llvm/IR/Constants.h" |
| 16 | #include "llvm/IR/InstIterator.h" |
| 17 | #include "llvm/IR/IntrinsicInst.h" |
| 18 | #include "llvm/IR/IntrinsicsDirectX.h" |
| 19 | #include "llvm/IR/Module.h" |
| 20 | #include "llvm/InitializePasses.h" |
| 21 | #include "llvm/MC/DXContainerPSVInfo.h" |
| 22 | #include "llvm/Support/Error.h" |
| 23 | #include "llvm/Support/raw_ostream.h" |
| 24 | #include <array> |
| 25 | #include <set> |
| 26 | |
| 27 | using namespace llvm; |
| 28 | using namespace llvm::dxil; |
| 29 | using namespace llvm::hlsl; |
| 30 | |
| 31 | static Error signatureError(const Twine &Message) { |
| 32 | return createStringError(EC: inconvertibleErrorCode(), S: Message); |
| 33 | } |
| 34 | |
| 35 | static bool isSignatureAccess(const IntrinsicInst &I) { |
| 36 | return I.getIntrinsicID() == Intrinsic::dx_load_input || |
| 37 | I.getIntrinsicID() == Intrinsic::dx_store_output; |
| 38 | } |
| 39 | |
| 40 | // Validate widths before fromMetadata extracts integer values or narrows them. |
| 41 | static Error checkElementNode(const MDNode *Node) { |
| 42 | if (!Node || Node->getNumOperands() != 13) |
| 43 | return signatureError(Message: "expected a 13-operand signature element" ); |
| 44 | for (unsigned I = 0; I != 13; ++I) { |
| 45 | if (I == 1 || I == 4) |
| 46 | continue; |
| 47 | auto *CI = mdconst::dyn_extract_or_null<ConstantInt>(MD: Node->getOperand(I)); |
| 48 | unsigned Width = (I == 7 || (I >= 9 && I <= 11)) ? 8 : 32; |
| 49 | if (!CI || CI->getBitWidth() != Width) |
| 50 | return signatureError(Message: "expected i" + Twine(Width) + " at operand " + |
| 51 | Twine(I)); |
| 52 | } |
| 53 | if (!isa_and_nonnull<MDString>(Val: Node->getOperand(I: 1))) |
| 54 | return signatureError(Message: "expected semantic name string" ); |
| 55 | auto *Indices = dyn_cast_or_null<MDNode>(Val: Node->getOperand(I: 4)); |
| 56 | if (!Indices) |
| 57 | return signatureError(Message: "expected semantic indices node" ); |
| 58 | for (const MDOperand &Index : Indices->operands()) { |
| 59 | auto *CI = mdconst::dyn_extract_or_null<ConstantInt>(MD: Index); |
| 60 | if (!CI || CI->getBitWidth() != 32) |
| 61 | return signatureError(Message: "expected i32 semantic index" ); |
| 62 | } |
| 63 | return Error::success(); |
| 64 | } |
| 65 | |
| 66 | static bool isIntegerComponent(ElementType Type) { |
| 67 | return Type == ElementType::I1 || Type == ElementType::I16 || |
| 68 | Type == ElementType::U16 || Type == ElementType::I32 || |
| 69 | Type == ElementType::U32; |
| 70 | } |
| 71 | |
| 72 | static Error validateSemanticType(const SemanticSignatureElement &E) { |
| 73 | switch (E.SemanticKind) { |
| 74 | case dxbc::PSV::SemanticKind::Position: |
| 75 | if (E.Rows != 1 || E.Cols != 4 || |
| 76 | (E.CompType != ElementType::F16 && E.CompType != ElementType::F32)) |
| 77 | return signatureError( |
| 78 | Message: "SV_Position requires one row of four floating-point components" ); |
| 79 | break; |
| 80 | case dxbc::PSV::SemanticKind::VertexID: |
| 81 | if (E.Rows != 1 || E.Cols != 1 || |
| 82 | (E.CompType != ElementType::I16 && E.CompType != ElementType::U16 && |
| 83 | E.CompType != ElementType::I32 && E.CompType != ElementType::U32)) |
| 84 | return signatureError( |
| 85 | Message: "SV_VertexID requires a scalar 16- or 32-bit integer" ); |
| 86 | break; |
| 87 | case dxbc::PSV::SemanticKind::InstanceID: |
| 88 | if (E.Rows != 1 || E.Cols != 1 || |
| 89 | (E.CompType != ElementType::U16 && E.CompType != ElementType::U32)) |
| 90 | return signatureError( |
| 91 | Message: "SV_InstanceID requires a scalar 16- or 32-bit unsigned integer" ); |
| 92 | break; |
| 93 | default: |
| 94 | break; |
| 95 | } |
| 96 | return Error::success(); |
| 97 | } |
| 98 | |
| 99 | static Error validateSemanticIndex(const SemanticSignatureElement &E) { |
| 100 | switch (E.SemanticKind) { |
| 101 | case dxbc::PSV::SemanticKind::Target: |
| 102 | if (E.Rows != 1 || E.SemanticIndices.front() >= 8) |
| 103 | return signatureError( |
| 104 | Message: "pixel targets require one row and an index within 0-7" ); |
| 105 | break; |
| 106 | case dxbc::PSV::SemanticKind::VertexID: |
| 107 | if (E.SemanticIndices.front() != 0) |
| 108 | return signatureError(Message: "SV_VertexID requires semantic index 0" ); |
| 109 | break; |
| 110 | default: |
| 111 | break; |
| 112 | } |
| 113 | return Error::success(); |
| 114 | } |
| 115 | |
| 116 | static Error parseSignature(Metadata *MD, |
| 117 | SmallVectorImpl<SemanticSignatureElement> &Out, |
| 118 | ModuleSignatureInfo &Info, |
| 119 | Triple::EnvironmentType Stage, IOType IO, |
| 120 | bool Native16, unsigned &Extent) { |
| 121 | if (!MD) |
| 122 | return Error::success(); |
| 123 | auto *List = dyn_cast<MDNode>(Val: MD); |
| 124 | if (!List) |
| 125 | return signatureError(Message: "expected a signature element list or null" ); |
| 126 | if (List->getNumOperands() > 128) |
| 127 | return signatureError(Message: "too many signature elements" ); |
| 128 | |
| 129 | std::set<std::pair<std::string, uint32_t>> Semantics; |
| 130 | std::array<uint8_t, MaxSignatureRows> AllocatedColumns = {}; |
| 131 | std::array<dxbc::PSV::InterpolationMode, MaxSignatureRows> RowModes = {}; |
| 132 | std::array<bool, MaxSignatureRows> RowNative16 = {}; |
| 133 | for (const MDOperand &Op : List->operands()) { |
| 134 | auto *Node = dyn_cast_or_null<MDNode>(Val: Op); |
| 135 | if (Error Err = checkElementNode(Node)) |
| 136 | return Err; |
| 137 | auto Parsed = SemanticSignatureElement::fromMetadata(Node); |
| 138 | if (!Parsed) |
| 139 | return Parsed.takeError(); |
| 140 | SemanticSignatureElement E = std::move(*Parsed); |
| 141 | if (E.SigId != Out.size()) |
| 142 | return signatureError(Message: "signature IDs must be dense and in list order" ); |
| 143 | if (Stage != Triple::Vertex && Stage != Triple::Pixel) |
| 144 | return signatureError(Message: "nonempty signatures are currently supported only " |
| 145 | "for vertex and pixel entries" ); |
| 146 | if (!E.Rows || E.Rows > MaxSignatureRows) |
| 147 | return signatureError(Message: "signature row count must be within 1-32" ); |
| 148 | if (E.SemanticName.empty() || E.SemanticName.contains(C: '\0')) |
| 149 | return signatureError(Message: "invalid semantic name" ); |
| 150 | if (E.CompType != ElementType::F16 && E.CompType != ElementType::F32 && |
| 151 | !isIntegerComponent(Type: E.CompType)) |
| 152 | return signatureError(Message: "unsupported signature component type" ); |
| 153 | if (E.GSStream != 0) |
| 154 | return signatureError(Message: "nonzero stream requires a geometry output" ); |
| 155 | auto NamedKind = getSemanticKind(SemanticName: E.SemanticName); |
| 156 | auto Interpretation = getInterpretationKind(SemanticKind: NamedKind, ShaderStage: Stage, IOTy: IO); |
| 157 | if (Interpretation == SemanticInterpretation::Invalid) |
| 158 | return signatureError(Message: "invalid or unsupported semantic for this stage " |
| 159 | "and signature category" ); |
| 160 | if (Interpretation == SemanticInterpretation::NotAllocated) |
| 161 | return signatureError(Message: "semantic accessed by a dedicated intrinsic must " |
| 162 | "not appear in an input/output signature" ); |
| 163 | auto ExpectedKind = Interpretation == SemanticInterpretation::Arbitrary |
| 164 | ? dxbc::PSV::SemanticKind::Arbitrary |
| 165 | : NamedKind; |
| 166 | if (E.SemanticKind != ExpectedKind) |
| 167 | return signatureError(Message: "semantic name and kind disagree at this signature " |
| 168 | "point" ); |
| 169 | if (!E.isAllocated()) |
| 170 | return signatureError(Message: "signature element " + Twine(E.SigId) + " ('" + |
| 171 | E.SemanticName + "') has no allocated location" ); |
| 172 | if (E.StartRow >= MaxSignatureRows || |
| 173 | E.Rows > MaxSignatureRows - E.StartRow || E.StartCol + E.Cols > 4) |
| 174 | return signatureError( |
| 175 | Message: "allocated signature element exceeds register bounds" ); |
| 176 | if (E.InterpMode == dxbc::PSV::InterpolationMode::Invalid) |
| 177 | return signatureError(Message: "invalid interpolation mode" ); |
| 178 | |
| 179 | for (uint32_t Index : E.SemanticIndices) |
| 180 | if (!Semantics.emplace(args: E.SemanticName.upper(), args&: Index).second) |
| 181 | return signatureError(Message: "duplicate semantic name and index" ); |
| 182 | if (Error Err = validateSemanticType(E)) |
| 183 | return Err; |
| 184 | if (Error Err = validateSemanticIndex(E)) |
| 185 | return Err; |
| 186 | |
| 187 | if (Stage == Triple::Vertex && IO == IOType::In && |
| 188 | (E.StartRow != Extent || E.StartCol != 0)) |
| 189 | return signatureError(Message: "vertex inputs require stacked locations in " |
| 190 | "signature order (element " + |
| 191 | Twine(E.SigId) + ")" ); |
| 192 | if (Interpretation == SemanticInterpretation::Target && |
| 193 | (E.StartRow != E.SemanticIndices.front() || E.StartCol != 0)) |
| 194 | return signatureError(Message: "pixel target location must match its semantic " |
| 195 | "index at column 0" ); |
| 196 | if (Stage == Triple::Pixel && IO == IOType::In && |
| 197 | isIntegerComponent(Type: E.CompType) && |
| 198 | E.InterpMode != dxbc::PSV::InterpolationMode::Constant) |
| 199 | return signatureError( |
| 200 | Message: "integer pixel inputs require constant interpolation" ); |
| 201 | |
| 202 | // Co-packed elements must obey row constraints, even when their component |
| 203 | // masks do not overlap. Min-precision types occupy 32-bit components. |
| 204 | bool IsNative16 = Native16 && (E.CompType == ElementType::F16 || |
| 205 | E.CompType == ElementType::I16 || |
| 206 | E.CompType == ElementType::U16); |
| 207 | uint8_t Mask = E.getDeclaredMask(); |
| 208 | for (unsigned Row = E.StartRow; Row != E.StartRow + E.Rows; ++Row) { |
| 209 | if (AllocatedColumns[Row] & Mask) |
| 210 | return signatureError(Message: "signature elements overlap at row " + |
| 211 | Twine(Row) + " (element " + Twine(E.SigId) + ")" ); |
| 212 | if (AllocatedColumns[Row] && RowModes[Row] != E.InterpMode) |
| 213 | return signatureError(Message: "signature elements have different interpolation " |
| 214 | "modes at row " + |
| 215 | Twine(Row) + " (element " + Twine(E.SigId) + ")" ); |
| 216 | if (AllocatedColumns[Row] && RowNative16[Row] != IsNative16) |
| 217 | return signatureError(Message: "signature elements have different component " |
| 218 | "widths at row " + |
| 219 | Twine(Row) + " (element " + Twine(E.SigId) + ")" ); |
| 220 | AllocatedColumns[Row] |= Mask; |
| 221 | RowModes[Row] = E.InterpMode; |
| 222 | RowNative16[Row] = IsNative16; |
| 223 | } |
| 224 | Extent = std::max(a: Extent, b: E.StartRow + E.Rows); |
| 225 | E.SemanticName = Info.Names.insert(key: E.SemanticName).first->getKey(); |
| 226 | |
| 227 | // Access masks will be recomputed so reset to 0 here. |
| 228 | E.UsageMask = 0; |
| 229 | E.DynIndexMask = 0; |
| 230 | Out.push_back(Elt: std::move(E)); |
| 231 | } |
| 232 | return Error::success(); |
| 233 | } |
| 234 | |
| 235 | static Error analyzeAccess(const IntrinsicInst &I, EntrySignature &Sig) { |
| 236 | bool Input = I.getIntrinsicID() == Intrinsic::dx_load_input; |
| 237 | auto &Elements = Input ? Sig.Inputs : Sig.Outputs; |
| 238 | auto *ID = dyn_cast<ConstantInt>(Val: I.getArgOperand(i: 0)); |
| 239 | auto *Col = dyn_cast<ConstantInt>(Val: I.getArgOperand(i: 2)); |
| 240 | StringRef Category = Input ? "input" : "output" ; |
| 241 | if (!ID) |
| 242 | return signatureError(Message: Category + |
| 243 | " signature: signature access requires a constant " |
| 244 | "element ID" ); |
| 245 | if (ID->getZExtValue() >= Elements.size()) |
| 246 | return signatureError(Message: Category + |
| 247 | " signature: signature access has an invalid element " |
| 248 | "ID " + |
| 249 | Twine(ID->getZExtValue())); |
| 250 | auto &E = Elements[ID->getZExtValue()]; |
| 251 | auto AccessError = [&](const Twine &Message) { |
| 252 | return signatureError(Message: Category + " signature element " + Twine(E.SigId) + |
| 253 | " ('" + E.SemanticName + "'): " + Message); |
| 254 | }; |
| 255 | Type *Ty = Input ? I.getType() : I.getArgOperand(i: 3)->getType(); |
| 256 | unsigned Width = 1; |
| 257 | if (auto *VT = dyn_cast<FixedVectorType>(Val: Ty)) { |
| 258 | Width = VT->getNumElements(); |
| 259 | Ty = VT->getElementType(); |
| 260 | } |
| 261 | if (!Col || Col->getZExtValue() >= E.Cols || |
| 262 | Width > E.Cols - Col->getZExtValue()) |
| 263 | return AccessError("signature access has an invalid component index" ); |
| 264 | auto *Row = dyn_cast<ConstantInt>(Val: I.getArgOperand(i: 1)); |
| 265 | if (Row && Row->getZExtValue() >= E.Rows) |
| 266 | return AccessError("signature access has an invalid row index" ); |
| 267 | if (!Row && E.Rows == 1) |
| 268 | return AccessError( |
| 269 | "dynamic indexing requires a multi-row signature element" ); |
| 270 | bool CorrectType = false; |
| 271 | switch (E.CompType) { |
| 272 | case ElementType::F16: |
| 273 | CorrectType = Ty->isHalfTy(); |
| 274 | break; |
| 275 | case ElementType::F32: |
| 276 | CorrectType = Ty->isFloatTy(); |
| 277 | break; |
| 278 | case ElementType::I16: |
| 279 | case ElementType::U16: |
| 280 | CorrectType = Ty->isIntegerTy(BitWidth: 16); |
| 281 | break; |
| 282 | case ElementType::I1: |
| 283 | // Boolean signature elements use the i32 load/store overload. |
| 284 | CorrectType = Ty->isIntegerTy(BitWidth: 32); |
| 285 | break; |
| 286 | case ElementType::I32: |
| 287 | case ElementType::U32: |
| 288 | CorrectType = Ty->isIntegerTy(BitWidth: 32); |
| 289 | break; |
| 290 | default: |
| 291 | llvm_unreachable("invalid signature component type" ); |
| 292 | } |
| 293 | if (!CorrectType) |
| 294 | return AccessError("signature access type disagrees with its element" ); |
| 295 | uint8_t Mask = ((1U << Width) - 1) << Col->getZExtValue(); |
| 296 | E.UsageMask |= Mask << E.StartCol; |
| 297 | if (!Row) |
| 298 | E.DynIndexMask |= Mask; |
| 299 | return Error::success(); |
| 300 | } |
| 301 | |
| 302 | static void buildDependencyMap(EntrySignature &Sig) { |
| 303 | // Each output vector needs four component bits, so one 32-bit word holds |
| 304 | // eight vectors. Adding seven rounds up to include a partially filled word. |
| 305 | unsigned Words = (Sig.OutputVectors + 7) / 8; |
| 306 | Sig.InputOutputMap.assign(NumElts: Sig.InputVectors * 4 * Words, Elt: 0); |
| 307 | // Conservatively connect every accessed input component to every written |
| 308 | // output component. This includes control, memory, call, and dynamic-index |
| 309 | // dependencies without incorrectly claiming that unknown dependencies are |
| 310 | // absent. Unused components and padding remain zero. Both DXIL and PSV use |
| 311 | // this same map; a more precise dependency analysis can refine it later. |
| 312 | for (const auto &Input : Sig.Inputs) |
| 313 | for (unsigned IR = 0; IR != Input.Rows; ++IR) |
| 314 | for (unsigned IC = 0; IC != 4; ++IC) { |
| 315 | if (!(Input.UsageMask & (1U << IC))) |
| 316 | continue; |
| 317 | unsigned Base = ((Input.StartRow + IR) * 4 + IC) * Words; |
| 318 | for (const auto &Output : Sig.Outputs) |
| 319 | for (unsigned OR = 0; OR != Output.Rows; ++OR) |
| 320 | for (unsigned OC = 0; OC != 4; ++OC) { |
| 321 | if (!(Output.UsageMask & (1U << OC))) |
| 322 | continue; |
| 323 | unsigned Bit = (Output.StartRow + OR) * 4 + OC; |
| 324 | Sig.InputOutputMap[Base + Bit / 32] |= 1U << (Bit % 32); |
| 325 | } |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | static Expected<ModuleSignatureInfo> |
| 330 | analyzeModule(Module &M, const ModuleMetadataInfo &MMI) { |
| 331 | ModuleSignatureInfo Info; |
| 332 | if (NamedMDNode *Table = M.getNamedMetadata(Name: "dx.semantic.signatures" )) { |
| 333 | for (const MDNode *Record : Table->operands()) { |
| 334 | if (Record->getNumOperands() != 3) |
| 335 | return signatureError( |
| 336 | Message: "expected an entry/input/output signature triple" ); |
| 337 | auto *VAM = dyn_cast_or_null<ValueAsMetadata>(Val: Record->getOperand(I: 0)); |
| 338 | auto *F = VAM ? dyn_cast<Function>(Val: VAM->getValue()) : nullptr; |
| 339 | if (!F || F->getParent() != &M || F->isDeclaration()) |
| 340 | return signatureError( |
| 341 | Message: "signature entry must be a defined function in the module" ); |
| 342 | auto EP = |
| 343 | llvm::find_if(Range: MMI.EntryPropertyVec, |
| 344 | P: [F](const EntryProperties &E) { return E.Entry == F; }); |
| 345 | if (EP == MMI.EntryPropertyVec.end()) |
| 346 | return signatureError(Message: "signature function is not a shader entry" ); |
| 347 | if (Info.Entries.contains(Val: F)) |
| 348 | return signatureError(Message: "duplicate signature record for entry '" + |
| 349 | F->getName() + "'" ); |
| 350 | EntrySignature Sig; |
| 351 | Sig.Stage = EP->ShaderStage; |
| 352 | if (auto *Native = mdconst::dyn_extract_or_null<ConstantInt>( |
| 353 | MD: M.getModuleFlag(Key: "dx.nativelowprec" ))) |
| 354 | Sig.UseNative16Bit = !Native->isZero(); |
| 355 | if (Sig.UseNative16Bit && MMI.ShaderModelVersion < VersionTuple(6, 2)) |
| 356 | return signatureError( |
| 357 | Message: "native 16-bit signatures require shader model 6.2" ); |
| 358 | auto AddContext = [&](Error Err, StringRef Category) -> Error { |
| 359 | return signatureError(Message: "entry '" + F->getName() + "' " + Category + |
| 360 | " signature: " + toString(E: std::move(Err))); |
| 361 | }; |
| 362 | if (Error Err = |
| 363 | parseSignature(MD: Record->getOperand(I: 1), Out&: Sig.Inputs, Info, Stage: Sig.Stage, |
| 364 | IO: IOType::In, Native16: Sig.UseNative16Bit, Extent&: Sig.InputVectors)) |
| 365 | return AddContext(std::move(Err), "input" ); |
| 366 | if (Error Err = parseSignature(MD: Record->getOperand(I: 2), Out&: Sig.Outputs, Info, |
| 367 | Stage: Sig.Stage, IO: IOType::Out, Native16: Sig.UseNative16Bit, |
| 368 | Extent&: Sig.OutputVectors)) |
| 369 | return AddContext(std::move(Err), "output" ); |
| 370 | for (const Instruction &I : instructions(F)) |
| 371 | if (auto *II = dyn_cast<IntrinsicInst>(Val: &I); |
| 372 | II && isSignatureAccess(I: *II)) |
| 373 | if (Error Err = analyzeAccess(I: *II, Sig)) |
| 374 | return signatureError(Message: "entry '" + F->getName() + |
| 375 | "': " + toString(E: std::move(Err))); |
| 376 | buildDependencyMap(Sig); |
| 377 | Info.Entries.try_emplace(Key: F, Args: std::move(Sig)); |
| 378 | } |
| 379 | } |
| 380 | // Signature IDs are local to an entry. Do not guess which entry a helper's |
| 381 | // accesses belong to, even if that helper happens to have a single caller. |
| 382 | for (const Function &F : M) |
| 383 | if (!Info.Entries.contains(Val: &F)) |
| 384 | for (const Instruction &I : instructions(F)) |
| 385 | if (auto *II = dyn_cast<IntrinsicInst>(Val: &I); |
| 386 | II && isSignatureAccess(I: *II)) |
| 387 | return signatureError(Message: "signature access in function '" + F.getName() + |
| 388 | "' has no entry signature metadata" ); |
| 389 | return std::move(Info); |
| 390 | } |
| 391 | |
| 392 | static ModuleSignatureInfo collectSignatures(Module &M, |
| 393 | const ModuleMetadataInfo &MMI) { |
| 394 | auto Result = analyzeModule(M, MMI); |
| 395 | if (!Result) { |
| 396 | M.getContext().emitError(ErrorStr: "Invalid semantic signature: " + |
| 397 | toString(E: Result.takeError())); |
| 398 | return ModuleSignatureInfo(); |
| 399 | } |
| 400 | return std::move(*Result); |
| 401 | } |
| 402 | |
| 403 | static MDNode *emitSignature(LLVMContext &Ctx, |
| 404 | ArrayRef<SemanticSignatureElement> Elements, |
| 405 | VersionTuple ValidatorVersion) { |
| 406 | if (Elements.empty()) |
| 407 | return nullptr; |
| 408 | auto I32 = [&](uint32_t V) { |
| 409 | return ConstantAsMetadata::get(C: ConstantInt::get(Ty: Type::getInt32Ty(C&: Ctx), V)); |
| 410 | }; |
| 411 | auto I8 = [&](uint8_t V) { |
| 412 | return ConstantAsMetadata::get(C: ConstantInt::get(Ty: Type::getInt8Ty(C&: Ctx), V)); |
| 413 | }; |
| 414 | SmallVector<Metadata *> Nodes; |
| 415 | for (const auto &E : Elements) { |
| 416 | SmallVector<Metadata *> Indices, Props; |
| 417 | for (uint32_t Index : E.SemanticIndices) |
| 418 | Indices.push_back(Elt: I32(Index)); |
| 419 | if (E.GSStream) |
| 420 | Props.append(IL: {I32(0), I32(E.GSStream)}); |
| 421 | if (E.DynIndexMask) |
| 422 | Props.append(IL: {I32(2), I32(E.DynIndexMask)}); |
| 423 | if (E.UsageMask && |
| 424 | (ValidatorVersion.empty() || ValidatorVersion == VersionTuple(0, 0) || |
| 425 | ValidatorVersion >= VersionTuple(1, 5))) |
| 426 | Props.append(IL: {I32(3), I32(E.UsageMask >> E.StartCol)}); |
| 427 | Nodes.push_back(Elt: MDNode::get( |
| 428 | Context&: Ctx, MDs: {I32(E.SigId), MDString::get(Context&: Ctx, Str: E.SemanticName), |
| 429 | I8(static_cast<uint8_t>(E.CompType)), |
| 430 | I8(static_cast<uint8_t>(E.SemanticKind)), |
| 431 | MDNode::get(Context&: Ctx, MDs: Indices), I8(static_cast<uint8_t>(E.InterpMode)), |
| 432 | I32(E.Rows), I8(E.Cols), I32(E.StartRow), I8(E.StartCol), |
| 433 | Props.empty() ? nullptr : MDNode::get(Context&: Ctx, MDs: Props)})); |
| 434 | } |
| 435 | return MDNode::get(Context&: Ctx, MDs: Nodes); |
| 436 | } |
| 437 | |
| 438 | static dxbc::SigComponentType getComponentType(ElementType Type, bool Legacy) { |
| 439 | switch (Type) { |
| 440 | case ElementType::I1: |
| 441 | return Legacy ? dxbc::SigComponentType::Unknown |
| 442 | : dxbc::SigComponentType::UInt32; |
| 443 | case ElementType::I16: |
| 444 | return dxbc::SigComponentType::SInt16; |
| 445 | case ElementType::U16: |
| 446 | return dxbc::SigComponentType::UInt16; |
| 447 | case ElementType::I32: |
| 448 | return dxbc::SigComponentType::SInt32; |
| 449 | case ElementType::U32: |
| 450 | return dxbc::SigComponentType::UInt32; |
| 451 | case ElementType::F16: |
| 452 | return dxbc::SigComponentType::Float16; |
| 453 | case ElementType::F32: |
| 454 | return dxbc::SigComponentType::Float32; |
| 455 | default: |
| 456 | llvm_unreachable("invalid signature component type" ); |
| 457 | } |
| 458 | } |
| 459 | |
| 460 | static dxbc::D3DSystemValue getSystemValue(dxbc::PSV::SemanticKind Kind) { |
| 461 | using KindTy = dxbc::PSV::SemanticKind; |
| 462 | switch (Kind) { |
| 463 | case KindTy::Arbitrary: |
| 464 | return dxbc::D3DSystemValue::Undefined; |
| 465 | case KindTy::VertexID: |
| 466 | return dxbc::D3DSystemValue::VertexID; |
| 467 | case KindTy::InstanceID: |
| 468 | return dxbc::D3DSystemValue::InstanceID; |
| 469 | case KindTy::Position: |
| 470 | return dxbc::D3DSystemValue::Position; |
| 471 | case KindTy::IsFrontFace: |
| 472 | return dxbc::D3DSystemValue::IsFrontFace; |
| 473 | case KindTy::ClipDistance: |
| 474 | return dxbc::D3DSystemValue::ClipDistance; |
| 475 | case KindTy::CullDistance: |
| 476 | return dxbc::D3DSystemValue::CullDistance; |
| 477 | case KindTy::Target: |
| 478 | return dxbc::D3DSystemValue::Target; |
| 479 | default: |
| 480 | llvm_unreachable("validated vertex/pixel signature semantic" ); |
| 481 | } |
| 482 | } |
| 483 | |
| 484 | // The DXIL reader normalizes system-value semantic names when loading module |
| 485 | // metadata. The validator then recreates ISG1/OSG1 from those normalized names |
| 486 | // and compares the parts byte-for-byte, so the container must use the same |
| 487 | // spelling even if HLSL (or the source metadata) used a different casing. |
| 488 | static StringRef getContainerSemanticName(const SemanticSignatureElement &E) { |
| 489 | using Kind = dxbc::PSV::SemanticKind; |
| 490 | switch (E.SemanticKind) { |
| 491 | case Kind::Arbitrary: |
| 492 | return E.SemanticName; |
| 493 | case Kind::VertexID: |
| 494 | return "SV_VertexID" ; |
| 495 | case Kind::InstanceID: |
| 496 | return "SV_InstanceID" ; |
| 497 | case Kind::Position: |
| 498 | return "SV_Position" ; |
| 499 | case Kind::IsFrontFace: |
| 500 | return "SV_IsFrontFace" ; |
| 501 | case Kind::ClipDistance: |
| 502 | return "SV_ClipDistance" ; |
| 503 | case Kind::CullDistance: |
| 504 | return "SV_CullDistance" ; |
| 505 | case Kind::Target: |
| 506 | return "SV_Target" ; |
| 507 | default: |
| 508 | llvm_unreachable("validated vertex/pixel signature semantic" ); |
| 509 | } |
| 510 | } |
| 511 | |
| 512 | static void buildSignature(mcdxbc::Signature &Sig, |
| 513 | ArrayRef<SemanticSignatureElement> Elements, |
| 514 | bool Input, bool Native16, bool Legacy) { |
| 515 | for (const auto &E : Elements) { |
| 516 | uint8_t Mask = E.getDeclaredMask(); |
| 517 | uint8_t ExclusiveMask = |
| 518 | Input ? E.getAlwaysReadsMask() : E.getNeverWritesMask(); |
| 519 | if (Legacy) |
| 520 | ExclusiveMask = Input ? 0 : static_cast<uint8_t>(~Mask); |
| 521 | for (auto [Row, Index] : enumerate(First: E.SemanticIndices)) |
| 522 | Sig.addParam(Stream: E.GSStream, Name: getContainerSemanticName(E), Index, |
| 523 | SystemValue: getSystemValue(Kind: E.SemanticKind), |
| 524 | CompType: getComponentType(Type: E.CompType, Legacy), Register: E.StartRow + Row, Mask, |
| 525 | ExclusiveMask, MinPrecision: E.getMinPrecision(UseMinPrecision: !Native16)); |
| 526 | } |
| 527 | } |
| 528 | |
| 529 | static void buildPSVElements(SmallVectorImpl<mcdxbc::PSVSignatureElement> &Dest, |
| 530 | ArrayRef<SemanticSignatureElement> Elements, |
| 531 | bool Legacy) { |
| 532 | for (const auto &E : Elements) { |
| 533 | auto Type = getComponentType(Type: E.CompType, Legacy); |
| 534 | // SigComponentType and PSV ComponentType share COMPONENT_TYPE definitions. |
| 535 | Dest.push_back(Elt: {.Name: E.SemanticKind == dxbc::PSV::SemanticKind::Arbitrary |
| 536 | ? E.SemanticName |
| 537 | : StringRef(), |
| 538 | .Indices: E.SemanticIndices, .StartRow: static_cast<uint8_t>(E.StartRow), .Cols: E.Cols, |
| 539 | .StartCol: E.StartCol, .Allocated: E.isAllocated(), .Kind: E.SemanticKind, |
| 540 | .Type: static_cast<dxbc::PSV::ComponentType>(Type), .Mode: E.InterpMode, |
| 541 | .DynamicMask: E.DynIndexMask, .Stream: static_cast<uint8_t>(E.GSStream)}); |
| 542 | } |
| 543 | } |
| 544 | |
| 545 | MDTuple *EntrySignature::getAsMetadata(LLVMContext &Ctx, |
| 546 | VersionTuple ValidatorVersion) const { |
| 547 | if (Inputs.empty() && Outputs.empty()) |
| 548 | return nullptr; |
| 549 | return MDNode::get(Context&: Ctx, |
| 550 | MDs: {emitSignature(Ctx, Elements: Inputs, ValidatorVersion), |
| 551 | emitSignature(Ctx, Elements: Outputs, ValidatorVersion), nullptr}); |
| 552 | } |
| 553 | |
| 554 | void EntrySignature::buildSignatures(mcdxbc::Signature &Input, |
| 555 | mcdxbc::Signature &Output, |
| 556 | VersionTuple ValidatorVersion) const { |
| 557 | bool Legacy = !ValidatorVersion.empty() && |
| 558 | ValidatorVersion != VersionTuple(0, 0) && |
| 559 | ValidatorVersion < VersionTuple(1, 5); |
| 560 | buildSignature(Sig&: Input, Elements: Inputs, Input: true, Native16: UseNative16Bit, Legacy); |
| 561 | buildSignature(Sig&: Output, Elements: Outputs, Input: false, Native16: UseNative16Bit, Legacy); |
| 562 | } |
| 563 | |
| 564 | void EntrySignature::updatePSV(mcdxbc::PSVRuntimeInfo &PSV, |
| 565 | VersionTuple ValidatorVersion) const { |
| 566 | bool Legacy = !ValidatorVersion.empty() && |
| 567 | ValidatorVersion != VersionTuple(0, 0) && |
| 568 | ValidatorVersion < VersionTuple(1, 5); |
| 569 | buildPSVElements(Dest&: PSV.InputElements, Elements: Inputs, Legacy); |
| 570 | buildPSVElements(Dest&: PSV.OutputElements, Elements: Outputs, Legacy); |
| 571 | PSV.BaseData.SigInputVectors = InputVectors; |
| 572 | PSV.BaseData.SigOutputVectors[0] = OutputVectors; |
| 573 | PSV.InputOutputMap[0] = InputOutputMap; |
| 574 | if (Stage == Triple::Vertex) |
| 575 | PSV.BaseData.StageInfo.VS.OutputPositionPresent = |
| 576 | llvm::any_of(Range: Outputs, P: [](const auto &E) { |
| 577 | return E.SemanticKind == dxbc::PSV::SemanticKind::Position; |
| 578 | }); |
| 579 | if (Stage == Triple::Pixel) |
| 580 | PSV.BaseData.StageInfo.PS.SampleFrequency = |
| 581 | llvm::any_of(Range: Inputs, P: [](const auto &E) { |
| 582 | return E.InterpMode == dxbc::PSV::InterpolationMode::LinearSample || |
| 583 | E.InterpMode == |
| 584 | dxbc::PSV::InterpolationMode::LinearNoperspectiveSample; |
| 585 | }); |
| 586 | } |
| 587 | |
| 588 | SmallVector<uint32_t> EntrySignature::getDependencyState() const { |
| 589 | SmallVector<uint32_t> State = {InputVectors * 4, OutputVectors * 4}; |
| 590 | llvm::append_range(C&: State, R: InputOutputMap); |
| 591 | return State; |
| 592 | } |
| 593 | |
| 594 | void EntrySignature::print(raw_ostream &OS) const { |
| 595 | auto Print = [&](StringRef Name, ArrayRef<SemanticSignatureElement> Elements, |
| 596 | unsigned Vectors) { |
| 597 | OS << " " << Name << ": " << Elements.size() << " elements, " << Vectors |
| 598 | << " vectors\n" ; |
| 599 | for (const auto &E : Elements) |
| 600 | OS << " " << E.SigId << ": " << E.SemanticName << " rows=" << E.Rows |
| 601 | << " cols=" << unsigned(E.Cols) << " at " << E.StartRow << ":" |
| 602 | << unsigned(E.StartCol) << " usage=" << unsigned(E.UsageMask) |
| 603 | << " dynamic=" << unsigned(E.DynIndexMask) << '\n'; |
| 604 | }; |
| 605 | Print("Inputs" , Inputs, InputVectors); |
| 606 | Print("Outputs" , Outputs, OutputVectors); |
| 607 | } |
| 608 | |
| 609 | void ModuleSignatureInfo::print(raw_ostream &OS, const Module &M) const { |
| 610 | for (const Function &F : M) |
| 611 | if (const auto *Sig = get(F: &F)) { |
| 612 | OS << "Semantic signatures for '" << F.getName() << "':\n" ; |
| 613 | Sig->print(OS); |
| 614 | } |
| 615 | } |
| 616 | |
| 617 | AnalysisKey SignatureAnalysis::Key; |
| 618 | ModuleSignatureInfo SignatureAnalysis::run(Module &M, |
| 619 | ModuleAnalysisManager &AM) { |
| 620 | return collectSignatures(M, MMI: AM.getResult<DXILMetadataAnalysis>(IR&: M)); |
| 621 | } |
| 622 | |
| 623 | PreservedAnalyses SignatureAnalysisPrinter::run(Module &M, |
| 624 | ModuleAnalysisManager &AM) { |
| 625 | AM.getResult<SignatureAnalysis>(IR&: M).print(OS, M); |
| 626 | return PreservedAnalyses::all(); |
| 627 | } |
| 628 | |
| 629 | bool SignatureAnalysisWrapper::runOnModule(Module &M) { |
| 630 | Info = std::make_unique<ModuleSignatureInfo>(args: collectSignatures( |
| 631 | M, MMI: getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata())); |
| 632 | return false; |
| 633 | } |
| 634 | |
| 635 | void SignatureAnalysisWrapper::getAnalysisUsage(AnalysisUsage &AU) const { |
| 636 | AU.setPreservesAll(); |
| 637 | AU.addRequired<DXILMetadataAnalysisWrapperPass>(); |
| 638 | } |
| 639 | |
| 640 | void SignatureAnalysisWrapper::print(raw_ostream &OS, const Module *M) const { |
| 641 | if (Info) |
| 642 | Info->print(OS, M: *M); |
| 643 | } |
| 644 | |
| 645 | char SignatureAnalysisWrapper::ID; |
| 646 | INITIALIZE_PASS_BEGIN(SignatureAnalysisWrapper, "dxil-signature-analysis" , |
| 647 | "DXIL Semantic Signature Analysis" , false, true) |
| 648 | INITIALIZE_PASS_DEPENDENCY(DXILMetadataAnalysisWrapperPass) |
| 649 | INITIALIZE_PASS_END(SignatureAnalysisWrapper, "dxil-signature-analysis" , |
| 650 | "DXIL Semantic Signature Analysis" , false, true) |
| 651 | |