blob: f7ea8eb9a6e56c236f2c126a422d344a863c44a9 [file] [edit]
//===- SemanticSignatures.cpp - HLSL Semantic Signature helpers -----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file This file implements a library for working with HLSL shader input and
/// output semantic signatures and their DirectX metadata representation.
///
//===----------------------------------------------------------------------===//
#include "llvm/Frontend/HLSL/SemanticSignatures.h"
#include "llvm/ADT/STLForwardCompat.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Type.h"
using namespace llvm;
using namespace llvm::hlsl;
namespace {
// Inclusive upper bounds of the operand enums
constexpr uint32_t MaxCompType =
static_cast<uint32_t>(dxil::ElementType::LastEntry);
constexpr uint32_t MaxSemanticKind =
static_cast<uint32_t>(dxbc::PSV::SemanticKind::Invalid);
constexpr uint32_t MaxInterpMode =
static_cast<uint32_t>(dxbc::PSV::InterpolationMode::Invalid);
Error makeError(const Twine &Msg) {
return createStringError(inconvertibleErrorCode(), Msg);
}
Expected<uint64_t> extractInt(const MDNode *Node, unsigned OpId) {
auto *CI = mdconst::dyn_extract_or_null<ConstantInt>(Node->getOperand(OpId));
if (!CI)
return makeError("expected integer operand " + Twine(OpId));
return CI->getZExtValue();
}
} // namespace
Expected<SemanticSignatureElement>
SemanticSignatureElement::fromMetadata(const MDNode *Node) {
// Operand positions within a signature element metadata node.
enum class OpIdx : unsigned {
SigId,
SemanticName,
CompType,
SemanticKind,
SemanticIndices,
InterpMode,
Rows,
Cols,
StartRow,
StartCol,
UsageMask,
DynIndexMask,
GSStream,
LastEntry = GSStream,
};
const unsigned NumElementOperands = to_underlying(OpIdx::LastEntry) + 1;
if (!Node)
return makeError("signature element node is null");
if (Node->getNumOperands() != NumElementOperands)
return makeError("signature element node has wrong number of operands");
SemanticSignatureElement Elem;
Expected<uint64_t> SigId = extractInt(Node, to_underlying(OpIdx::SigId));
if (!SigId)
return SigId.takeError();
Elem.SigId = *SigId;
auto *Name =
dyn_cast<MDString>(Node->getOperand(to_underlying(OpIdx::SemanticName)));
if (!Name)
return makeError("expected semantic name string");
Elem.SemanticName = Name->getString();
Expected<uint64_t> CompType =
extractInt(Node, to_underlying(OpIdx::CompType));
if (!CompType)
return CompType.takeError();
if (*CompType > MaxCompType)
return makeError("invalid component type");
Elem.CompType = static_cast<dxil::ElementType>(*CompType);
Expected<uint64_t> SemanticKind =
extractInt(Node, to_underlying(OpIdx::SemanticKind));
if (!SemanticKind)
return SemanticKind.takeError();
if (*SemanticKind > MaxSemanticKind)
return makeError("invalid semantic kind");
Elem.SemanticKind = static_cast<dxbc::PSV::SemanticKind>(*SemanticKind);
auto *Indices =
dyn_cast<MDNode>(Node->getOperand(to_underlying(OpIdx::SemanticIndices)));
if (!Indices)
return makeError("expected semantic indices node");
for (unsigned I = 0, E = Indices->getNumOperands(); I != E; ++I) {
Expected<uint64_t> Index = extractInt(Indices, I);
if (!Index)
return Index.takeError();
Elem.SemanticIndices.push_back(*Index);
}
Expected<uint64_t> InterpMode =
extractInt(Node, to_underlying(OpIdx::InterpMode));
if (!InterpMode)
return InterpMode.takeError();
if (*InterpMode > MaxInterpMode)
return makeError("invalid interpolation mode");
Elem.InterpMode = static_cast<dxbc::PSV::InterpolationMode>(*InterpMode);
Expected<uint64_t> Rows = extractInt(Node, to_underlying(OpIdx::Rows));
if (!Rows)
return Rows.takeError();
Elem.Rows = *Rows;
Expected<uint64_t> Cols = extractInt(Node, to_underlying(OpIdx::Cols));
if (!Cols)
return Cols.takeError();
if (*Cols < 1 || *Cols > 4)
return makeError("number of components per row must be within 1-4");
Elem.Cols = *Cols;
Expected<uint64_t> StartRow =
extractInt(Node, to_underlying(OpIdx::StartRow));
if (!StartRow)
return StartRow.takeError();
Elem.StartRow = *StartRow;
Expected<uint64_t> StartCol =
extractInt(Node, to_underlying(OpIdx::StartCol));
if (!StartCol)
return StartCol.takeError();
if (*StartCol > 3 && *StartCol != UnallocatedCol)
return makeError("start column must be within 0-3 or unallocated");
Elem.StartCol = *StartCol;
// The row/col sentinels are always set together
if ((Elem.StartRow == UnallocatedRow) != (Elem.StartCol == UnallocatedCol))
return makeError("start row and column sentinels must be set together");
Expected<uint64_t> UsageMask =
extractInt(Node, to_underlying(OpIdx::UsageMask));
if (!UsageMask)
return UsageMask.takeError();
if (*UsageMask > 0xF)
return makeError("usage mask must be a 4-bit value");
Elem.UsageMask = *UsageMask;
Expected<uint64_t> DynIndexMask =
extractInt(Node, to_underlying(OpIdx::DynIndexMask));
if (!DynIndexMask)
return DynIndexMask.takeError();
if (*DynIndexMask > 0xF)
return makeError("dynamic index mask must be a 4-bit value");
Elem.DynIndexMask = *DynIndexMask;
Expected<uint64_t> GSStream =
extractInt(Node, to_underlying(OpIdx::GSStream));
if (!GSStream)
return GSStream.takeError();
if (*GSStream > 3)
return makeError("geometry shader stream index must be within 0-3");
Elem.GSStream = *GSStream;
if (Elem.SemanticIndices.size() != Elem.Rows)
return makeError(
"number of semantic indices must equal the number of rows");
return Elem;
}
MDNode *SemanticSignatureElement::toMetadata(LLVMContext &Ctx) const {
Type *I32Ty = Type::getInt32Ty(Ctx);
Type *I8Ty = Type::getInt8Ty(Ctx);
auto GetI32 = [&](uint32_t Val) -> Metadata * {
return ConstantAsMetadata::get(ConstantInt::get(I32Ty, Val));
};
auto GetI8 = [&](uint8_t Val) -> Metadata * {
return ConstantAsMetadata::get(ConstantInt::get(I8Ty, Val));
};
SmallVector<Metadata *> IndexOps;
for (uint32_t Index : SemanticIndices)
IndexOps.push_back(GetI32(Index));
return MDNode::get(Ctx,
{GetI32(SigId), MDString::get(Ctx, SemanticName),
GetI32(static_cast<uint32_t>(CompType)),
GetI32(static_cast<uint32_t>(SemanticKind)),
MDNode::get(Ctx, IndexOps),
GetI32(static_cast<uint32_t>(InterpMode)), GetI32(Rows),
GetI8(Cols), GetI32(StartRow), GetI8(StartCol),
GetI8(UsageMask), GetI8(DynIndexMask), GetI32(GSStream)});
}