309 lines
10 KiB
C++
309 lines
10 KiB
C++
//===- DXILMetadata.cpp - DXIL Metadata helper objects --------------------===//
|
|
//
|
|
// 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 contains helper objects for working with DXIL metadata.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "DXILMetadata.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/IRBuilder.h"
|
|
#include "llvm/IR/Metadata.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/Support/VersionTuple.h"
|
|
#include "llvm/TargetParser/Triple.h"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::dxil;
|
|
|
|
ValidatorVersionMD::ValidatorVersionMD(Module &M)
|
|
: Entry(M.getOrInsertNamedMetadata("dx.valver")) {}
|
|
|
|
void ValidatorVersionMD::update(VersionTuple ValidatorVer) {
|
|
auto &Ctx = Entry->getParent()->getContext();
|
|
IRBuilder<> B(Ctx);
|
|
Metadata *MDVals[2];
|
|
MDVals[0] = ConstantAsMetadata::get(B.getInt32(ValidatorVer.getMajor()));
|
|
MDVals[1] =
|
|
ConstantAsMetadata::get(B.getInt32(ValidatorVer.getMinor().value_or(0)));
|
|
|
|
if (isEmpty())
|
|
Entry->addOperand(MDNode::get(Ctx, MDVals));
|
|
else
|
|
Entry->setOperand(0, MDNode::get(Ctx, MDVals));
|
|
}
|
|
|
|
bool ValidatorVersionMD::isEmpty() { return Entry->getNumOperands() == 0; }
|
|
|
|
static StringRef getShortShaderStage(Triple::EnvironmentType Env) {
|
|
switch (Env) {
|
|
case Triple::Pixel:
|
|
return "ps";
|
|
case Triple::Vertex:
|
|
return "vs";
|
|
case Triple::Geometry:
|
|
return "gs";
|
|
case Triple::Hull:
|
|
return "hs";
|
|
case Triple::Domain:
|
|
return "ds";
|
|
case Triple::Compute:
|
|
return "cs";
|
|
case Triple::Library:
|
|
return "lib";
|
|
case Triple::Mesh:
|
|
return "ms";
|
|
case Triple::Amplification:
|
|
return "as";
|
|
default:
|
|
break;
|
|
}
|
|
llvm_unreachable("Unsupported environment for DXIL generation.");
|
|
return "";
|
|
}
|
|
|
|
void dxil::createShaderModelMD(Module &M) {
|
|
NamedMDNode *Entry = M.getOrInsertNamedMetadata("dx.shaderModel");
|
|
Triple TT(M.getTargetTriple());
|
|
VersionTuple Ver = TT.getOSVersion();
|
|
LLVMContext &Ctx = M.getContext();
|
|
IRBuilder<> B(Ctx);
|
|
|
|
Metadata *Vals[3];
|
|
Vals[0] = MDString::get(Ctx, getShortShaderStage(TT.getEnvironment()));
|
|
Vals[1] = ConstantAsMetadata::get(B.getInt32(Ver.getMajor()));
|
|
Vals[2] = ConstantAsMetadata::get(B.getInt32(Ver.getMinor().value_or(0)));
|
|
Entry->addOperand(MDNode::get(Ctx, Vals));
|
|
}
|
|
|
|
static uint32_t getShaderStage(Triple::EnvironmentType Env) {
|
|
return (uint32_t)Env - (uint32_t)llvm::Triple::Pixel;
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct EntryProps {
|
|
Triple::EnvironmentType ShaderKind;
|
|
// FIXME: support more shader profiles.
|
|
// See https://github.com/llvm/llvm-project/issues/57927.
|
|
struct {
|
|
unsigned NumThreads[3];
|
|
} CS;
|
|
|
|
EntryProps(Function &F, Triple::EnvironmentType ModuleShaderKind)
|
|
: ShaderKind(ModuleShaderKind) {
|
|
|
|
if (ShaderKind == Triple::EnvironmentType::Library) {
|
|
Attribute EntryAttr = F.getFnAttribute("hlsl.shader");
|
|
StringRef EntryProfile = EntryAttr.getValueAsString();
|
|
Triple T("", "", "", EntryProfile);
|
|
ShaderKind = T.getEnvironment();
|
|
}
|
|
|
|
if (ShaderKind == Triple::EnvironmentType::Compute) {
|
|
auto NumThreadsStr =
|
|
F.getFnAttribute("hlsl.numthreads").getValueAsString();
|
|
SmallVector<StringRef> NumThreads;
|
|
NumThreadsStr.split(NumThreads, ',');
|
|
assert(NumThreads.size() == 3 && "invalid numthreads");
|
|
auto Zip =
|
|
llvm::zip(NumThreads, MutableArrayRef<unsigned>(CS.NumThreads));
|
|
for (auto It : Zip) {
|
|
StringRef Str = std::get<0>(It);
|
|
APInt V;
|
|
[[maybe_unused]] bool Result = Str.getAsInteger(10, V);
|
|
assert(!Result && "Failed to parse numthreads");
|
|
|
|
unsigned &Num = std::get<1>(It);
|
|
Num = V.getLimitedValue();
|
|
}
|
|
}
|
|
}
|
|
|
|
MDTuple *emitDXILEntryProps(uint64_t RawShaderFlag, LLVMContext &Ctx,
|
|
bool IsLib) {
|
|
std::vector<Metadata *> MDVals;
|
|
|
|
if (RawShaderFlag != 0)
|
|
appendShaderFlags(MDVals, RawShaderFlag, Ctx);
|
|
|
|
// Add shader kind for lib entrys.
|
|
if (IsLib && ShaderKind != Triple::EnvironmentType::Library)
|
|
appendShaderKind(MDVals, Ctx);
|
|
|
|
if (ShaderKind == Triple::EnvironmentType::Compute)
|
|
appendNumThreads(MDVals, Ctx);
|
|
// FIXME: support more props.
|
|
// See https://github.com/llvm/llvm-project/issues/57948.
|
|
return MDNode::get(Ctx, MDVals);
|
|
}
|
|
|
|
static MDTuple *emitEntryPropsForEmptyEntry(uint64_t RawShaderFlag,
|
|
LLVMContext &Ctx) {
|
|
if (RawShaderFlag == 0)
|
|
return nullptr;
|
|
|
|
std::vector<Metadata *> MDVals;
|
|
|
|
appendShaderFlags(MDVals, RawShaderFlag, Ctx);
|
|
// FIXME: support more props.
|
|
// See https://github.com/llvm/llvm-project/issues/57948.
|
|
return MDNode::get(Ctx, MDVals);
|
|
}
|
|
|
|
private:
|
|
enum EntryPropsTag {
|
|
ShaderFlagsTag = 0,
|
|
GSStateTag,
|
|
DSStateTag,
|
|
HSStateTag,
|
|
NumThreadsTag,
|
|
AutoBindingSpaceTag,
|
|
RayPayloadSizeTag,
|
|
RayAttribSizeTag,
|
|
ShaderKindTag,
|
|
MSStateTag,
|
|
ASStateTag,
|
|
WaveSizeTag,
|
|
EntryRootSigTag,
|
|
};
|
|
|
|
void appendNumThreads(std::vector<Metadata *> &MDVals, LLVMContext &Ctx) {
|
|
MDVals.emplace_back(ConstantAsMetadata::get(
|
|
ConstantInt::get(Type::getInt32Ty(Ctx), NumThreadsTag)));
|
|
|
|
std::vector<Metadata *> NumThreadVals;
|
|
for (auto Num : ArrayRef<unsigned>(CS.NumThreads))
|
|
NumThreadVals.emplace_back(ConstantAsMetadata::get(
|
|
ConstantInt::get(Type::getInt32Ty(Ctx), Num)));
|
|
MDVals.emplace_back(MDNode::get(Ctx, NumThreadVals));
|
|
}
|
|
|
|
static void appendShaderFlags(std::vector<Metadata *> &MDVals,
|
|
uint64_t RawShaderFlag, LLVMContext &Ctx) {
|
|
MDVals.emplace_back(ConstantAsMetadata::get(
|
|
ConstantInt::get(Type::getInt32Ty(Ctx), ShaderFlagsTag)));
|
|
MDVals.emplace_back(ConstantAsMetadata::get(
|
|
ConstantInt::get(Type::getInt64Ty(Ctx), RawShaderFlag)));
|
|
}
|
|
|
|
void appendShaderKind(std::vector<Metadata *> &MDVals, LLVMContext &Ctx) {
|
|
MDVals.emplace_back(ConstantAsMetadata::get(
|
|
ConstantInt::get(Type::getInt32Ty(Ctx), ShaderKindTag)));
|
|
MDVals.emplace_back(ConstantAsMetadata::get(
|
|
ConstantInt::get(Type::getInt32Ty(Ctx), getShaderStage(ShaderKind))));
|
|
}
|
|
};
|
|
|
|
class EntryMD {
|
|
Function &F;
|
|
LLVMContext &Ctx;
|
|
EntryProps Props;
|
|
|
|
public:
|
|
EntryMD(Function &F, Triple::EnvironmentType ModuleShaderKind)
|
|
: F(F), Ctx(F.getContext()), Props(F, ModuleShaderKind) {}
|
|
|
|
MDTuple *emitEntryTuple(MDTuple *Resources, uint64_t RawShaderFlag) {
|
|
// FIXME: add signature for profile other than CS.
|
|
// See https://github.com/llvm/llvm-project/issues/57928.
|
|
MDTuple *Signatures = nullptr;
|
|
return emitDxilEntryPointTuple(
|
|
&F, F.getName().str(), Signatures, Resources,
|
|
Props.emitDXILEntryProps(RawShaderFlag, Ctx, /*IsLib*/ false), Ctx);
|
|
}
|
|
|
|
MDTuple *emitEntryTupleForLib(uint64_t RawShaderFlag) {
|
|
// FIXME: add signature for profile other than CS.
|
|
// See https://github.com/llvm/llvm-project/issues/57928.
|
|
MDTuple *Signatures = nullptr;
|
|
return emitDxilEntryPointTuple(
|
|
&F, F.getName().str(), Signatures,
|
|
/*entry in lib doesn't need resources metadata*/ nullptr,
|
|
Props.emitDXILEntryProps(RawShaderFlag, Ctx, /*IsLib*/ true), Ctx);
|
|
}
|
|
|
|
// Library will have empty entry metadata which only store the resource table
|
|
// metadata.
|
|
static MDTuple *emitEmptyEntryForLib(MDTuple *Resources,
|
|
uint64_t RawShaderFlag,
|
|
LLVMContext &Ctx) {
|
|
return emitDxilEntryPointTuple(
|
|
nullptr, "", nullptr, Resources,
|
|
EntryProps::emitEntryPropsForEmptyEntry(RawShaderFlag, Ctx), Ctx);
|
|
}
|
|
|
|
private:
|
|
static MDTuple *emitDxilEntryPointTuple(Function *Fn, const std::string &Name,
|
|
MDTuple *Signatures,
|
|
MDTuple *Resources,
|
|
MDTuple *Properties,
|
|
LLVMContext &Ctx) {
|
|
Metadata *MDVals[5];
|
|
MDVals[0] = Fn ? ValueAsMetadata::get(Fn) : nullptr;
|
|
MDVals[1] = MDString::get(Ctx, Name.c_str());
|
|
MDVals[2] = Signatures;
|
|
MDVals[3] = Resources;
|
|
MDVals[4] = Properties;
|
|
return MDNode::get(Ctx, MDVals);
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
void dxil::createEntryMD(Module &M, const uint64_t ShaderFlags) {
|
|
SmallVector<Function *> EntryList;
|
|
for (auto &F : M.functions()) {
|
|
if (!F.hasFnAttribute("hlsl.shader"))
|
|
continue;
|
|
EntryList.emplace_back(&F);
|
|
}
|
|
|
|
auto &Ctx = M.getContext();
|
|
// FIXME: generate metadata for resource.
|
|
// See https://github.com/llvm/llvm-project/issues/57926.
|
|
MDTuple *MDResources = nullptr;
|
|
if (auto *NamedResources = M.getNamedMetadata("dx.resources"))
|
|
MDResources = dyn_cast<MDTuple>(NamedResources->getOperand(0));
|
|
|
|
std::vector<MDNode *> Entries;
|
|
Triple T = Triple(M.getTargetTriple());
|
|
switch (T.getEnvironment()) {
|
|
case Triple::EnvironmentType::Library: {
|
|
// Add empty entry to put resource metadata.
|
|
MDTuple *EmptyEntry =
|
|
EntryMD::emitEmptyEntryForLib(MDResources, ShaderFlags, Ctx);
|
|
Entries.emplace_back(EmptyEntry);
|
|
|
|
for (Function *Entry : EntryList) {
|
|
EntryMD MD(*Entry, T.getEnvironment());
|
|
Entries.emplace_back(MD.emitEntryTupleForLib(0));
|
|
}
|
|
} break;
|
|
case Triple::EnvironmentType::Compute:
|
|
case Triple::EnvironmentType::Amplification:
|
|
case Triple::EnvironmentType::Mesh:
|
|
case Triple::EnvironmentType::Vertex:
|
|
case Triple::EnvironmentType::Hull:
|
|
case Triple::EnvironmentType::Domain:
|
|
case Triple::EnvironmentType::Geometry:
|
|
case Triple::EnvironmentType::Pixel: {
|
|
assert(EntryList.size() == 1 &&
|
|
"non-lib profiles should only have one entry");
|
|
EntryMD MD(*EntryList.front(), T.getEnvironment());
|
|
Entries.emplace_back(MD.emitEntryTuple(MDResources, ShaderFlags));
|
|
} break;
|
|
default:
|
|
assert(0 && "invalid profile");
|
|
break;
|
|
}
|
|
|
|
NamedMDNode *EntryPointsNamedMD =
|
|
M.getOrInsertNamedMetadata("dx.entryPoints");
|
|
for (auto *Entry : Entries)
|
|
EntryPointsNamedMD->addOperand(Entry);
|
|
}
|