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 "DXILMetadata.h" 15 #include "DXILResourceAnalysis.h" 16 #include "DXILShaderFlags.h" 17 #include "DirectX.h" 18 #include "DirectXIRPasses/PointerTypeAnalysis.h" 19 #include "llvm/ADT/STLExtras.h" 20 #include "llvm/ADT/SmallVector.h" 21 #include "llvm/ADT/StringSet.h" 22 #include "llvm/CodeGen/Passes.h" 23 #include "llvm/IR/AttributeMask.h" 24 #include "llvm/IR/IRBuilder.h" 25 #include "llvm/IR/Instruction.h" 26 #include "llvm/IR/Module.h" 27 #include "llvm/InitializePasses.h" 28 #include "llvm/Pass.h" 29 #include "llvm/Support/Compiler.h" 30 #include "llvm/Support/VersionTuple.h" 31 32 #define DEBUG_TYPE "dxil-prepare" 33 34 using namespace llvm; 35 using namespace llvm::dxil; 36 37 namespace { 38 39 constexpr bool isValidForDXIL(Attribute::AttrKind Attr) { 40 return is_contained({Attribute::Alignment, 41 Attribute::AlwaysInline, 42 Attribute::Builtin, 43 Attribute::ByVal, 44 Attribute::InAlloca, 45 Attribute::Cold, 46 Attribute::Convergent, 47 Attribute::InlineHint, 48 Attribute::InReg, 49 Attribute::JumpTable, 50 Attribute::MinSize, 51 Attribute::Naked, 52 Attribute::Nest, 53 Attribute::NoAlias, 54 Attribute::NoBuiltin, 55 Attribute::NoCapture, 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 88 static 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 103 static 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 119 static void cleanModuleFlags(Module &M) { 120 NamedMDNode *MDFlags = M.getModuleFlagsMetadata(); 121 if (!MDFlags) 122 return; 123 124 SmallVector<llvm::Module::ModuleFlagEntry> FlagEntries; 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 144 class 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 if (cast<TypedPointerType>(It->second)->getElementType() == Ty) 154 return nullptr; 155 // Insert bitcasts where we are removing the instruction. 156 Builder.SetInsertPoint(&Inst); 157 // This code only gets hit in opaque-pointer mode, so the type of the 158 // pointer doesn't matter. 159 PointerType *PtrTy = cast<PointerType>(Operand->getType()); 160 return Builder.Insert( 161 CastInst::Create(Instruction::BitCast, Operand, 162 Builder.getPtrTy(PtrTy->getAddressSpace()))); 163 } 164 165 public: 166 bool runOnModule(Module &M) override { 167 PointerTypeMap PointerTypes = PointerTypeAnalysis::run(M); 168 AttributeMask AttrMask; 169 for (Attribute::AttrKind I = Attribute::None; I != Attribute::EndAttrKinds; 170 I = Attribute::AttrKind(I + 1)) { 171 if (!isValidForDXIL(I)) 172 AttrMask.addAttribute(I); 173 } 174 175 dxil::ValidatorVersionMD ValVerMD(M); 176 VersionTuple ValVer = ValVerMD.getAsVersionTuple(); 177 bool SkipValidation = ValVer.getMajor() == 0 && ValVer.getMinor() == 0; 178 179 for (auto &F : M.functions()) { 180 F.removeFnAttrs(AttrMask); 181 F.removeRetAttrs(AttrMask); 182 // Only remove string attributes if we are not skipping validation. 183 // This will reserve the experimental attributes when validation version 184 // is 0.0 for experiment mode. 185 removeStringFunctionAttributes(F, SkipValidation); 186 for (size_t Idx = 0, End = F.arg_size(); Idx < End; ++Idx) 187 F.removeParamAttrs(Idx, AttrMask); 188 189 for (auto &BB : F) { 190 IRBuilder<> Builder(&BB); 191 for (auto &I : make_early_inc_range(BB)) { 192 if (I.getOpcode() == Instruction::FNeg) { 193 Builder.SetInsertPoint(&I); 194 Value *In = I.getOperand(0); 195 Value *Zero = ConstantFP::get(In->getType(), -0.0); 196 I.replaceAllUsesWith(Builder.CreateFSub(Zero, In)); 197 I.eraseFromParent(); 198 continue; 199 } 200 201 // Emtting NoOp bitcast instructions allows the ValueEnumerator to be 202 // unmodified as it reserves instruction IDs during contruction. 203 if (auto LI = dyn_cast<LoadInst>(&I)) { 204 if (Value *NoOpBitcast = maybeGenerateBitcast( 205 Builder, PointerTypes, I, LI->getPointerOperand(), 206 LI->getType())) { 207 LI->replaceAllUsesWith( 208 Builder.CreateLoad(LI->getType(), NoOpBitcast)); 209 LI->eraseFromParent(); 210 } 211 continue; 212 } 213 if (auto SI = dyn_cast<StoreInst>(&I)) { 214 if (Value *NoOpBitcast = maybeGenerateBitcast( 215 Builder, PointerTypes, I, SI->getPointerOperand(), 216 SI->getValueOperand()->getType())) { 217 218 SI->replaceAllUsesWith( 219 Builder.CreateStore(SI->getValueOperand(), NoOpBitcast)); 220 SI->eraseFromParent(); 221 } 222 continue; 223 } 224 if (auto GEP = dyn_cast<GetElementPtrInst>(&I)) { 225 if (Value *NoOpBitcast = maybeGenerateBitcast( 226 Builder, PointerTypes, I, GEP->getPointerOperand(), 227 GEP->getSourceElementType())) 228 GEP->setOperand(0, NoOpBitcast); 229 continue; 230 } 231 if (auto *CB = dyn_cast<CallBase>(&I)) { 232 CB->removeFnAttrs(AttrMask); 233 CB->removeRetAttrs(AttrMask); 234 for (size_t Idx = 0, End = CB->arg_size(); Idx < End; ++Idx) 235 CB->removeParamAttrs(Idx, AttrMask); 236 continue; 237 } 238 } 239 } 240 } 241 // Remove flags not for DXIL. 242 cleanModuleFlags(M); 243 return true; 244 } 245 246 DXILPrepareModule() : ModulePass(ID) {} 247 void getAnalysisUsage(AnalysisUsage &AU) const override { 248 AU.addPreserved<ShaderFlagsAnalysisWrapper>(); 249 AU.addPreserved<DXILResourceWrapper>(); 250 } 251 static char ID; // Pass identification. 252 }; 253 char DXILPrepareModule::ID = 0; 254 255 } // end anonymous namespace 256 257 INITIALIZE_PASS_BEGIN(DXILPrepareModule, DEBUG_TYPE, "DXIL Prepare Module", 258 false, false) 259 INITIALIZE_PASS_END(DXILPrepareModule, DEBUG_TYPE, "DXIL Prepare Module", false, 260 false) 261 262 ModulePass *llvm::createDXILPrepareModulePass() { 263 return new DXILPrepareModule(); 264 } 265