#include "DXILMetadata.h"
#include "llvm/ADT/Triple.h"
+#include "llvm/IR/Constants.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
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)));
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);
+}