LLVM 24.0.0git
DXILWriterPass.cpp
Go to the documentation of this file.
1//===- DXILWriterPass.cpp - Bitcode writing pass --------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// DXILWriterPass implementation.
10//
11//===----------------------------------------------------------------------===//
12
13#include "DXILWriterPass.h"
14#include "DXILBitcodeWriter.h"
16#include "llvm/ADT/DenseMap.h"
17#include "llvm/ADT/STLExtras.h"
18#include "llvm/ADT/StringRef.h"
20#include "llvm/IR/Constants.h"
21#include "llvm/IR/DebugInfo.h"
25#include "llvm/IR/Intrinsics.h"
26#include "llvm/IR/LLVMContext.h"
27#include "llvm/IR/Module.h"
28#include "llvm/IR/PassManager.h"
30#include "llvm/Pass.h"
35
36using namespace llvm;
37using namespace llvm::dxil;
38
42 "dx-pdb-path",
43 cl::desc("Write debug information to the given file, or automatically "
44 "named file in directory when ending in '/'"),
45 cl::value_desc("filename"));
47 "dx-source-in-debug-module",
48 cl::desc("Embed source code into debug module on DirectX target"),
49 cl::init(false));
51
52namespace {
53class WriteDXILPass : public llvm::ModulePass {
54 raw_ostream &OS; // raw_ostream to print on
55
56public:
57 static char ID; // Pass identification, replacement for typeid
58 WriteDXILPass() : ModulePass(ID), OS(dbgs()) {
60 }
61
62 explicit WriteDXILPass(raw_ostream &o) : ModulePass(ID), OS(o) {
64 }
65
66 StringRef getPassName() const override { return "Bitcode Writer"; }
67
68 bool runOnModule(Module &M) override {
69 const auto DIMap = DXILDebugInfoPass::run(M);
70 WriteDXILToFile(M, OS, DIMap);
71 return false;
72 }
73 void getAnalysisUsage(AnalysisUsage &AU) const override {
74 AU.setPreservesAll();
75 }
76};
77
78static void legalizeLifetimeIntrinsics(Module &M) {
79 LLVMContext &Ctx = M.getContext();
80 Type *I64Ty = IntegerType::get(Ctx, 64);
81 Type *PtrTy = PointerType::get(Ctx, 0);
82 Intrinsic::ID LifetimeIIDs[2] = {Intrinsic::lifetime_start,
83 Intrinsic::lifetime_end};
84 for (Intrinsic::ID &IID : LifetimeIIDs) {
85 Function *F = M.getFunction(Intrinsic::getName(IID, {PtrTy}, &M));
86 if (!F)
87 continue;
88
89 // Get or insert an LLVM 3.7-compliant lifetime intrinsic function of the
90 // form `void @llvm.lifetime.[start/end](i64, ptr)` with the NoUnwind
91 // attribute
92 AttributeList Attr;
93 Attr = Attr.addFnAttribute(Ctx, Attribute::NoUnwind);
94 FunctionCallee LifetimeCallee = M.getOrInsertFunction(
95 Intrinsic::getBaseName(IID), Attr, Type::getVoidTy(Ctx), I64Ty, PtrTy);
96
97 // Replace all calls to lifetime intrinsics with calls to the
98 // LLVM 3.7-compliant version of the lifetime intrinsic
99 for (User *U : make_early_inc_range(F->users())) {
101 assert(CI &&
102 "Expected user of a lifetime intrinsic function to be a CallInst");
103
104 // LLVM 3.7 lifetime intrinics require an i8* operand, so we insert
105 // a bitcast to ensure that is the case
106 Value *PtrOperand = CI->getArgOperand(0);
107 PointerType *PtrOpPtrTy = cast<PointerType>(PtrOperand->getType());
108 Value *NoOpBitCast = CastInst::Create(Instruction::BitCast, PtrOperand,
109 PtrOpPtrTy, "", CI->getIterator());
110
111 // LLVM 3.7 lifetime intrinsics have an explicit size operand, whose value
112 // we can obtain from the pointer operand which must be an AllocaInst (as
113 // of https://github.com/llvm/llvm-project/pull/149310)
114 AllocaInst *AI = dyn_cast<AllocaInst>(PtrOperand);
115 assert(AI &&
116 "The pointer operand of a lifetime intrinsic call must be an "
117 "AllocaInst");
118 std::optional<TypeSize> AllocSize =
120 assert(AllocSize.has_value() &&
121 "Expected the allocation size of AllocaInst to be known");
122 CallInst *NewCI = CallInst::Create(
123 LifetimeCallee,
124 {ConstantInt::get(I64Ty, AllocSize.value().getFixedValue()),
125 NoOpBitCast},
126 "", CI->getIterator());
128 NewCI->addParamAttr(1, ParamAttr);
129
130 CI->eraseFromParent();
131 }
132
133 F->eraseFromParent();
134 }
135}
136
137static void removeLifetimeIntrinsics(Module &M) {
138 Intrinsic::ID LifetimeIIDs[2] = {Intrinsic::lifetime_start,
139 Intrinsic::lifetime_end};
140 for (Intrinsic::ID &IID : LifetimeIIDs) {
141 Function *F = M.getFunction(Intrinsic::getBaseName(IID));
142 if (!F)
143 continue;
144
145 for (User *U : make_early_inc_range(F->users())) {
147 assert(CI && "Expected user of lifetime function to be a CallInst");
149 assert(BCI && "Expected pointer operand of CallInst to be a BitCastInst");
150 CI->eraseFromParent();
151 BCI->eraseFromParent();
152 }
153 F->eraseFromParent();
154 }
155}
156
157static void replaceNamedMetadataArray(Module &M, StringRef Name,
158 ArrayRef<Metadata *> NewOps) {
159 NamedMDNode *NMD = M.getNamedMetadata(Name);
160 if (!NMD)
161 return;
162 NMD->eraseFromParent();
163 M.getOrInsertNamedMetadata(Name)->addOperand(
164 MDTuple::get(M.getContext(), NewOps));
165}
166
167class EmbedDXILPass : public llvm::ModulePass {
168 std::string writeModule(Module &M, bool HasDebugInfo, bool WriteDebug) {
169 std::string Data;
170 llvm::raw_string_ostream OS(Data);
171
172 if (HasDebugInfo) {
173 if (WriteDebug) {
174 if (!SourceInDebugModule) {
175 // Replace dx.source metadata nodes with stubs.
176 LLVMContext &Ctx = M.getContext();
177 MDString *EmptyString = MDString::get(Ctx, "");
178 replaceNamedMetadataArray(M, "dx.source.contents",
179 {EmptyString, EmptyString});
180 replaceNamedMetadataArray(M, "dx.source.defines", {});
181 replaceNamedMetadataArray(M, "dx.source.mainFileName", {EmptyString});
182 replaceNamedMetadataArray(M, "dx.source.args", {});
183 }
184 } else {
185 // If we have an ILDB part, strip DXIL from all debug info.
187
188 // Also, manually remove debug version flags and dx.source nodes.
189 if (NamedMDNode *Flags = M.getModuleFlagsMetadata()) {
191 M.getModuleFlagsMetadata(FlagEntries);
192 Flags->eraseFromParent();
193 for (llvm::Module::ModuleFlagEntry &Entry : FlagEntries) {
194 if (Entry.Key->getString() == "Dwarf Version" ||
195 Entry.Key->getString() == "Debug Info Version") {
196 continue;
197 }
198 M.addModuleFlag(Entry.Behavior, Entry.Key->getString(), Entry.Val);
199 }
200 }
201 for (NamedMDNode &NMD : llvm::make_early_inc_range(M.named_metadata()))
202 if (NMD.getName().starts_with("dx.source"))
203 NMD.eraseFromParent();
204 }
205 } else {
206#ifdef EXPENSIVE_CHECKS
207 assert(
208 StripDebugInfo(M) == false &&
209 "The module must not contain any debug info here."
210 "Shader modules with debug info must have !DICompileUnit metadata.");
211#endif
212 }
213 const auto DIMap = DXILDebugInfoPass::run(M);
214 WriteDXILToFile(M, OS, DIMap);
215 return Data;
216 }
217
218 GlobalVariable *createSectionGlobal(Module &M, StringRef Data,
219 StringRef GlobalName,
220 StringRef SectionName) {
221 Constant *ModuleConstant =
223 auto *GV = new llvm::GlobalVariable(M, ModuleConstant->getType(), true,
225 ModuleConstant, GlobalName);
226 GV->setSection(SectionName);
227 GV->setAlignment(Align(4));
228 return GV;
229 }
230
231public:
232 static char ID; // Pass identification, replacement for typeid
233 EmbedDXILPass() : ModulePass(ID) {
235 }
236
237 StringRef getPassName() const override { return "DXIL Embedder"; }
238
239 bool runOnModule(Module &M) override {
240 // Perform late legalization of lifetime intrinsics that would otherwise
241 // fail the Module Verifier if performed in an earlier pass
242 legalizeLifetimeIntrinsics(M);
243
244 bool HasDebugInfo = !M.debug_compile_units().empty();
245
246 if (SlimDebug && EmbedDebug)
247 reportFatalUsageError("/Qembed_debug is not compatible with /Zs");
248
249 // If both StripDebug and EmbedDebug are specified, StripDebug is ignored.
250 if (StripDebug && EmbedDebug)
251 StripDebug = false;
252 // Enable EmbedDebug if there is debug info, but it is not being stripped
253 // or written to a PDB file.
254 if (HasDebugInfo && !StripDebug && !SlimDebug && PdbDebugPath.empty())
255 EmbedDebug = true;
256 if (!HasDebugInfo && EmbedDebug)
258 "Missing debug info for embedding into the container");
259 if (!HasDebugInfo && !PdbDebugPath.empty())
260 reportFatalUsageError("Missing debug info for writing to the PDB file");
261
262 std::string ILDBData;
263 if (HasDebugInfo) {
264 // Write DXIL with debug info to ILDB part.
265 // Clone the module to avoid alternating it with DebugInfoPass
266 // before stripping the debug info later.
267 ILDBData =
268 writeModule(*llvm::CloneModule(M), HasDebugInfo, /*WriteDebug=*/true);
269 }
270
271 // Clone the module to save dx.source metadata nodes from stripping, as they
272 // are needed for DXILMetadataAnalysisWrapperPass.
273 std::string DXILData =
274 writeModule(*llvm::CloneModule(M), HasDebugInfo, /*WriteDebug=*/false);
275
276 // We no longer need lifetime intrinsics after bitcode serialization, so we
277 // simply remove them to keep the Module Verifier happy after our
278 // not-so-legal legalizations
279 removeLifetimeIntrinsics(M);
280
282 if (HasDebugInfo) {
283 // Create a GV after both parts are written, otherwise it gets
284 // added to DXIL when `writeModule` is called the second time.
285 Globals.emplace_back(createSectionGlobal(M, ILDBData, "dx.ildb", "ILDB"));
286 }
287 Globals.emplace_back(createSectionGlobal(M, DXILData, "dx.dxil", "DXIL"));
288 appendToCompilerUsed(M, Globals);
289 return true;
290 }
291
292 void getAnalysisUsage(AnalysisUsage &AU) const override {
293 AU.setPreservesAll();
294 }
295};
296} // namespace
297
298char WriteDXILPass::ID = 0;
299INITIALIZE_PASS_BEGIN(WriteDXILPass, "dxil-write-bitcode", "Write Bitcode",
300 false, true)
302INITIALIZE_PASS_END(WriteDXILPass, "dxil-write-bitcode", "Write Bitcode", false,
303 true)
304
306 return new WriteDXILPass(Str);
307}
308
309char EmbedDXILPass::ID = 0;
310INITIALIZE_PASS(EmbedDXILPass, "dxil-embed", "Embed DXIL", false, true)
311
312ModulePass *llvm::createDXILEmbedderPass() { return new EmbedDXILPass(); }
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
@ ParamAttr
This file contains the declarations for the subclasses of Constant, which represent the different fla...
cl::opt< bool > SourceInDebugModule("dx-source-in-debug-module", cl::desc("Embed source code into debug module on DirectX target"), cl::init(false))
cl::opt< bool > StripDebug
cl::opt< bool > SlimDebug
cl::opt< bool > EmbedDebug
cl::opt< std::string > PdbDebugPath("dx-pdb-path", cl::desc("Write debug information to the given file, or automatically " "named file in directory when ending in '/'"), cl::value_desc("filename"))
This file provides a bitcode writing pass.
This file defines the DenseMap class.
Module.h This file contains the declarations for the Module class.
This header defines various interfaces for pass management in LLVM.
cl::opt< bool > EmbedDebug("dx-embed-debug", cl::desc("Embed PDB in shader container"))
cl::opt< bool > StripDebug("dx-strip-debug", cl::desc("Strip debug information from shader bytecode"))
cl::opt< bool > SlimDebug("dx-slim-debug", cl::desc("Generate slim PDB without ILDB part"))
#define F(x, y, z)
Definition MD5.cpp:54
Machine Check Debug Module
This is the interface to build a ModuleSummaryIndex for a module.
#define INITIALIZE_PASS_DEPENDENCY(depName)
Definition PassSupport.h:42
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition PassSupport.h:44
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition PassSupport.h:39
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
This file contains some templates that are useful if you are working with the STL at all.
an instruction to allocate memory on the stack
LLVM_ABI std::optional< TypeSize > getAllocationSize(const DataLayout &DL) const
Get allocation size in bytes.
void setPreservesAll()
Set by analyses that do not transform their input at all.
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
Functions, function parameters, and return types can have attributes to indicate how they should be t...
Definition Attributes.h:105
This class represents a no-op cast from one type to another.
AttributeSet getParamAttributes(unsigned ArgNo) const
Return the param attributes for this call.
Value * getArgOperand(unsigned i) const
void addParamAttr(unsigned ArgNo, Attribute::AttrKind Kind)
Adds the attribute to the indicated argument.
This class represents a function call, abstracting a target machine's calling convention.
static CallInst * Create(FunctionType *Ty, Value *F, const Twine &NameStr="", InsertPosition InsertBefore=nullptr)
static LLVM_ABI CastInst * Create(Instruction::CastOps, Value *S, Type *Ty, const Twine &Name="", InsertPosition InsertBefore=nullptr)
Provides a way to construct any of the CastInst subclasses using an opcode instead of the subclass's ...
static Constant * get(LLVMContext &Context, ArrayRef< ElementTy > Elts)
get() constructor - Return a constant with array type with an element count and element type matching...
Definition Constants.h:878
A handy container for a FunctionType+Callee-pointer pair, which can be passed around as a single enti...
@ PrivateLinkage
Like Internal, but omit from symbol table.
Definition GlobalValue.h:61
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
LLVM_ABI const DataLayout & getDataLayout() const
Get the data layout of the module this instruction belongs to.
static LLVM_ABI IntegerType * get(LLVMContext &C, unsigned NumBits)
This static method is the primary way of constructing an IntegerType.
Definition Type.cpp:348
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:68
static LLVM_ABI MDString * get(LLVMContext &Context, StringRef Str)
Definition Metadata.cpp:614
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1511
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition Pass.h:255
Legacy wrapper pass to provide the ModuleSummaryIndex object.
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:67
A tuple of MDNodes.
Definition Metadata.h:1753
LLVM_ABI StringRef getName() const
LLVM_ABI void eraseFromParent()
Drop all references and remove the node from parent module.
static LLVM_ABI PassRegistry * getPassRegistry()
getPassRegistry - Access the global registry object, which is automatically initialized at applicatio...
static LLVM_ABI PointerType * get(Type *ElementType, unsigned AddressSpace)
This constructs a pointer to an object of the specified type in a numbered address space.
reference emplace_back(ArgTypes &&... Args)
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition StringRef.h:258
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
static LLVM_ABI Type * getVoidTy(LLVMContext &C)
Definition Type.cpp:282
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:255
self_iterator getIterator()
Definition ilist_node.h:123
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
@ Entry
Definition COFF.h:862
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
LLVM_ABI StringRef getName(ID id)
Return the LLVM name for an intrinsic, such as "llvm.ppc.altivec.lvx".
LLVM_ABI StringRef getBaseName(ID id)
Return the LLVM name for an intrinsic, without encoded types for overloading, such as "llvm....
initializer< Ty > init(const Ty &Val)
DXILDebugInfoMap run(Module &M)
void WriteDXILToFile(const Module &M, raw_ostream &Out, const DXILDebugInfoMap &DebugInfo)
Write the specified module to the specified raw output stream.
This is an optimization pass for GlobalISel generic memory operations.
ArrayRef< CharT > arrayRefFromStringRef(StringRef Input)
Construct an array ref of bytes from a string ref.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
ModulePass * createDXILWriterPass(raw_ostream &Str)
Create and return a pass that writes the module to the specified ostream.
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition STLExtras.h:633
LLVM_ABI size_t writeModule(const Module &M, uint8_t *Dest, size_t MaxSize)
Fuzzer friendly interface for the llvm bitcode printer.
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
void initializeEmbedDXILPassPass(PassRegistry &)
Initializer for dxil embedder pass.
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
LLVM_ABI bool StripDebugInfo(Module &M)
Strip debug info in the module if it exists.
LLVM_ABI void appendToCompilerUsed(Module &M, ArrayRef< GlobalValue * > Values)
Adds global values to the llvm.compiler.used list.
ModulePass * createDXILEmbedderPass()
Create and return a pass that writes the module to a global variable in the module for later emission...
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
void initializeWriteDXILPassPass(PassRegistry &)
Initializer for dxil writer pass.
LLVM_ABI std::unique_ptr< Module > CloneModule(const Module &M)
Return an exact copy of the specified module.
LLVM_ABI void reportFatalUsageError(Error Err)
Report a fatal error that does not indicate a bug in LLVM.
Definition Error.cpp:177