blob: dc3c05376b2c13d408e41ea382480e3804ab0092 [file] [edit]
//===- DXContainerPDB.cpp - DirectX PDB writer pass -----------------------===//
//
// 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 "DirectX.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/BinaryFormat/DXContainer.h"
#include "llvm/DebugInfo/CodeView/GUID.h"
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Module.h"
#include "llvm/MC/MCDXContainerWriter.h"
#include "llvm/Pass.h"
#include "llvm/Support/IOSandbox.h"
using namespace llvm;
namespace {
class DXContainerPDB : public ModulePass, MCDXContainerBaseWriter {
Module *M = nullptr;
SmallVector<MCDXContainerPart> Parts;
void reset() {
M = nullptr;
Parts.clear();
}
public:
static char ID;
DXContainerPDB() : ModulePass(ID) {}
StringRef getPassName() const override { return "DirectX PDB Emitter"; }
bool runOnModule(Module &M) override;
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
}
bool shouldSkipSection(StringRef SectionName, size_t SectionSize) override;
ArrayRef<MCDXContainerPart> collectParts() override;
};
} // namespace
bool DXContainerPDB::shouldSkipSection(StringRef SectionName,
size_t SectionSize) {
if (MCDXContainerBaseWriter::shouldSkipSection(SectionName, SectionSize))
return true;
// Skip sections that are irrelevant for debug info.
static const StringSet<> DebugSections{"ILDB", "ILDN", "HASH", "PDBI",
"SRCI", "STAT", "RDAT", "VERS"};
return !DebugSections.contains(SectionName);
}
static StringRef getGlobalData(const GlobalVariable &GV) {
if (GV.hasInitializer())
if (const auto *Data =
dyn_cast<ConstantDataSequential>(GV.getInitializer()))
return Data->getRawDataValues();
return {};
}
ArrayRef<MCDXContainerPart> DXContainerPDB::collectParts() {
Parts.clear();
for (const GlobalVariable &GV : M->globals()) {
StringRef Name = GV.getSection();
StringRef Data = getGlobalData(GV);
if (Data.empty())
continue;
if (shouldSkipSection(Name, Data.size()))
continue;
Parts.push_back({Name, Data});
}
return Parts;
}
bool DXContainerPDB::runOnModule(Module &M) {
this->M = &M;
StringRef DebugFileName;
ArrayRef<char> ModuleHash;
for (const GlobalVariable &GV : M.globals()) {
if (GV.getSection() == PdbFileNameSectionName) {
assert(DebugFileName.empty() && "Duplicate PDBNAME section");
DebugFileName = getGlobalData(GV);
} else if (GV.getSection() == ModuleHashSectionName) {
assert(ModuleHash.empty() && "Duplicate PBDHASH section");
StringRef Data = getGlobalData(GV);
ModuleHash = ArrayRef(Data.data(), Data.size());
}
}
// PDB emission was not requested.
if (DebugFileName.empty())
return false;
if (ModuleHash.empty())
report_fatal_error("Module hash for PDB not found");
BumpPtrAllocator Allocator;
pdb::PDBFileBuilder Builder(Allocator);
// DirectXShaderCompiler uses block size 512.
if (Error Err = Builder.initialize(512))
reportFatalInternalError(std::move(Err));
// Reserved streams that should be empty.
static_assert(pdb::kSpecialStreamCount == 5 &&
"First 5 streams should be empty in DirectX PDB file");
for (uint32_t I = 0; I < pdb::kSpecialStreamCount; ++I) {
if (auto Err = Builder.getMsfBuilder().addStream(0).takeError())
reportFatalInternalError(std::move(Err));
}
// Add DXContainer stream.
if (auto Err = Builder.getMsfBuilder().addStream(0).takeError())
reportFatalInternalError(std::move(Err));
// InfoStream must be filled. Bitcode hash from HASH part is used for PDB
// GUID.
codeview::GUID PdbGuid;
assert(ModuleHash.size() == std::size(PdbGuid.Guid) &&
"Module hash length must be match GUID length");
std::copy_n(ModuleHash.begin(), std::size(PdbGuid.Guid), PdbGuid.Guid);
auto &InfoBuilder = Builder.getInfoBuilder();
InfoBuilder.setAge(1);
InfoBuilder.setGuid(PdbGuid);
InfoBuilder.setSignature(0);
InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70);
// Write DXContainer.
raw_svector_ostream OS(*Builder.getDXContainerData());
write(OS, M.getTargetTriple());
// Write PDB file.
// FIXME(sandboxing): Remove this by routing PDB output through the VFS.
auto BypassSandbox = sys::sandbox::scopedDisable();
codeview::GUID IgnoredOutGuid;
if (Error Err = Builder.commit(DebugFileName, &IgnoredOutGuid))
reportFatalUsageError("Couldn't write to PDB file: " +
Twine(toString(std::move(Err))));
reset();
return false;
}
char DXContainerPDB::ID = 0;
INITIALIZE_PASS(DXContainerPDB, "dxil-pdb", "DirectX PDB Emitter", false, true)
ModulePass *llvm::createDXContainerPDBPass() { return new DXContainerPDB(); }