Largely a straight-forward replacement with occasional simplifcations. For AMDGPU, I assumed that unconditional branches are always uniform and therefore "simplified"/changed AMDGPUAnnotateUniformValues to only annotate conditional branches. Target-specific FastISel only selects conditional branches, unconditional branches are already handled by the non-target-specific code.
648 lines
24 KiB
C++
648 lines
24 KiB
C++
//===- DXILTranslateMetadata.cpp - Pass to emit DXIL metadata -------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "DXILTranslateMetadata.h"
|
|
#include "DXILRootSignature.h"
|
|
#include "DXILShaderFlags.h"
|
|
#include "DirectX.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/Analysis/DXILMetadataAnalysis.h"
|
|
#include "llvm/Analysis/DXILResource.h"
|
|
#include "llvm/IR/BasicBlock.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/DiagnosticInfo.h"
|
|
#include "llvm/IR/DiagnosticPrinter.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/IRBuilder.h"
|
|
#include "llvm/IR/LLVMContext.h"
|
|
#include "llvm/IR/MDBuilder.h"
|
|
#include "llvm/IR/Metadata.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/InitializePasses.h"
|
|
#include "llvm/Pass.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/VersionTuple.h"
|
|
#include "llvm/TargetParser/Triple.h"
|
|
#include <cstdint>
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::dxil;
|
|
|
|
namespace {
|
|
|
|
/// A simple wrapper of DiagnosticInfo that generates module-level diagnostic
|
|
/// for the DXILValidateMetadata pass
|
|
class DiagnosticInfoValidateMD : public DiagnosticInfo {
|
|
private:
|
|
const Twine &Msg;
|
|
const Module &Mod;
|
|
|
|
public:
|
|
/// \p M is the module for which the diagnostic is being emitted. \p Msg is
|
|
/// the message to show. Note that this class does not copy this message, so
|
|
/// this reference must be valid for the whole life time of the diagnostic.
|
|
DiagnosticInfoValidateMD(const Module &M,
|
|
const Twine &Msg LLVM_LIFETIME_BOUND,
|
|
DiagnosticSeverity Severity = DS_Error)
|
|
: DiagnosticInfo(DK_Unsupported, Severity), Msg(Msg), Mod(M) {}
|
|
|
|
void print(DiagnosticPrinter &DP) const override {
|
|
DP << Mod.getName() << ": " << Msg << '\n';
|
|
}
|
|
};
|
|
|
|
static void reportError(Module &M, Twine Message,
|
|
DiagnosticSeverity Severity = DS_Error) {
|
|
M.getContext().diagnose(DiagnosticInfoValidateMD(M, Message, Severity));
|
|
}
|
|
|
|
static void reportLoopError(Module &M, Twine Message,
|
|
DiagnosticSeverity Severity = DS_Error) {
|
|
reportError(M, Twine("Invalid \"llvm.loop\" metadata: ") + Message, Severity);
|
|
}
|
|
|
|
enum class EntryPropsTag {
|
|
ShaderFlags = 0,
|
|
GSState,
|
|
DSState,
|
|
HSState,
|
|
NumThreads,
|
|
AutoBindingSpace,
|
|
RayPayloadSize,
|
|
RayAttribSize,
|
|
ShaderKind,
|
|
MSState,
|
|
ASStateTag,
|
|
WaveSize,
|
|
EntryRootSig,
|
|
WaveRange = 23,
|
|
};
|
|
|
|
} // namespace
|
|
|
|
static NamedMDNode *emitResourceMetadata(Module &M, DXILResourceMap &DRM,
|
|
DXILResourceTypeMap &DRTM) {
|
|
LLVMContext &Context = M.getContext();
|
|
|
|
for (ResourceInfo &RI : DRM)
|
|
if (!RI.hasSymbol())
|
|
RI.createSymbol(M,
|
|
DRTM[RI.getHandleTy()].createElementStruct(RI.getName()));
|
|
|
|
SmallVector<Metadata *> SRVs, UAVs, CBufs, Smps;
|
|
for (const ResourceInfo &RI : DRM.srvs())
|
|
SRVs.push_back(RI.getAsMetadata(M, DRTM[RI.getHandleTy()]));
|
|
for (const ResourceInfo &RI : DRM.uavs())
|
|
UAVs.push_back(RI.getAsMetadata(M, DRTM[RI.getHandleTy()]));
|
|
for (const ResourceInfo &RI : DRM.cbuffers())
|
|
CBufs.push_back(RI.getAsMetadata(M, DRTM[RI.getHandleTy()]));
|
|
for (const ResourceInfo &RI : DRM.samplers())
|
|
Smps.push_back(RI.getAsMetadata(M, DRTM[RI.getHandleTy()]));
|
|
|
|
Metadata *SRVMD = SRVs.empty() ? nullptr : MDNode::get(Context, SRVs);
|
|
Metadata *UAVMD = UAVs.empty() ? nullptr : MDNode::get(Context, UAVs);
|
|
Metadata *CBufMD = CBufs.empty() ? nullptr : MDNode::get(Context, CBufs);
|
|
Metadata *SmpMD = Smps.empty() ? nullptr : MDNode::get(Context, Smps);
|
|
|
|
if (DRM.empty())
|
|
return nullptr;
|
|
|
|
NamedMDNode *ResourceMD = M.getOrInsertNamedMetadata("dx.resources");
|
|
ResourceMD->addOperand(
|
|
MDNode::get(M.getContext(), {SRVMD, UAVMD, CBufMD, SmpMD}));
|
|
|
|
return ResourceMD;
|
|
}
|
|
|
|
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";
|
|
case Triple::RootSignature:
|
|
return "rootsig";
|
|
default:
|
|
break;
|
|
}
|
|
llvm_unreachable("Unsupported environment for DXIL generation.");
|
|
}
|
|
|
|
static uint32_t getShaderStage(Triple::EnvironmentType Env) {
|
|
return (uint32_t)Env - (uint32_t)llvm::Triple::Pixel;
|
|
}
|
|
|
|
static SmallVector<Metadata *>
|
|
getTagValueAsMetadata(EntryPropsTag Tag, uint64_t Value, LLVMContext &Ctx) {
|
|
SmallVector<Metadata *> MDVals;
|
|
MDVals.emplace_back(ConstantAsMetadata::get(
|
|
ConstantInt::get(Type::getInt32Ty(Ctx), static_cast<int>(Tag))));
|
|
switch (Tag) {
|
|
case EntryPropsTag::ShaderFlags:
|
|
MDVals.emplace_back(ConstantAsMetadata::get(
|
|
ConstantInt::get(Type::getInt64Ty(Ctx), Value)));
|
|
break;
|
|
case EntryPropsTag::ShaderKind:
|
|
MDVals.emplace_back(ConstantAsMetadata::get(
|
|
ConstantInt::get(Type::getInt32Ty(Ctx), Value)));
|
|
break;
|
|
case EntryPropsTag::GSState:
|
|
case EntryPropsTag::DSState:
|
|
case EntryPropsTag::HSState:
|
|
case EntryPropsTag::NumThreads:
|
|
case EntryPropsTag::AutoBindingSpace:
|
|
case EntryPropsTag::RayPayloadSize:
|
|
case EntryPropsTag::RayAttribSize:
|
|
case EntryPropsTag::MSState:
|
|
case EntryPropsTag::ASStateTag:
|
|
case EntryPropsTag::WaveSize:
|
|
case EntryPropsTag::EntryRootSig:
|
|
case EntryPropsTag::WaveRange:
|
|
llvm_unreachable("NYI: Unhandled entry property tag");
|
|
}
|
|
return MDVals;
|
|
}
|
|
|
|
static MDTuple *getEntryPropAsMetadata(Module &M, const EntryProperties &EP,
|
|
uint64_t EntryShaderFlags,
|
|
const ModuleMetadataInfo &MMDI) {
|
|
SmallVector<Metadata *> MDVals;
|
|
LLVMContext &Ctx = EP.Entry->getContext();
|
|
if (EntryShaderFlags != 0)
|
|
MDVals.append(getTagValueAsMetadata(EntryPropsTag::ShaderFlags,
|
|
EntryShaderFlags, Ctx));
|
|
|
|
if (EP.Entry != nullptr) {
|
|
// FIXME: support more props.
|
|
// See https://github.com/llvm/llvm-project/issues/57948.
|
|
// Add shader kind for lib entries.
|
|
if (MMDI.ShaderProfile == Triple::EnvironmentType::Library &&
|
|
EP.ShaderStage != Triple::EnvironmentType::Library)
|
|
MDVals.append(getTagValueAsMetadata(EntryPropsTag::ShaderKind,
|
|
getShaderStage(EP.ShaderStage), Ctx));
|
|
|
|
if (EP.ShaderStage == Triple::EnvironmentType::Compute) {
|
|
// Handle mandatory "hlsl.numthreads"
|
|
MDVals.emplace_back(ConstantAsMetadata::get(ConstantInt::get(
|
|
Type::getInt32Ty(Ctx), static_cast<int>(EntryPropsTag::NumThreads))));
|
|
Metadata *NumThreadVals[] = {ConstantAsMetadata::get(ConstantInt::get(
|
|
Type::getInt32Ty(Ctx), EP.NumThreadsX)),
|
|
ConstantAsMetadata::get(ConstantInt::get(
|
|
Type::getInt32Ty(Ctx), EP.NumThreadsY)),
|
|
ConstantAsMetadata::get(ConstantInt::get(
|
|
Type::getInt32Ty(Ctx), EP.NumThreadsZ))};
|
|
MDVals.emplace_back(MDNode::get(Ctx, NumThreadVals));
|
|
|
|
// Handle optional "hlsl.wavesize". The fields are optionally represented
|
|
// if they are non-zero.
|
|
if (EP.WaveSizeMin != 0) {
|
|
bool IsWaveRange = VersionTuple(6, 8) <= MMDI.ShaderModelVersion;
|
|
bool IsWaveSize =
|
|
!IsWaveRange && VersionTuple(6, 6) <= MMDI.ShaderModelVersion;
|
|
|
|
if (!IsWaveRange && !IsWaveSize) {
|
|
reportError(M, "Shader model 6.6 or greater is required to specify "
|
|
"the \"hlsl.wavesize\" function attribute");
|
|
return nullptr;
|
|
}
|
|
|
|
// A range is being specified if EP.WaveSizeMax != 0
|
|
if (EP.WaveSizeMax && !IsWaveRange) {
|
|
reportError(
|
|
M, "Shader model 6.8 or greater is required to specify "
|
|
"wave size range values of the \"hlsl.wavesize\" function "
|
|
"attribute");
|
|
return nullptr;
|
|
}
|
|
|
|
EntryPropsTag Tag =
|
|
IsWaveSize ? EntryPropsTag::WaveSize : EntryPropsTag::WaveRange;
|
|
MDVals.emplace_back(ConstantAsMetadata::get(
|
|
ConstantInt::get(Type::getInt32Ty(Ctx), static_cast<int>(Tag))));
|
|
|
|
SmallVector<Metadata *> WaveSizeVals = {ConstantAsMetadata::get(
|
|
ConstantInt::get(Type::getInt32Ty(Ctx), EP.WaveSizeMin))};
|
|
if (IsWaveRange) {
|
|
WaveSizeVals.push_back(ConstantAsMetadata::get(
|
|
ConstantInt::get(Type::getInt32Ty(Ctx), EP.WaveSizeMax)));
|
|
WaveSizeVals.push_back(ConstantAsMetadata::get(
|
|
ConstantInt::get(Type::getInt32Ty(Ctx), EP.WaveSizePref)));
|
|
}
|
|
|
|
MDVals.emplace_back(MDNode::get(Ctx, WaveSizeVals));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (MDVals.empty())
|
|
return nullptr;
|
|
return MDNode::get(Ctx, MDVals);
|
|
}
|
|
|
|
static MDTuple *constructEntryMetadata(const Function *EntryFn,
|
|
MDTuple *Signatures, MDNode *Resources,
|
|
MDTuple *Properties, LLVMContext &Ctx) {
|
|
// Each entry point metadata record specifies:
|
|
// * reference to the entry point function global symbol
|
|
// * unmangled name
|
|
// * list of signatures
|
|
// * list of resources
|
|
// * list of tag-value pairs of shader capabilities and other properties
|
|
Metadata *MDVals[5];
|
|
MDVals[0] =
|
|
EntryFn ? ValueAsMetadata::get(const_cast<Function *>(EntryFn)) : nullptr;
|
|
MDVals[1] = MDString::get(Ctx, EntryFn ? EntryFn->getName() : "");
|
|
MDVals[2] = Signatures;
|
|
MDVals[3] = Resources;
|
|
MDVals[4] = Properties;
|
|
return MDNode::get(Ctx, MDVals);
|
|
}
|
|
|
|
static MDTuple *emitEntryMD(Module &M, const EntryProperties &EP,
|
|
MDTuple *Signatures, MDNode *MDResources,
|
|
const uint64_t EntryShaderFlags,
|
|
const ModuleMetadataInfo &MMDI) {
|
|
MDTuple *Properties = getEntryPropAsMetadata(M, EP, EntryShaderFlags, MMDI);
|
|
return constructEntryMetadata(EP.Entry, Signatures, MDResources, Properties,
|
|
EP.Entry->getContext());
|
|
}
|
|
|
|
static void emitValidatorVersionMD(Module &M, const ModuleMetadataInfo &MMDI) {
|
|
if (MMDI.ValidatorVersion.empty())
|
|
return;
|
|
|
|
LLVMContext &Ctx = M.getContext();
|
|
IRBuilder<> IRB(Ctx);
|
|
Metadata *MDVals[2];
|
|
MDVals[0] =
|
|
ConstantAsMetadata::get(IRB.getInt32(MMDI.ValidatorVersion.getMajor()));
|
|
MDVals[1] = ConstantAsMetadata::get(
|
|
IRB.getInt32(MMDI.ValidatorVersion.getMinor().value_or(0)));
|
|
NamedMDNode *ValVerNode = M.getOrInsertNamedMetadata("dx.valver");
|
|
// Set validator version obtained from DXIL Metadata Analysis pass
|
|
ValVerNode->clearOperands();
|
|
ValVerNode->addOperand(MDNode::get(Ctx, MDVals));
|
|
}
|
|
|
|
static void emitShaderModelVersionMD(Module &M,
|
|
const ModuleMetadataInfo &MMDI) {
|
|
LLVMContext &Ctx = M.getContext();
|
|
IRBuilder<> IRB(Ctx);
|
|
Metadata *SMVals[3];
|
|
VersionTuple SM = MMDI.ShaderModelVersion;
|
|
SMVals[0] = MDString::get(Ctx, getShortShaderStage(MMDI.ShaderProfile));
|
|
SMVals[1] = ConstantAsMetadata::get(IRB.getInt32(SM.getMajor()));
|
|
SMVals[2] = ConstantAsMetadata::get(IRB.getInt32(SM.getMinor().value_or(0)));
|
|
NamedMDNode *SMMDNode = M.getOrInsertNamedMetadata("dx.shaderModel");
|
|
SMMDNode->addOperand(MDNode::get(Ctx, SMVals));
|
|
}
|
|
|
|
static void emitDXILVersionTupleMD(Module &M, const ModuleMetadataInfo &MMDI) {
|
|
LLVMContext &Ctx = M.getContext();
|
|
IRBuilder<> IRB(Ctx);
|
|
VersionTuple DXILVer = MMDI.DXILVersion;
|
|
Metadata *DXILVals[2];
|
|
DXILVals[0] = ConstantAsMetadata::get(IRB.getInt32(DXILVer.getMajor()));
|
|
DXILVals[1] =
|
|
ConstantAsMetadata::get(IRB.getInt32(DXILVer.getMinor().value_or(0)));
|
|
NamedMDNode *DXILVerMDNode = M.getOrInsertNamedMetadata("dx.version");
|
|
DXILVerMDNode->addOperand(MDNode::get(Ctx, DXILVals));
|
|
}
|
|
|
|
static MDTuple *emitTopLevelLibraryNode(Module &M, MDNode *RMD,
|
|
uint64_t ShaderFlags) {
|
|
LLVMContext &Ctx = M.getContext();
|
|
MDTuple *Properties = nullptr;
|
|
if (ShaderFlags != 0) {
|
|
SmallVector<Metadata *> MDVals;
|
|
MDVals.append(
|
|
getTagValueAsMetadata(EntryPropsTag::ShaderFlags, ShaderFlags, Ctx));
|
|
Properties = MDNode::get(Ctx, MDVals);
|
|
}
|
|
// Library has an entry metadata with resource table metadata and all other
|
|
// MDNodes as null.
|
|
return constructEntryMetadata(nullptr, nullptr, RMD, Properties, Ctx);
|
|
}
|
|
|
|
static void translateBranchMetadata(Module &M, Instruction *BBTerminatorInst) {
|
|
MDNode *HlslControlFlowMD =
|
|
BBTerminatorInst->getMetadata("hlsl.controlflow.hint");
|
|
|
|
if (!HlslControlFlowMD)
|
|
return;
|
|
|
|
assert(HlslControlFlowMD->getNumOperands() == 2 &&
|
|
"invalid operands for hlsl.controlflow.hint");
|
|
|
|
MDBuilder MDHelper(M.getContext());
|
|
|
|
llvm::Metadata *HintsStr = MDHelper.createString("dx.controlflow.hints");
|
|
llvm::Metadata *HintsValue = MDHelper.createConstant(
|
|
mdconst::extract<ConstantInt>(HlslControlFlowMD->getOperand(1)));
|
|
|
|
MDNode *MDNode = llvm::MDNode::get(M.getContext(), {HintsStr, HintsValue});
|
|
|
|
BBTerminatorInst->setMetadata("dx.controlflow.hints", MDNode);
|
|
BBTerminatorInst->setMetadata("hlsl.controlflow.hint", nullptr);
|
|
}
|
|
|
|
// Determines if the metadata node will be compatible with DXIL's loop metadata
|
|
// representation.
|
|
//
|
|
// Reports an error for compatible metadata that is ill-formed.
|
|
static bool isLoopMDCompatible(Module &M, Metadata *MD) {
|
|
// DXIL only accepts the following loop hints:
|
|
std::array<StringLiteral, 3> ValidHintNames = {"llvm.loop.unroll.count",
|
|
"llvm.loop.unroll.disable",
|
|
"llvm.loop.unroll.full"};
|
|
|
|
MDNode *HintMD = dyn_cast<MDNode>(MD);
|
|
if (!HintMD || HintMD->getNumOperands() == 0)
|
|
return false;
|
|
|
|
auto *HintStr = dyn_cast<MDString>(HintMD->getOperand(0));
|
|
if (!HintStr)
|
|
return false;
|
|
|
|
if (!llvm::is_contained(ValidHintNames, HintStr->getString()))
|
|
return false;
|
|
|
|
auto ValidCountNode = [](MDNode *CountMD) -> bool {
|
|
if (CountMD->getNumOperands() == 2)
|
|
if (auto *Count = dyn_cast<ConstantAsMetadata>(CountMD->getOperand(1)))
|
|
if (isa<ConstantInt>(Count->getValue()))
|
|
return true;
|
|
return false;
|
|
};
|
|
|
|
if (HintStr->getString() == "llvm.loop.unroll.count") {
|
|
if (!ValidCountNode(HintMD)) {
|
|
reportLoopError(M, "\"llvm.loop.unroll.count\" must have 2 operands and "
|
|
"the second must be a constant integer");
|
|
return false;
|
|
}
|
|
} else if (HintMD->getNumOperands() != 1) {
|
|
reportLoopError(
|
|
M, "\"llvm.loop.unroll.disable\" and \"llvm.loop.unroll.full\" "
|
|
"must be provided as a single operand");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void translateLoopMetadata(Module &M, Instruction *I, MDNode *BaseMD) {
|
|
// A distinct node has the self-referential form: !0 = !{ !0, ... }
|
|
auto IsDistinctNode = [](MDNode *Node) -> bool {
|
|
return Node && Node->getNumOperands() != 0 && Node == Node->getOperand(0);
|
|
};
|
|
|
|
// Set metadata to null to remove empty/ill-formed metadata from instruction
|
|
if (BaseMD->getNumOperands() == 0 || !IsDistinctNode(BaseMD))
|
|
return I->setMetadata("llvm.loop", nullptr);
|
|
|
|
// It is valid to have a chain of self-refential loop metadata nodes, as
|
|
// below. We will collapse these into just one when we reconstruct the
|
|
// metadata.
|
|
//
|
|
// Eg:
|
|
// !0 = !{!0, !1}
|
|
// !1 = !{!1, !2}
|
|
// !2 = !{!"llvm.loop.unroll.disable"}
|
|
//
|
|
// So, traverse down a potential self-referential chain
|
|
while (1 < BaseMD->getNumOperands() &&
|
|
IsDistinctNode(dyn_cast<MDNode>(BaseMD->getOperand(1))))
|
|
BaseMD = dyn_cast<MDNode>(BaseMD->getOperand(1));
|
|
|
|
// To reconstruct a distinct node we create a temporary node that we will
|
|
// then update to create a self-reference.
|
|
llvm::TempMDTuple TempNode = llvm::MDNode::getTemporary(M.getContext(), {});
|
|
SmallVector<Metadata *> CompatibleOperands = {TempNode.get()};
|
|
|
|
// Iterate and reconstruct the metadata nodes that contains any hints,
|
|
// stripping any unrecognized metadata.
|
|
ArrayRef<MDOperand> Operands = BaseMD->operands();
|
|
for (auto &Op : Operands.drop_front())
|
|
if (isLoopMDCompatible(M, Op.get()))
|
|
CompatibleOperands.push_back(Op.get());
|
|
|
|
if (2 < CompatibleOperands.size())
|
|
reportLoopError(M, "Provided conflicting hints");
|
|
|
|
MDNode *CompatibleLoopMD = MDNode::get(M.getContext(), CompatibleOperands);
|
|
TempNode->replaceAllUsesWith(CompatibleLoopMD);
|
|
|
|
I->setMetadata("llvm.loop", CompatibleLoopMD);
|
|
}
|
|
|
|
using InstructionMDList = std::array<unsigned, 7>;
|
|
|
|
static InstructionMDList getCompatibleInstructionMDs(llvm::Module &M) {
|
|
return {
|
|
M.getMDKindID("dx.nonuniform"), M.getMDKindID("dx.controlflow.hints"),
|
|
M.getMDKindID("dx.precise"), llvm::LLVMContext::MD_range,
|
|
llvm::LLVMContext::MD_alias_scope, llvm::LLVMContext::MD_noalias,
|
|
M.getMDKindID("llvm.loop")};
|
|
}
|
|
|
|
static void translateInstructionMetadata(Module &M) {
|
|
// construct allowlist of valid metadata node kinds
|
|
InstructionMDList DXILCompatibleMDs = getCompatibleInstructionMDs(M);
|
|
unsigned char MDLoopKind = M.getContext().getMDKindID("llvm.loop");
|
|
|
|
for (Function &F : M) {
|
|
for (BasicBlock &BB : F) {
|
|
// This needs to be done first so that "hlsl.controlflow.hints" isn't
|
|
// removed in the allow-list below
|
|
if (auto *I = BB.getTerminator())
|
|
translateBranchMetadata(M, I);
|
|
|
|
for (auto &I : make_early_inc_range(BB)) {
|
|
if (isa<UncondBrInst, CondBrInst>(I))
|
|
if (MDNode *LoopMD = I.getMetadata(MDLoopKind))
|
|
translateLoopMetadata(M, &I, LoopMD);
|
|
I.dropUnknownNonDebugMetadata(DXILCompatibleMDs);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void cleanModuleFlags(Module &M) {
|
|
NamedMDNode *MDFlags = M.getModuleFlagsMetadata();
|
|
if (!MDFlags)
|
|
return;
|
|
|
|
SmallVector<llvm::Module::ModuleFlagEntry> FlagEntries;
|
|
M.getModuleFlagsMetadata(FlagEntries);
|
|
bool Updated = false;
|
|
for (auto &Flag : FlagEntries) {
|
|
// llvm 3.7 only supports behavior up to AppendUnique.
|
|
if (Flag.Behavior <= Module::ModFlagBehavior::AppendUnique)
|
|
continue;
|
|
Flag.Behavior = Module::ModFlagBehavior::Warning;
|
|
Updated = true;
|
|
}
|
|
|
|
if (!Updated)
|
|
return;
|
|
|
|
MDFlags->eraseFromParent();
|
|
|
|
for (auto &Flag : FlagEntries)
|
|
M.addModuleFlag(Flag.Behavior, Flag.Key->getString(), Flag.Val);
|
|
}
|
|
|
|
using GlobalMDList = std::array<StringLiteral, 7>;
|
|
|
|
// The following are compatible with DXIL but not emit with clang, they can
|
|
// be added when applicable:
|
|
// dx.typeAnnotations, dx.viewIDState, dx.dxrPayloadAnnotations
|
|
static GlobalMDList CompatibleNamedModuleMDs = {
|
|
"llvm.ident", "llvm.module.flags", "dx.resources", "dx.valver",
|
|
"dx.shaderModel", "dx.version", "dx.entryPoints",
|
|
};
|
|
|
|
static void translateGlobalMetadata(Module &M, DXILResourceMap &DRM,
|
|
DXILResourceTypeMap &DRTM,
|
|
const ModuleShaderFlags &ShaderFlags,
|
|
const ModuleMetadataInfo &MMDI) {
|
|
LLVMContext &Ctx = M.getContext();
|
|
IRBuilder<> IRB(Ctx);
|
|
SmallVector<MDNode *> EntryFnMDNodes;
|
|
|
|
emitValidatorVersionMD(M, MMDI);
|
|
emitShaderModelVersionMD(M, MMDI);
|
|
emitDXILVersionTupleMD(M, MMDI);
|
|
NamedMDNode *NamedResourceMD = emitResourceMetadata(M, DRM, DRTM);
|
|
auto *ResourceMD =
|
|
(NamedResourceMD != nullptr) ? NamedResourceMD->getOperand(0) : nullptr;
|
|
// FIXME: Add support to construct Signatures
|
|
// See https://github.com/llvm/llvm-project/issues/57928
|
|
MDTuple *Signatures = nullptr;
|
|
|
|
if (MMDI.ShaderProfile == Triple::EnvironmentType::Library) {
|
|
// Get the combined shader flag mask of all functions in the library to be
|
|
// used as shader flags mask value associated with top-level library entry
|
|
// metadata.
|
|
uint64_t CombinedMask = ShaderFlags.getCombinedFlags();
|
|
EntryFnMDNodes.emplace_back(
|
|
emitTopLevelLibraryNode(M, ResourceMD, CombinedMask));
|
|
} else if (1 < MMDI.EntryPropertyVec.size())
|
|
reportError(M, "Non-library shader: One and only one entry expected");
|
|
|
|
for (const EntryProperties &EntryProp : MMDI.EntryPropertyVec) {
|
|
uint64_t EntryShaderFlags = 0;
|
|
if (MMDI.ShaderProfile != Triple::EnvironmentType::Library) {
|
|
EntryShaderFlags = ShaderFlags.getFunctionFlags(EntryProp.Entry);
|
|
if (EntryProp.ShaderStage != MMDI.ShaderProfile)
|
|
reportError(
|
|
M, "Shader stage '" +
|
|
Twine(getShortShaderStage(EntryProp.ShaderStage)) +
|
|
"' for entry '" + Twine(EntryProp.Entry->getName()) +
|
|
"' different from specified target profile '" +
|
|
Twine(Triple::getEnvironmentTypeName(MMDI.ShaderProfile) +
|
|
"'"));
|
|
}
|
|
EntryFnMDNodes.emplace_back(emitEntryMD(
|
|
M, EntryProp, Signatures, ResourceMD, EntryShaderFlags, MMDI));
|
|
}
|
|
|
|
NamedMDNode *EntryPointsNamedMD =
|
|
M.getOrInsertNamedMetadata("dx.entryPoints");
|
|
for (auto *Entry : EntryFnMDNodes)
|
|
EntryPointsNamedMD->addOperand(Entry);
|
|
|
|
cleanModuleFlags(M);
|
|
|
|
// Finally, strip all module metadata that is not explicitly specified in the
|
|
// allow-list
|
|
SmallVector<NamedMDNode *> ToStrip;
|
|
|
|
for (NamedMDNode &NamedMD : M.named_metadata())
|
|
if (!NamedMD.getName().starts_with("llvm.dbg.") &&
|
|
!llvm::is_contained(CompatibleNamedModuleMDs, NamedMD.getName()))
|
|
ToStrip.push_back(&NamedMD);
|
|
|
|
for (NamedMDNode *NamedMD : ToStrip)
|
|
NamedMD->eraseFromParent();
|
|
}
|
|
|
|
PreservedAnalyses DXILTranslateMetadata::run(Module &M,
|
|
ModuleAnalysisManager &MAM) {
|
|
DXILResourceMap &DRM = MAM.getResult<DXILResourceAnalysis>(M);
|
|
DXILResourceTypeMap &DRTM = MAM.getResult<DXILResourceTypeAnalysis>(M);
|
|
const ModuleShaderFlags &ShaderFlags = MAM.getResult<ShaderFlagsAnalysis>(M);
|
|
const dxil::ModuleMetadataInfo MMDI = MAM.getResult<DXILMetadataAnalysis>(M);
|
|
|
|
translateGlobalMetadata(M, DRM, DRTM, ShaderFlags, MMDI);
|
|
translateInstructionMetadata(M);
|
|
|
|
return PreservedAnalyses::all();
|
|
}
|
|
|
|
void DXILTranslateMetadataLegacy::getAnalysisUsage(AnalysisUsage &AU) const {
|
|
AU.addRequired<DXILResourceTypeWrapperPass>();
|
|
AU.addRequired<DXILResourceWrapperPass>();
|
|
AU.addRequired<ShaderFlagsAnalysisWrapper>();
|
|
AU.addRequired<DXILMetadataAnalysisWrapperPass>();
|
|
AU.addRequired<RootSignatureAnalysisWrapper>();
|
|
|
|
AU.addPreserved<DXILMetadataAnalysisWrapperPass>();
|
|
AU.addPreserved<DXILResourceBindingWrapperPass>();
|
|
AU.addPreserved<DXILResourceWrapperPass>();
|
|
AU.addPreserved<RootSignatureAnalysisWrapper>();
|
|
AU.addPreserved<ShaderFlagsAnalysisWrapper>();
|
|
}
|
|
|
|
bool DXILTranslateMetadataLegacy::runOnModule(Module &M) {
|
|
DXILResourceMap &DRM =
|
|
getAnalysis<DXILResourceWrapperPass>().getResourceMap();
|
|
DXILResourceTypeMap &DRTM =
|
|
getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
|
|
const ModuleShaderFlags &ShaderFlags =
|
|
getAnalysis<ShaderFlagsAnalysisWrapper>().getShaderFlags();
|
|
dxil::ModuleMetadataInfo MMDI =
|
|
getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
|
|
|
|
translateGlobalMetadata(M, DRM, DRTM, ShaderFlags, MMDI);
|
|
translateInstructionMetadata(M);
|
|
return true;
|
|
}
|
|
|
|
char DXILTranslateMetadataLegacy::ID = 0;
|
|
|
|
ModulePass *llvm::createDXILTranslateMetadataLegacyPass() {
|
|
return new DXILTranslateMetadataLegacy();
|
|
}
|
|
|
|
INITIALIZE_PASS_BEGIN(DXILTranslateMetadataLegacy, "dxil-translate-metadata",
|
|
"DXIL Translate Metadata", false, false)
|
|
INITIALIZE_PASS_DEPENDENCY(DXILResourceWrapperPass)
|
|
INITIALIZE_PASS_DEPENDENCY(ShaderFlagsAnalysisWrapper)
|
|
INITIALIZE_PASS_DEPENDENCY(RootSignatureAnalysisWrapper)
|
|
INITIALIZE_PASS_DEPENDENCY(DXILMetadataAnalysisWrapperPass)
|
|
INITIALIZE_PASS_END(DXILTranslateMetadataLegacy, "dxil-translate-metadata",
|
|
"DXIL Translate Metadata", false, false)
|