LLVM 22.0.0git
DXILPrepare.cpp
Go to the documentation of this file.
1//===- DXILPrepare.cpp - Prepare LLVM Module for DXIL encoding ------------===//
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/// \file This file contains pases and utilities to convert a modern LLVM
10/// module into a module compatible with the LLVM 3.7-based DirectX Intermediate
11/// Language (DXIL).
12//===----------------------------------------------------------------------===//
13
14#include "DXILRootSignature.h"
15#include "DXILShaderFlags.h"
16#include "DirectX.h"
18#include "llvm/ADT/STLExtras.h"
20#include "llvm/ADT/StringSet.h"
23#include "llvm/CodeGen/Passes.h"
25#include "llvm/IR/IRBuilder.h"
26#include "llvm/IR/Instruction.h"
27#include "llvm/IR/Module.h"
29#include "llvm/Pass.h"
32
33#define DEBUG_TYPE "dxil-prepare"
34
35using namespace llvm;
36using namespace llvm::dxil;
37
38namespace {
39
40constexpr bool isValidForDXIL(Attribute::AttrKind Attr) {
41 return is_contained({Attribute::Alignment,
42 Attribute::AlwaysInline,
43 Attribute::Builtin,
44 Attribute::ByVal,
45 Attribute::InAlloca,
46 Attribute::Cold,
47 Attribute::Convergent,
48 Attribute::InlineHint,
49 Attribute::InReg,
50 Attribute::JumpTable,
51 Attribute::MinSize,
52 Attribute::Naked,
53 Attribute::Nest,
54 Attribute::NoAlias,
55 Attribute::NoBuiltin,
56 Attribute::NoDuplicate,
57 Attribute::NoImplicitFloat,
58 Attribute::NoInline,
59 Attribute::NonLazyBind,
60 Attribute::NonNull,
61 Attribute::Dereferenceable,
62 Attribute::DereferenceableOrNull,
63 Attribute::Memory,
64 Attribute::NoRedZone,
65 Attribute::NoReturn,
66 Attribute::NoUnwind,
67 Attribute::OptimizeForSize,
68 Attribute::OptimizeNone,
69 Attribute::ReadNone,
70 Attribute::ReadOnly,
71 Attribute::Returned,
72 Attribute::ReturnsTwice,
73 Attribute::SExt,
74 Attribute::StackAlignment,
75 Attribute::StackProtect,
76 Attribute::StackProtectReq,
77 Attribute::StackProtectStrong,
78 Attribute::SafeStack,
79 Attribute::StructRet,
80 Attribute::SanitizeAddress,
81 Attribute::SanitizeThread,
82 Attribute::SanitizeMemory,
83 Attribute::UWTable,
84 Attribute::ZExt},
85 Attr);
86}
87
88static void collectDeadStringAttrs(AttributeMask &DeadAttrs, AttributeSet &&AS,
89 const StringSet<> &LiveKeys,
90 bool AllowExperimental) {
91 for (auto &Attr : AS) {
92 if (!Attr.isStringAttribute())
93 continue;
94 StringRef Key = Attr.getKindAsString();
95 if (LiveKeys.contains(Key))
96 continue;
97 if (AllowExperimental && Key.starts_with("exp-"))
98 continue;
99 DeadAttrs.addAttribute(Key);
100 }
101}
102
103static void removeStringFunctionAttributes(Function &F,
104 bool AllowExperimental) {
105 AttributeList Attrs = F.getAttributes();
106 const StringSet<> LiveKeys = {"waveops-include-helper-lanes",
107 "fp32-denorm-mode"};
108 // Collect DeadKeys in FnAttrs.
109 AttributeMask DeadAttrs;
110 collectDeadStringAttrs(DeadAttrs, Attrs.getFnAttrs(), LiveKeys,
111 AllowExperimental);
112 collectDeadStringAttrs(DeadAttrs, Attrs.getRetAttrs(), LiveKeys,
113 AllowExperimental);
114
115 F.removeFnAttrs(DeadAttrs);
116 F.removeRetAttrs(DeadAttrs);
117}
118
119static void cleanModuleFlags(Module &M) {
120 NamedMDNode *MDFlags = M.getModuleFlagsMetadata();
121 if (!MDFlags)
122 return;
123
125 M.getModuleFlagsMetadata(FlagEntries);
126 bool Updated = false;
127 for (auto &Flag : FlagEntries) {
128 // llvm 3.7 only supports behavior up to AppendUnique.
129 if (Flag.Behavior <= Module::ModFlagBehavior::AppendUnique)
130 continue;
131 Flag.Behavior = Module::ModFlagBehavior::Warning;
132 Updated = true;
133 }
134
135 if (!Updated)
136 return;
137
138 MDFlags->eraseFromParent();
139
140 for (auto &Flag : FlagEntries)
141 M.addModuleFlag(Flag.Behavior, Flag.Key->getString(), Flag.Val);
142}
143
144class DXILPrepareModule : public ModulePass {
145
146 static Value *maybeGenerateBitcast(IRBuilder<> &Builder,
147 PointerTypeMap &PointerTypes,
148 Instruction &Inst, Value *Operand,
149 Type *Ty) {
150 // Omit bitcasts if the incoming value matches the instruction type.
151 auto It = PointerTypes.find(Operand);
152 if (It != PointerTypes.end()) {
153 auto *OpTy = cast<TypedPointerType>(It->second)->getElementType();
154 if (OpTy == Ty)
155 return nullptr;
156 }
157
158 Type *ValTy = Operand->getType();
159 // Also omit the bitcast for matching global array types
160 if (auto *GlobalVar = dyn_cast<GlobalVariable>(Operand))
161 ValTy = GlobalVar->getValueType();
162
163 if (auto *AI = dyn_cast<AllocaInst>(Operand))
164 ValTy = AI->getAllocatedType();
165
166 if (auto *ArrTy = dyn_cast<ArrayType>(ValTy)) {
167 Type *ElTy = ArrTy->getElementType();
168 if (ElTy == Ty)
169 return nullptr;
170 }
171
172 // finally, drill down GEP instructions until we get the array
173 // that is being accessed, and compare element types
174 if (ConstantExpr *GEPInstr = dyn_cast<ConstantExpr>(Operand)) {
175 while (GEPInstr->getOpcode() == Instruction::GetElementPtr) {
176 Value *OpArg = GEPInstr->getOperand(0);
177 if (ConstantExpr *NewGEPInstr = dyn_cast<ConstantExpr>(OpArg)) {
178 GEPInstr = NewGEPInstr;
179 continue;
180 }
181
182 if (auto *GlobalVar = dyn_cast<GlobalVariable>(OpArg))
183 ValTy = GlobalVar->getValueType();
184 if (auto *AI = dyn_cast<AllocaInst>(Operand))
185 ValTy = AI->getAllocatedType();
186 if (auto *ArrTy = dyn_cast<ArrayType>(ValTy)) {
187 Type *ElTy = ArrTy->getElementType();
188 if (ElTy == Ty)
189 return nullptr;
190 }
191 break;
192 }
193 }
194
195 // Insert bitcasts where we are removing the instruction.
196 Builder.SetInsertPoint(&Inst);
197 // This code only gets hit in opaque-pointer mode, so the type of the
198 // pointer doesn't matter.
199 PointerType *PtrTy = cast<PointerType>(Operand->getType());
200 return Builder.Insert(
201 CastInst::Create(Instruction::BitCast, Operand,
202 Builder.getPtrTy(PtrTy->getAddressSpace())));
203 }
204
205 static std::array<unsigned, 6> getCompatibleInstructionMDs(llvm::Module &M) {
206 return {M.getMDKindID("dx.nonuniform"),
207 M.getMDKindID("dx.controlflow.hints"),
208 M.getMDKindID("dx.precise"),
209 llvm::LLVMContext::MD_range,
210 llvm::LLVMContext::MD_alias_scope,
211 llvm::LLVMContext::MD_noalias};
212 }
213
214public:
215 bool runOnModule(Module &M) override {
217 AttributeMask AttrMask;
219 I = Attribute::AttrKind(I + 1)) {
220 if (!isValidForDXIL(I))
221 AttrMask.addAttribute(I);
222 }
223
224 const dxil::ModuleMetadataInfo MetadataInfo =
225 getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
226 VersionTuple ValVer = MetadataInfo.ValidatorVersion;
227 bool SkipValidation = ValVer.getMajor() == 0 && ValVer.getMinor() == 0;
228
229 // construct allowlist of valid metadata node kinds
230 std::array<unsigned, 6> DXILCompatibleMDs = getCompatibleInstructionMDs(M);
231
232 for (auto &F : M.functions()) {
233 F.removeFnAttrs(AttrMask);
234 F.removeRetAttrs(AttrMask);
235 // Only remove string attributes if we are not skipping validation.
236 // This will reserve the experimental attributes when validation version
237 // is 0.0 for experiment mode.
238 removeStringFunctionAttributes(F, SkipValidation);
239 for (size_t Idx = 0, End = F.arg_size(); Idx < End; ++Idx)
240 F.removeParamAttrs(Idx, AttrMask);
241
242 for (auto &BB : F) {
243 IRBuilder<> Builder(&BB);
244 for (auto &I : make_early_inc_range(BB)) {
245
246 I.dropUnknownNonDebugMetadata(DXILCompatibleMDs);
247
248 // Emtting NoOp bitcast instructions allows the ValueEnumerator to be
249 // unmodified as it reserves instruction IDs during contruction.
250 if (auto LI = dyn_cast<LoadInst>(&I)) {
251 if (Value *NoOpBitcast = maybeGenerateBitcast(
252 Builder, PointerTypes, I, LI->getPointerOperand(),
253 LI->getType())) {
254 LI->replaceAllUsesWith(
255 Builder.CreateLoad(LI->getType(), NoOpBitcast));
256 LI->eraseFromParent();
257 }
258 continue;
259 }
260 if (auto SI = dyn_cast<StoreInst>(&I)) {
261 if (Value *NoOpBitcast = maybeGenerateBitcast(
262 Builder, PointerTypes, I, SI->getPointerOperand(),
263 SI->getValueOperand()->getType())) {
264
265 SI->replaceAllUsesWith(
266 Builder.CreateStore(SI->getValueOperand(), NoOpBitcast));
267 SI->eraseFromParent();
268 }
269 continue;
270 }
271 if (auto GEP = dyn_cast<GetElementPtrInst>(&I)) {
272 if (Value *NoOpBitcast = maybeGenerateBitcast(
273 Builder, PointerTypes, I, GEP->getPointerOperand(),
274 GEP->getSourceElementType()))
275 GEP->setOperand(0, NoOpBitcast);
276 continue;
277 }
278 if (auto *CB = dyn_cast<CallBase>(&I)) {
279 CB->removeFnAttrs(AttrMask);
280 CB->removeRetAttrs(AttrMask);
281 for (size_t Idx = 0, End = CB->arg_size(); Idx < End; ++Idx)
282 CB->removeParamAttrs(Idx, AttrMask);
283 continue;
284 }
285 }
286 }
287 }
288 // Remove flags not for DXIL.
289 cleanModuleFlags(M);
290
291 // dx.rootsignatures will have been parsed from its metadata form as its
292 // binary form as part of the RootSignatureAnalysisWrapper, so safely
293 // remove it as it is not recognized in DXIL
294 if (NamedMDNode *RootSignature = M.getNamedMetadata("dx.rootsignatures"))
295 RootSignature->eraseFromParent();
296
297 return true;
298 }
299
300 DXILPrepareModule() : ModulePass(ID) {}
301 void getAnalysisUsage(AnalysisUsage &AU) const override {
308 }
309 static char ID; // Pass identification.
310};
311char DXILPrepareModule::ID = 0;
312
313} // end anonymous namespace
314
315INITIALIZE_PASS_BEGIN(DXILPrepareModule, DEBUG_TYPE, "DXIL Prepare Module",
316 false, false)
319INITIALIZE_PASS_END(DXILPrepareModule, DEBUG_TYPE, "DXIL Prepare Module", false,
320 false)
321
323 return new DXILPrepareModule();
324}
#define DEBUG_TYPE
Hexagon Common GEP
Module.h This file contains the declarations for the Module class.
#define F(x, y, z)
Definition MD5.cpp:55
#define I(x, y, z)
Definition MD5.cpp:58
#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
This file contains some templates that are useful if you are working with the STL at all.
This file defines the SmallVector class.
StringSet - A set-like wrapper for the StringMap.
Defines the llvm::VersionTuple class, which represents a version in the form major[....
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
This class stores enough information to efficiently remove some attributes from an existing AttrBuild...
AttributeMask & addAttribute(Attribute::AttrKind Val)
Add an attribute to the mask.
This class holds the attributes for a particular argument, parameter, function, or return value.
Definition Attributes.h:361
AttrKind
This enumeration lists the attributes that can be associated with parameters, function results,...
Definition Attributes.h:88
@ None
No attributes have been set.
Definition Attributes.h:90
@ EndAttrKinds
Sentinel value useful for loops.
Definition Attributes.h:93
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 ...
A constant value that is initialized with an expression using other constant values.
Definition Constants.h:1120
iterator find(const_arg_type_t< KeyT > Val)
Definition DenseMap.h:165
iterator end()
Definition DenseMap.h:81
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2780
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition Pass.h:255
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:67
@ AppendUnique
Appends the two values, which are required to be metadata nodes.
Definition Module.h:146
@ Warning
Emits a warning if two values disagree.
Definition Module.h:124
A tuple of MDNodes.
Definition Metadata.h:1753
LLVM_ABI void eraseFromParent()
Drop all references and remove the node from parent module.
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
StringSet - A wrapper for StringMap that provides set-like functionality.
Definition StringSet.h:25
bool contains(StringRef key) const
Check if the set contains the given key.
Definition StringSet.h:60
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:45
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:256
Represents a version number in the form major[.minor[.subminor[.build]]].
unsigned getMajor() const
Retrieve the major version number.
std::optional< unsigned > getMinor() const
Retrieve the minor version number, if provided.
Wrapper pass for the legacy pass manager.
Wrapper pass for the legacy pass manager.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
PointerTypeMap run(const Module &M)
Compute the PointerTypeMap for the module M.
DenseMap< const Value *, Type * > PointerTypeMap
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:649
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:646
ModulePass * createDXILPrepareModulePass()
Pass to convert modules into DXIL-compatable modules.
LLVM_ATTRIBUTE_VISIBILITY_DEFAULT AnalysisKey InnerAnalysisManagerProxy< AnalysisManagerT, IRUnitT, ExtraArgTs... >::Key
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:565
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Definition STLExtras.h:1899