[DirectX] Add DXILDebugInfo pass (#191254)

The pass is enabled for DXILBitcodeWriter and ValueEnumerator to
transform LLVM IR and provide data required to lower it to DXIL.

There are 3 ways how DXILDebugInfo pass can change lowering to DXIL:

1. Transform LLVM IR directly in DXILDebugInfoPass::run. This works well
when the target IR does not break any invariants of LLVM IR.

2. Add extra metadata for ValueEnumerator to process with MDExtra map.
When ValueEnumerator enumerates a Key of MDExtra, it also follows the
Value and all its operands.

3. Replace a metadata for ValueEnumerator with a another metadata. When
ValueEnumerator enumerates a Key of MDReplace, or when the writer uses
getMetadataID, the corresponding value of MDReplace map is returned
instead.

Co-authored-by: Andrew Savonichev <andrew.savonichev@gmail.com>
This commit is contained in:
Harald van Dijk
2026-04-27 18:16:43 +01:00
committed by GitHub
parent 71d63d6107
commit fb4ce472ad
11 changed files with 131 additions and 20 deletions

View File

@@ -49,7 +49,7 @@ add_llvm_target(DirectXCodeGen
DXILBitWriter
DirectXDesc
DirectXInfo
DirectXPointerTypeAnalysis
DirectXIRPasses
FrontendHLSL
IPO
MC

View File

@@ -12,7 +12,7 @@ add_llvm_component_library(LLVMDXILBitWriter
Analysis
BitWriter
Core
DirectXPointerTypeAnalysis
DirectXIRPasses
MC
Object
Support

View File

@@ -12,6 +12,7 @@
#include "DXILBitcodeWriter.h"
#include "DXILValueEnumerator.h"
#include "DirectXIRPasses/DXILDebugInfo.h"
#include "DirectXIRPasses/PointerTypeAnalysis.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/BinaryFormat/Dwarf.h"
@@ -128,16 +129,20 @@ class DXILBitcodeWriter {
/// This maps values to their typed pointers
PointerTypeMap PointerMap;
/// Tracks debug info metadata.
const DXILDebugInfoMap &DebugInfo;
public:
/// Constructs a ModuleBitcodeWriter object for the given Module,
/// writing to the provided \p Buffer.
DXILBitcodeWriter(const Module &M, SmallVectorImpl<char> &Buffer,
StringTableBuilder &StrtabBuilder, BitstreamWriter &Stream)
StringTableBuilder &StrtabBuilder, BitstreamWriter &Stream,
const DXILDebugInfoMap &DebugInfo)
: I8Ty(Type::getInt8Ty(M.getContext())),
I8PtrTy(TypedPointerType::get(I8Ty, 0)), Stream(Stream),
StrtabBuilder(StrtabBuilder), M(M), VE(M, I8PtrTy), Buffer(Buffer),
BitcodeStartBit(Stream.GetCurrentBitNo()),
PointerMap(PointerTypeAnalysis::run(M)) {
StrtabBuilder(StrtabBuilder), M(M), VE(M, I8PtrTy, DebugInfo),
Buffer(Buffer), BitcodeStartBit(Stream.GetCurrentBitNo()),
PointerMap(PointerTypeAnalysis::run(M)), DebugInfo(DebugInfo) {
GlobalValueId = VE.getValues().size();
// Enumerate the typed pointers
for (auto El : PointerMap)
@@ -393,7 +398,8 @@ dxil::BitcodeWriter::BitcodeWriter(SmallVectorImpl<char> &Buffer)
dxil::BitcodeWriter::~BitcodeWriter() { }
/// Write the specified module to the specified output stream.
void dxil::WriteDXILToFile(const Module &M, raw_ostream &Out) {
void dxil::WriteDXILToFile(const Module &M, raw_ostream &Out,
const DXILDebugInfoMap &DebugInfo) {
SmallVector<char, 0> Buffer;
Buffer.reserve(256 * 1024);
@@ -404,7 +410,7 @@ void dxil::WriteDXILToFile(const Module &M, raw_ostream &Out) {
Buffer.insert(Buffer.begin(), BWH_HeaderSize, 0);
BitcodeWriter Writer(Buffer);
Writer.writeModule(M);
Writer.writeModule(M, DebugInfo);
// Write the generated bitstream to "Out".
if (!Buffer.empty())
@@ -424,7 +430,8 @@ void BitcodeWriter::writeBlob(unsigned Block, unsigned Record, StringRef Blob) {
Stream->ExitBlock();
}
void BitcodeWriter::writeModule(const Module &M) {
void BitcodeWriter::writeModule(const Module &M,
const DXILDebugInfoMap &DebugInfo) {
// The Mods vector is used by irsymtab::build, which requires non-const
// Modules in case it needs to materialize metadata. But the bitcode writer
@@ -433,7 +440,7 @@ void BitcodeWriter::writeModule(const Module &M) {
assert(M.isMaterialized());
Mods.push_back(const_cast<Module *>(&M));
DXILBitcodeWriter ModuleWriter(M, Buffer, StrtabBuilder, *Stream);
DXILBitcodeWriter ModuleWriter(M, Buffer, StrtabBuilder, *Stream, DebugInfo);
ModuleWriter.write();
}

View File

@@ -29,6 +29,8 @@ class raw_ostream;
namespace dxil {
class DXILDebugInfoMap;
class BitcodeWriter {
SmallVectorImpl<char> &Buffer;
std::unique_ptr<BitstreamWriter> Stream;
@@ -50,14 +52,15 @@ public:
~BitcodeWriter();
/// Write the specified module to the buffer specified at construction time.
void writeModule(const Module &M);
void writeModule(const Module &M, const DXILDebugInfoMap &DebugInfo);
};
/// Write the specified module to the specified raw output stream.
///
/// For streams where it matters, the given stream should be in "binary"
/// mode.
void WriteDXILToFile(const Module &M, raw_ostream &Out);
void WriteDXILToFile(const Module &M, raw_ostream &Out,
const DXILDebugInfoMap &DebugInfo);
} // namespace dxil

View File

@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "DXILValueEnumerator.h"
#include "DirectXIRPasses/DXILDebugInfo.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/Argument.h"
@@ -361,7 +362,9 @@ static UseListOrderStack predictUseListOrder(const Module &M) {
return Stack;
}
ValueEnumerator::ValueEnumerator(const Module &M, Type *PrefixType) {
ValueEnumerator::ValueEnumerator(const Module &M, Type *PrefixType,
const DXILDebugInfoMap &DebugInfo)
: DebugInfo(DebugInfo) {
EnumerateType(PrefixType);
UseListOrders = predictUseListOrder(M);
@@ -648,6 +651,8 @@ void ValueEnumerator::dropFunctionFromMetadata(
}
void ValueEnumerator::EnumerateMetadata(unsigned F, const Metadata *MD) {
MD = getDXILMetadata(MD);
// It's vital for reader efficiency that uniqued subgraphs are done in
// post-order; it's expensive when their operands have forward references.
// If a distinct node is referenced from a uniqued node, it'll be delayed
@@ -669,7 +674,7 @@ void ValueEnumerator::EnumerateMetadata(unsigned F, const Metadata *MD) {
Worklist.back().second, N->op_end(),
[&](const Metadata *MD) { return enumerateMetadataImpl(F, MD); });
if (I != N->op_end()) {
auto *Op = cast<MDNode>(*I);
auto *Op = cast<MDNode>(getDXILMetadata(*I));
Worklist.back().second = ++I;
// Delay traversing Op if it's a distinct node and N is uniqued.
@@ -680,6 +685,15 @@ void ValueEnumerator::EnumerateMetadata(unsigned F, const Metadata *MD) {
continue;
}
if (const Metadata *ExtraMD = DebugInfo.MDExtra.lookup(N)) {
if (enumerateMetadataImpl(F, ExtraMD)) {
if (const auto *ExtraN = dyn_cast<MDNode>(ExtraMD)) {
Worklist.push_back(std::make_pair(ExtraN, ExtraN->op_begin()));
continue;
}
}
}
// All the operands have been visited. Now assign an ID.
Worklist.pop_back();
MDs.push_back(N);
@@ -697,6 +711,8 @@ void ValueEnumerator::EnumerateMetadata(unsigned F, const Metadata *MD) {
const MDNode *ValueEnumerator::enumerateMetadataImpl(unsigned F,
const Metadata *MD) {
MD = getDXILMetadata(MD);
if (!MD)
return nullptr;
@@ -867,6 +883,12 @@ void ValueEnumerator::organizeMetadata() {
FunctionMDInfo[PrevF] = R;
}
const Metadata *ValueEnumerator::getDXILMetadata(const Metadata *M) const {
if (const Metadata *Replace = DebugInfo.MDReplace.lookup(M))
return Replace;
return M;
}
void ValueEnumerator::incorporateFunctionMetadata(const Function &F) {
NumModuleMDs = MDs.size();

View File

@@ -43,6 +43,8 @@ class ValueSymbolTable;
namespace dxil {
class DXILDebugInfoMap;
class ValueEnumerator {
public:
using TypeList = std::vector<Type *>;
@@ -138,8 +140,11 @@ private:
unsigned FirstFuncConstantID;
unsigned FirstInstID;
const DXILDebugInfoMap &DebugInfo;
public:
ValueEnumerator(const Module &M, Type *PrefixType);
ValueEnumerator(const Module &M, Type *PrefixType,
const DXILDebugInfoMap &DebugInfo);
ValueEnumerator(const ValueEnumerator &) = delete;
ValueEnumerator &operator=(const ValueEnumerator &) = delete;
@@ -157,7 +162,7 @@ public:
}
unsigned getMetadataOrNullID(const Metadata *MD) const {
return MetadataMap.lookup(MD).ID;
return MetadataMap.lookup(getDXILMetadata(MD)).ID;
}
unsigned numMDs() const { return MDs.size(); }
@@ -231,6 +236,8 @@ public:
/// should only be used by rare constructs such as address-of-label.
unsigned getGlobalBasicBlockID(const BasicBlock *BB) const;
const Metadata *getDXILMetadata(const Metadata *M) const;
/// incorporateFunction/purgeFunction - If you'd like to deal with a function,
/// use these two methods to get its data into the ValueEnumerator!
void incorporateFunction(const Function &F);

View File

@@ -12,6 +12,7 @@
#include "DXILWriterPass.h"
#include "DXILBitcodeWriter.h"
#include "DirectXIRPasses/DXILDebugInfo.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
@@ -49,7 +50,8 @@ public:
StringRef getPassName() const override { return "Bitcode Writer"; }
bool runOnModule(Module &M) override {
WriteDXILToFile(M, OS);
const auto DIMap = DXILDebugInfoPass::run(M);
WriteDXILToFile(M, OS, DIMap);
return false;
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
@@ -153,7 +155,8 @@ public:
// fail the Module Verifier if performed in an earlier pass
legalizeLifetimeIntrinsics(M);
WriteDXILToFile(M, OS);
const auto DIMap = DXILDebugInfoPass::run(M);
WriteDXILToFile(M, OS, DIMap);
// We no longer need lifetime intrinsics after bitcode serialization, so we
// simply remove them to keep the Module Verifier happy after our

View File

@@ -1,4 +1,5 @@
add_llvm_component_library(LLVMDirectXPointerTypeAnalysis
add_llvm_component_library(LLVMDirectXIRPasses
DXILDebugInfo.cpp
PointerTypeAnalysis.cpp
LINK_COMPONENTS

View File

@@ -0,0 +1,24 @@
//===--- DXILDebugInfo.cpp - analysis&lowering for Debug info -*- C++ -*- ---=//
//
// 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 "DXILDebugInfo.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/Module.h"
#define DEBUG_TYPE "dx-debug-info"
using namespace llvm;
using namespace llvm::dxil;
DXILDebugInfoMap DXILDebugInfoPass::run(Module &M) {
DXILDebugInfoMap Res;
DebugInfoFinder DIF;
DIF.processModule(M);
return Res;
}

View File

@@ -0,0 +1,44 @@
//===----- DebugInfo.h - analysis and lowering for Debug info -*- C++ -*- -===//
//
// 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 Analyze and downgrade debug info metadata to match DXIL (LLVM 3.7).
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_DIRECTX_DXILDEBUGINFO_H
#define LLVM_LIB_TARGET_DIRECTX_DXILDEBUGINFO_H
#include "llvm/ADT/DenseMap.h"
namespace llvm {
class Module;
class Metadata;
namespace dxil {
class DXILDebugInfoMap {
public:
using MDMap = DenseMap<const Metadata *, const Metadata *>;
/// Enumerate extra metadata when Key is encountered in ValueEnumerator.
MDMap MDExtra;
/// Completely replace one metadata with another in ValueEnumerator.
MDMap MDReplace;
};
namespace DXILDebugInfoPass {
DXILDebugInfoMap run(Module &M);
} // namespace DXILDebugInfoPass
} // namespace dxil
} // namespace llvm
#endif // LLVM_LIB_TARGET_DIRECTX_DXILDEBUGINFO_H

View File

@@ -10,7 +10,7 @@ set(LLVM_LINK_COMPONENTS
DirectXCodeGen
DirectXDesc
DirectXInfo
DirectXPointerTypeAnalysis
DirectXIRPasses
MC
Passes
Support