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"
15#include "llvm/ADT/STLExtras.h"
16#include "llvm/ADT/StringRef.h"
17#include "llvm/Analysis/ModuleSummaryAnalysis.h"
18#include "llvm/IR/Constants.h"
19#include "llvm/IR/DebugInfo.h"
20#include "llvm/IR/DerivedTypes.h"
21#include "llvm/IR/GlobalVariable.h"
22#include "llvm/IR/IntrinsicInst.h"
23#include "llvm/IR/Intrinsics.h"
24#include "llvm/IR/LLVMContext.h"
25#include "llvm/IR/Module.h"
26#include "llvm/IR/PassManager.h"
27#include "llvm/InitializePasses.h"
28#include "llvm/Pass.h"
29#include "llvm/Support/Alignment.h"
30#include "llvm/Support/CommandLine.h"
31#include "llvm/Transforms/Utils/Cloning.h"
32#include "llvm/Transforms/Utils/ModuleUtils.h"
33
34using namespace llvm;
35using namespace llvm::dxil;
36
37extern cl::opt<bool> EmbedDebug;
38extern cl::opt<bool> StripDebug;
39cl::opt<std::string> PdbDebugPath(
40 "dx-pdb-path",
41 cl::desc("Write debug information to the given file, or automatically "
42 "named file in directory when ending in '/'"),
43 cl::value_desc("filename"));
44cl::opt<bool> SourceInDebugModule(
45 "dx-source-in-debug-module",
46 cl::desc("Embed source code into debug module on DirectX target"),
47 cl::init(Val: false));
48extern cl::opt<bool> SlimDebug;
49
50namespace {
51class WriteDXILPass : public llvm::ModulePass {
52 raw_ostream &OS; // raw_ostream to print on
53
54public:
55 static char ID; // Pass identification, replacement for typeid
56 WriteDXILPass() : ModulePass(ID), OS(dbgs()) {
57 initializeWriteDXILPassPass(*PassRegistry::getPassRegistry());
58 }
59
60 explicit WriteDXILPass(raw_ostream &o) : ModulePass(ID), OS(o) {
61 initializeWriteDXILPassPass(*PassRegistry::getPassRegistry());
62 }
63
64 StringRef getPassName() const override { return "Bitcode Writer"; }
65
66 bool runOnModule(Module &M) override {
67 WriteDXILToFile(M, Out&: OS);
68 return false;
69 }
70 void getAnalysisUsage(AnalysisUsage &AU) const override {
71 AU.setPreservesAll();
72 }
73};
74
75static void legalizeLifetimeIntrinsics(Module &M) {
76 LLVMContext &Ctx = M.getContext();
77 Type *I64Ty = IntegerType::get(C&: Ctx, NumBits: 64);
78 Type *PtrTy = PointerType::get(C&: Ctx, AddressSpace: 0);
79 Intrinsic::ID LifetimeIIDs[2] = {Intrinsic::lifetime_start,
80 Intrinsic::lifetime_end};
81 for (Intrinsic::ID &IID : LifetimeIIDs) {
82 Function *F = M.getFunction(Name: Intrinsic::getName(Id: IID, OverloadTys: {PtrTy}, M: &M));
83 if (!F)
84 continue;
85
86 // Get or insert an LLVM 3.7-compliant lifetime intrinsic function of the
87 // form `void @llvm.lifetime.[start/end](i64, ptr)` with the NoUnwind
88 // attribute
89 AttributeList Attr;
90 Attr = Attr.addFnAttribute(C&: Ctx, Kind: Attribute::NoUnwind);
91 FunctionCallee LifetimeCallee = M.getOrInsertFunction(
92 Name: Intrinsic::getBaseName(id: IID), AttributeList: Attr, RetTy: Type::getVoidTy(C&: Ctx), Args: I64Ty, Args: PtrTy);
93
94 // Replace all calls to lifetime intrinsics with calls to the
95 // LLVM 3.7-compliant version of the lifetime intrinsic
96 for (User *U : make_early_inc_range(Range: F->users())) {
97 CallInst *CI = dyn_cast<CallInst>(Val: U);
98 assert(CI &&
99 "Expected user of a lifetime intrinsic function to be a CallInst");
100
101 // LLVM 3.7 lifetime intrinics require an i8* operand, so we insert
102 // a bitcast to ensure that is the case
103 Value *PtrOperand = CI->getArgOperand(i: 0);
104 PointerType *PtrOpPtrTy = cast<PointerType>(Val: PtrOperand->getType());
105 Value *NoOpBitCast = CastInst::Create(Instruction::BitCast, S: PtrOperand,
106 Ty: PtrOpPtrTy, Name: "", InsertBefore: CI->getIterator());
107
108 // LLVM 3.7 lifetime intrinsics have an explicit size operand, whose value
109 // we can obtain from the pointer operand which must be an AllocaInst (as
110 // of https://github.com/llvm/llvm-project/pull/149310)
111 AllocaInst *AI = dyn_cast<AllocaInst>(Val: PtrOperand);
112 assert(AI &&
113 "The pointer operand of a lifetime intrinsic call must be an "
114 "AllocaInst");
115 std::optional<TypeSize> AllocSize =
116 AI->getAllocationSize(DL: CI->getDataLayout());
117 assert(AllocSize.has_value() &&
118 "Expected the allocation size of AllocaInst to be known");
119 CallInst *NewCI = CallInst::Create(
120 Func: LifetimeCallee,
121 Args: {ConstantInt::get(Ty: I64Ty, V: AllocSize.value().getFixedValue()),
122 NoOpBitCast},
123 NameStr: "", InsertBefore: CI->getIterator());
124 for (Attribute ParamAttr : CI->getParamAttributes(ArgNo: 0))
125 NewCI->addParamAttr(ArgNo: 1, Attr: ParamAttr);
126
127 CI->eraseFromParent();
128 }
129
130 F->eraseFromParent();
131 }
132}
133
134static void removeLifetimeIntrinsics(Module &M) {
135 Intrinsic::ID LifetimeIIDs[2] = {Intrinsic::lifetime_start,
136 Intrinsic::lifetime_end};
137 for (Intrinsic::ID &IID : LifetimeIIDs) {
138 Function *F = M.getFunction(Name: Intrinsic::getBaseName(id: IID));
139 if (!F)
140 continue;
141
142 for (User *U : make_early_inc_range(Range: F->users())) {
143 CallInst *CI = dyn_cast<CallInst>(Val: U);
144 assert(CI && "Expected user of lifetime function to be a CallInst");
145 BitCastInst *BCI = dyn_cast<BitCastInst>(Val: CI->getArgOperand(i: 1));
146 assert(BCI && "Expected pointer operand of CallInst to be a BitCastInst");
147 CI->eraseFromParent();
148 BCI->eraseFromParent();
149 }
150 F->eraseFromParent();
151 }
152}
153
154static void replaceNamedMetadataArray(Module &M, StringRef Name,
155 ArrayRef<Metadata *> NewOps) {
156 NamedMDNode *NMD = M.getNamedMetadata(Name);
157 if (!NMD)
158 return;
159 NMD->eraseFromParent();
160 M.getOrInsertNamedMetadata(Name)->addOperand(
161 M: MDTuple::get(Context&: M.getContext(), MDs: NewOps));
162}
163
164class EmbedDXILPass : public llvm::ModulePass {
165 std::string writeModule(Module &M, bool HasDebugInfo, bool WriteDebug) {
166 std::string Data;
167 llvm::raw_string_ostream OS(Data);
168
169 if (HasDebugInfo) {
170 if (WriteDebug) {
171 if (!SourceInDebugModule) {
172 // Replace dx.source metadata nodes with stubs.
173 LLVMContext &Ctx = M.getContext();
174 MDString *EmptyString = MDString::get(Context&: Ctx, Str: "");
175 replaceNamedMetadataArray(M, Name: "dx.source.contents",
176 NewOps: {EmptyString, EmptyString});
177 replaceNamedMetadataArray(M, Name: "dx.source.defines", NewOps: {});
178 replaceNamedMetadataArray(M, Name: "dx.source.mainFileName", NewOps: {EmptyString});
179 replaceNamedMetadataArray(M, Name: "dx.source.args", NewOps: {});
180 }
181 } else {
182 // If we have an ILDB part, strip DXIL from all debug info.
183 StripDebugInfo(M);
184
185 // Also, manually remove debug version flags and dx.source nodes.
186 if (NamedMDNode *Flags = M.getModuleFlagsMetadata()) {
187 SmallVector<llvm::Module::ModuleFlagEntry, 4> FlagEntries;
188 M.getModuleFlagsMetadata(Flags&: FlagEntries);
189 Flags->eraseFromParent();
190 for (llvm::Module::ModuleFlagEntry &Entry : FlagEntries) {
191 if (Entry.Key->getString() == "Dwarf Version" ||
192 Entry.Key->getString() == "Debug Info Version") {
193 continue;
194 }
195 M.addModuleFlag(Behavior: Entry.Behavior, Key: Entry.Key->getString(), Val: Entry.Val);
196 }
197 }
198 for (NamedMDNode &NMD : llvm::make_early_inc_range(Range: M.named_metadata()))
199 if (NMD.getName().starts_with(Prefix: "dx.source"))
200 NMD.eraseFromParent();
201 }
202 } else {
203#ifdef EXPENSIVE_CHECKS
204 assert(
205 StripDebugInfo(M) == false &&
206 "The module must not contain any debug info here."
207 "Shader modules with debug info must have !DICompileUnit metadata.");
208#endif
209 }
210 WriteDXILToFile(M, Out&: OS);
211 return Data;
212 }
213
214 GlobalVariable *createSectionGlobal(Module &M, StringRef Data,
215 StringRef GlobalName,
216 StringRef SectionName) {
217 Constant *ModuleConstant =
218 ConstantDataArray::get(Context&: M.getContext(), Elts: arrayRefFromStringRef(Input: Data));
219 auto *GV = new llvm::GlobalVariable(M, ModuleConstant->getType(), true,
220 GlobalValue::PrivateLinkage,
221 ModuleConstant, GlobalName);
222 GV->setSection(SectionName);
223 GV->setAlignment(Align(4));
224 return GV;
225 }
226
227public:
228 static char ID; // Pass identification, replacement for typeid
229 EmbedDXILPass() : ModulePass(ID) {
230 initializeEmbedDXILPassPass(*PassRegistry::getPassRegistry());
231 }
232
233 StringRef getPassName() const override { return "DXIL Embedder"; }
234
235 bool runOnModule(Module &M) override {
236 // Perform late legalization of lifetime intrinsics that would otherwise
237 // fail the Module Verifier if performed in an earlier pass
238 legalizeLifetimeIntrinsics(M);
239
240 bool HasDebugInfo = !M.debug_compile_units().empty();
241
242 if (SlimDebug && EmbedDebug)
243 reportFatalUsageError(reason: "/Qembed_debug is not compatible with /Zs");
244
245 // If both StripDebug and EmbedDebug are specified, StripDebug is ignored.
246 if (StripDebug && EmbedDebug)
247 StripDebug = false;
248 // Enable EmbedDebug if there is debug info, but it is not being stripped
249 // or written to a PDB file.
250 if (HasDebugInfo && !StripDebug && !SlimDebug && PdbDebugPath.empty())
251 EmbedDebug = true;
252 if (!HasDebugInfo && EmbedDebug)
253 reportFatalUsageError(
254 reason: "Missing debug info for embedding into the container");
255 if (!HasDebugInfo && !PdbDebugPath.empty())
256 reportFatalUsageError(reason: "Missing debug info for writing to the PDB file");
257
258 std::string ILDBData;
259 if (HasDebugInfo) {
260 // Write DXIL with debug info to ILDB part.
261 // Clone the module to avoid alternating it with DebugInfoPass
262 // before stripping the debug info later.
263 ILDBData =
264 writeModule(M&: *llvm::CloneModule(M), HasDebugInfo, /*WriteDebug=*/true);
265 }
266
267 // Clone the module to save dx.source metadata nodes from stripping, as they
268 // are needed for DXILMetadataAnalysisWrapperPass.
269 std::string DXILData =
270 writeModule(M&: *llvm::CloneModule(M), HasDebugInfo, /*WriteDebug=*/false);
271
272 // We no longer need lifetime intrinsics after bitcode serialization, so we
273 // simply remove them to keep the Module Verifier happy after our
274 // not-so-legal legalizations
275 removeLifetimeIntrinsics(M);
276
277 SmallVector<GlobalValue *, 2> Globals;
278 if (HasDebugInfo) {
279 // Create a GV after both parts are written, otherwise it gets
280 // added to DXIL when `writeModule` is called the second time.
281 Globals.emplace_back(Args: createSectionGlobal(M, Data: ILDBData, GlobalName: "dx.ildb", SectionName: "ILDB"));
282 }
283 Globals.emplace_back(Args: createSectionGlobal(M, Data: DXILData, GlobalName: "dx.dxil", SectionName: "DXIL"));
284 appendToCompilerUsed(M, Values: Globals);
285 return true;
286 }
287
288 void getAnalysisUsage(AnalysisUsage &AU) const override {
289 AU.setPreservesAll();
290 }
291};
292} // namespace
293
294char WriteDXILPass::ID = 0;
295INITIALIZE_PASS_BEGIN(WriteDXILPass, "dxil-write-bitcode", "Write Bitcode",
296 false, true)
297INITIALIZE_PASS_DEPENDENCY(ModuleSummaryIndexWrapperPass)
298INITIALIZE_PASS_END(WriteDXILPass, "dxil-write-bitcode", "Write Bitcode", false,
299 true)
300
301ModulePass *llvm::createDXILWriterPass(raw_ostream &Str) {
302 return new WriteDXILPass(Str);
303}
304
305char EmbedDXILPass::ID = 0;
306INITIALIZE_PASS(EmbedDXILPass, "dxil-embed", "Embed DXIL", false, true)
307
308ModulePass *llvm::createDXILEmbedderPass() { return new EmbedDXILPass(); }
309