181ad6265SDimitry Andric //===- DXILPrepare.cpp - Prepare LLVM Module for DXIL encoding ------------===// 281ad6265SDimitry Andric // 381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 681ad6265SDimitry Andric // 781ad6265SDimitry Andric //===----------------------------------------------------------------------===// 881ad6265SDimitry Andric /// 981ad6265SDimitry Andric /// \file This file contains pases and utilities to convert a modern LLVM 1081ad6265SDimitry Andric /// module into a module compatible with the LLVM 3.7-based DirectX Intermediate 1181ad6265SDimitry Andric /// Language (DXIL). 1281ad6265SDimitry Andric //===----------------------------------------------------------------------===// 1381ad6265SDimitry Andric 14*0fca6ea1SDimitry Andric #include "DXILMetadata.h" 15*0fca6ea1SDimitry Andric #include "DXILResourceAnalysis.h" 16*0fca6ea1SDimitry Andric #include "DXILShaderFlags.h" 1781ad6265SDimitry Andric #include "DirectX.h" 18bdd1243dSDimitry Andric #include "DirectXIRPasses/PointerTypeAnalysis.h" 1981ad6265SDimitry Andric #include "llvm/ADT/STLExtras.h" 2081ad6265SDimitry Andric #include "llvm/ADT/SmallVector.h" 21*0fca6ea1SDimitry Andric #include "llvm/ADT/StringSet.h" 2281ad6265SDimitry Andric #include "llvm/CodeGen/Passes.h" 2306c3fb27SDimitry Andric #include "llvm/IR/AttributeMask.h" 2481ad6265SDimitry Andric #include "llvm/IR/IRBuilder.h" 2581ad6265SDimitry Andric #include "llvm/IR/Instruction.h" 2681ad6265SDimitry Andric #include "llvm/IR/Module.h" 2781ad6265SDimitry Andric #include "llvm/InitializePasses.h" 2881ad6265SDimitry Andric #include "llvm/Pass.h" 2981ad6265SDimitry Andric #include "llvm/Support/Compiler.h" 30*0fca6ea1SDimitry Andric #include "llvm/Support/VersionTuple.h" 3181ad6265SDimitry Andric 3281ad6265SDimitry Andric #define DEBUG_TYPE "dxil-prepare" 3381ad6265SDimitry Andric 3481ad6265SDimitry Andric using namespace llvm; 3581ad6265SDimitry Andric using namespace llvm::dxil; 3681ad6265SDimitry Andric 3781ad6265SDimitry Andric namespace { 3881ad6265SDimitry Andric 3981ad6265SDimitry Andric constexpr bool isValidForDXIL(Attribute::AttrKind Attr) { 4081ad6265SDimitry Andric return is_contained({Attribute::Alignment, 4181ad6265SDimitry Andric Attribute::AlwaysInline, 4281ad6265SDimitry Andric Attribute::Builtin, 4381ad6265SDimitry Andric Attribute::ByVal, 4481ad6265SDimitry Andric Attribute::InAlloca, 4581ad6265SDimitry Andric Attribute::Cold, 4681ad6265SDimitry Andric Attribute::Convergent, 4781ad6265SDimitry Andric Attribute::InlineHint, 4881ad6265SDimitry Andric Attribute::InReg, 4981ad6265SDimitry Andric Attribute::JumpTable, 5081ad6265SDimitry Andric Attribute::MinSize, 5181ad6265SDimitry Andric Attribute::Naked, 5281ad6265SDimitry Andric Attribute::Nest, 5381ad6265SDimitry Andric Attribute::NoAlias, 5481ad6265SDimitry Andric Attribute::NoBuiltin, 5581ad6265SDimitry Andric Attribute::NoCapture, 5681ad6265SDimitry Andric Attribute::NoDuplicate, 5781ad6265SDimitry Andric Attribute::NoImplicitFloat, 5881ad6265SDimitry Andric Attribute::NoInline, 5981ad6265SDimitry Andric Attribute::NonLazyBind, 6081ad6265SDimitry Andric Attribute::NonNull, 6181ad6265SDimitry Andric Attribute::Dereferenceable, 6281ad6265SDimitry Andric Attribute::DereferenceableOrNull, 63bdd1243dSDimitry Andric Attribute::Memory, 6481ad6265SDimitry Andric Attribute::NoRedZone, 6581ad6265SDimitry Andric Attribute::NoReturn, 6681ad6265SDimitry Andric Attribute::NoUnwind, 6781ad6265SDimitry Andric Attribute::OptimizeForSize, 6881ad6265SDimitry Andric Attribute::OptimizeNone, 6981ad6265SDimitry Andric Attribute::ReadNone, 7081ad6265SDimitry Andric Attribute::ReadOnly, 7181ad6265SDimitry Andric Attribute::Returned, 7281ad6265SDimitry Andric Attribute::ReturnsTwice, 7381ad6265SDimitry Andric Attribute::SExt, 7481ad6265SDimitry Andric Attribute::StackAlignment, 7581ad6265SDimitry Andric Attribute::StackProtect, 7681ad6265SDimitry Andric Attribute::StackProtectReq, 7781ad6265SDimitry Andric Attribute::StackProtectStrong, 7881ad6265SDimitry Andric Attribute::SafeStack, 7981ad6265SDimitry Andric Attribute::StructRet, 8081ad6265SDimitry Andric Attribute::SanitizeAddress, 8181ad6265SDimitry Andric Attribute::SanitizeThread, 8281ad6265SDimitry Andric Attribute::SanitizeMemory, 8381ad6265SDimitry Andric Attribute::UWTable, 8481ad6265SDimitry Andric Attribute::ZExt}, 8581ad6265SDimitry Andric Attr); 8681ad6265SDimitry Andric } 8781ad6265SDimitry Andric 88*0fca6ea1SDimitry Andric static void collectDeadStringAttrs(AttributeMask &DeadAttrs, AttributeSet &&AS, 89*0fca6ea1SDimitry Andric const StringSet<> &LiveKeys, 90*0fca6ea1SDimitry Andric bool AllowExperimental) { 91*0fca6ea1SDimitry Andric for (auto &Attr : AS) { 92*0fca6ea1SDimitry Andric if (!Attr.isStringAttribute()) 93*0fca6ea1SDimitry Andric continue; 94*0fca6ea1SDimitry Andric StringRef Key = Attr.getKindAsString(); 95*0fca6ea1SDimitry Andric if (LiveKeys.contains(Key)) 96*0fca6ea1SDimitry Andric continue; 97*0fca6ea1SDimitry Andric if (AllowExperimental && Key.starts_with("exp-")) 98*0fca6ea1SDimitry Andric continue; 99*0fca6ea1SDimitry Andric DeadAttrs.addAttribute(Key); 100*0fca6ea1SDimitry Andric } 101*0fca6ea1SDimitry Andric } 102*0fca6ea1SDimitry Andric 103*0fca6ea1SDimitry Andric static void removeStringFunctionAttributes(Function &F, 104*0fca6ea1SDimitry Andric bool AllowExperimental) { 105*0fca6ea1SDimitry Andric AttributeList Attrs = F.getAttributes(); 106*0fca6ea1SDimitry Andric const StringSet<> LiveKeys = {"waveops-include-helper-lanes", 107*0fca6ea1SDimitry Andric "fp32-denorm-mode"}; 108*0fca6ea1SDimitry Andric // Collect DeadKeys in FnAttrs. 109*0fca6ea1SDimitry Andric AttributeMask DeadAttrs; 110*0fca6ea1SDimitry Andric collectDeadStringAttrs(DeadAttrs, Attrs.getFnAttrs(), LiveKeys, 111*0fca6ea1SDimitry Andric AllowExperimental); 112*0fca6ea1SDimitry Andric collectDeadStringAttrs(DeadAttrs, Attrs.getRetAttrs(), LiveKeys, 113*0fca6ea1SDimitry Andric AllowExperimental); 114*0fca6ea1SDimitry Andric 115*0fca6ea1SDimitry Andric F.removeFnAttrs(DeadAttrs); 116*0fca6ea1SDimitry Andric F.removeRetAttrs(DeadAttrs); 117*0fca6ea1SDimitry Andric } 118*0fca6ea1SDimitry Andric 119*0fca6ea1SDimitry Andric static void cleanModuleFlags(Module &M) { 120*0fca6ea1SDimitry Andric NamedMDNode *MDFlags = M.getModuleFlagsMetadata(); 121*0fca6ea1SDimitry Andric if (!MDFlags) 122*0fca6ea1SDimitry Andric return; 123*0fca6ea1SDimitry Andric 124*0fca6ea1SDimitry Andric SmallVector<llvm::Module::ModuleFlagEntry> FlagEntries; 125*0fca6ea1SDimitry Andric M.getModuleFlagsMetadata(FlagEntries); 126*0fca6ea1SDimitry Andric bool Updated = false; 127*0fca6ea1SDimitry Andric for (auto &Flag : FlagEntries) { 128*0fca6ea1SDimitry Andric // llvm 3.7 only supports behavior up to AppendUnique. 129*0fca6ea1SDimitry Andric if (Flag.Behavior <= Module::ModFlagBehavior::AppendUnique) 130*0fca6ea1SDimitry Andric continue; 131*0fca6ea1SDimitry Andric Flag.Behavior = Module::ModFlagBehavior::Warning; 132*0fca6ea1SDimitry Andric Updated = true; 133*0fca6ea1SDimitry Andric } 134*0fca6ea1SDimitry Andric 135*0fca6ea1SDimitry Andric if (!Updated) 136*0fca6ea1SDimitry Andric return; 137*0fca6ea1SDimitry Andric 138*0fca6ea1SDimitry Andric MDFlags->eraseFromParent(); 139*0fca6ea1SDimitry Andric 140*0fca6ea1SDimitry Andric for (auto &Flag : FlagEntries) 141*0fca6ea1SDimitry Andric M.addModuleFlag(Flag.Behavior, Flag.Key->getString(), Flag.Val); 142*0fca6ea1SDimitry Andric } 143*0fca6ea1SDimitry Andric 14481ad6265SDimitry Andric class DXILPrepareModule : public ModulePass { 14581ad6265SDimitry Andric 14681ad6265SDimitry Andric static Value *maybeGenerateBitcast(IRBuilder<> &Builder, 14781ad6265SDimitry Andric PointerTypeMap &PointerTypes, 14881ad6265SDimitry Andric Instruction &Inst, Value *Operand, 14981ad6265SDimitry Andric Type *Ty) { 15081ad6265SDimitry Andric // Omit bitcasts if the incoming value matches the instruction type. 15181ad6265SDimitry Andric auto It = PointerTypes.find(Operand); 15281ad6265SDimitry Andric if (It != PointerTypes.end()) 15381ad6265SDimitry Andric if (cast<TypedPointerType>(It->second)->getElementType() == Ty) 15481ad6265SDimitry Andric return nullptr; 15581ad6265SDimitry Andric // Insert bitcasts where we are removing the instruction. 15681ad6265SDimitry Andric Builder.SetInsertPoint(&Inst); 15781ad6265SDimitry Andric // This code only gets hit in opaque-pointer mode, so the type of the 15881ad6265SDimitry Andric // pointer doesn't matter. 15981ad6265SDimitry Andric PointerType *PtrTy = cast<PointerType>(Operand->getType()); 16081ad6265SDimitry Andric return Builder.Insert( 16181ad6265SDimitry Andric CastInst::Create(Instruction::BitCast, Operand, 1625f757f3fSDimitry Andric Builder.getPtrTy(PtrTy->getAddressSpace()))); 16381ad6265SDimitry Andric } 16481ad6265SDimitry Andric 16581ad6265SDimitry Andric public: 16681ad6265SDimitry Andric bool runOnModule(Module &M) override { 16781ad6265SDimitry Andric PointerTypeMap PointerTypes = PointerTypeAnalysis::run(M); 16881ad6265SDimitry Andric AttributeMask AttrMask; 16981ad6265SDimitry Andric for (Attribute::AttrKind I = Attribute::None; I != Attribute::EndAttrKinds; 17081ad6265SDimitry Andric I = Attribute::AttrKind(I + 1)) { 17181ad6265SDimitry Andric if (!isValidForDXIL(I)) 17281ad6265SDimitry Andric AttrMask.addAttribute(I); 17381ad6265SDimitry Andric } 174*0fca6ea1SDimitry Andric 175*0fca6ea1SDimitry Andric dxil::ValidatorVersionMD ValVerMD(M); 176*0fca6ea1SDimitry Andric VersionTuple ValVer = ValVerMD.getAsVersionTuple(); 177*0fca6ea1SDimitry Andric bool SkipValidation = ValVer.getMajor() == 0 && ValVer.getMinor() == 0; 178*0fca6ea1SDimitry Andric 17981ad6265SDimitry Andric for (auto &F : M.functions()) { 18081ad6265SDimitry Andric F.removeFnAttrs(AttrMask); 18181ad6265SDimitry Andric F.removeRetAttrs(AttrMask); 182*0fca6ea1SDimitry Andric // Only remove string attributes if we are not skipping validation. 183*0fca6ea1SDimitry Andric // This will reserve the experimental attributes when validation version 184*0fca6ea1SDimitry Andric // is 0.0 for experiment mode. 185*0fca6ea1SDimitry Andric removeStringFunctionAttributes(F, SkipValidation); 18681ad6265SDimitry Andric for (size_t Idx = 0, End = F.arg_size(); Idx < End; ++Idx) 18781ad6265SDimitry Andric F.removeParamAttrs(Idx, AttrMask); 18881ad6265SDimitry Andric 18981ad6265SDimitry Andric for (auto &BB : F) { 19081ad6265SDimitry Andric IRBuilder<> Builder(&BB); 19181ad6265SDimitry Andric for (auto &I : make_early_inc_range(BB)) { 19281ad6265SDimitry Andric if (I.getOpcode() == Instruction::FNeg) { 19381ad6265SDimitry Andric Builder.SetInsertPoint(&I); 19481ad6265SDimitry Andric Value *In = I.getOperand(0); 19581ad6265SDimitry Andric Value *Zero = ConstantFP::get(In->getType(), -0.0); 19681ad6265SDimitry Andric I.replaceAllUsesWith(Builder.CreateFSub(Zero, In)); 19781ad6265SDimitry Andric I.eraseFromParent(); 19881ad6265SDimitry Andric continue; 19981ad6265SDimitry Andric } 20081ad6265SDimitry Andric 20181ad6265SDimitry Andric // Emtting NoOp bitcast instructions allows the ValueEnumerator to be 20281ad6265SDimitry Andric // unmodified as it reserves instruction IDs during contruction. 20381ad6265SDimitry Andric if (auto LI = dyn_cast<LoadInst>(&I)) { 20481ad6265SDimitry Andric if (Value *NoOpBitcast = maybeGenerateBitcast( 20581ad6265SDimitry Andric Builder, PointerTypes, I, LI->getPointerOperand(), 20681ad6265SDimitry Andric LI->getType())) { 20781ad6265SDimitry Andric LI->replaceAllUsesWith( 20881ad6265SDimitry Andric Builder.CreateLoad(LI->getType(), NoOpBitcast)); 20981ad6265SDimitry Andric LI->eraseFromParent(); 21081ad6265SDimitry Andric } 21181ad6265SDimitry Andric continue; 21281ad6265SDimitry Andric } 21381ad6265SDimitry Andric if (auto SI = dyn_cast<StoreInst>(&I)) { 21481ad6265SDimitry Andric if (Value *NoOpBitcast = maybeGenerateBitcast( 21581ad6265SDimitry Andric Builder, PointerTypes, I, SI->getPointerOperand(), 21681ad6265SDimitry Andric SI->getValueOperand()->getType())) { 21781ad6265SDimitry Andric 21881ad6265SDimitry Andric SI->replaceAllUsesWith( 21981ad6265SDimitry Andric Builder.CreateStore(SI->getValueOperand(), NoOpBitcast)); 22081ad6265SDimitry Andric SI->eraseFromParent(); 22181ad6265SDimitry Andric } 22281ad6265SDimitry Andric continue; 22381ad6265SDimitry Andric } 22481ad6265SDimitry Andric if (auto GEP = dyn_cast<GetElementPtrInst>(&I)) { 22581ad6265SDimitry Andric if (Value *NoOpBitcast = maybeGenerateBitcast( 22681ad6265SDimitry Andric Builder, PointerTypes, I, GEP->getPointerOperand(), 2275f757f3fSDimitry Andric GEP->getSourceElementType())) 22881ad6265SDimitry Andric GEP->setOperand(0, NoOpBitcast); 22981ad6265SDimitry Andric continue; 23081ad6265SDimitry Andric } 231bdd1243dSDimitry Andric if (auto *CB = dyn_cast<CallBase>(&I)) { 232bdd1243dSDimitry Andric CB->removeFnAttrs(AttrMask); 233bdd1243dSDimitry Andric CB->removeRetAttrs(AttrMask); 234bdd1243dSDimitry Andric for (size_t Idx = 0, End = CB->arg_size(); Idx < End; ++Idx) 235bdd1243dSDimitry Andric CB->removeParamAttrs(Idx, AttrMask); 236bdd1243dSDimitry Andric continue; 237bdd1243dSDimitry Andric } 23881ad6265SDimitry Andric } 23981ad6265SDimitry Andric } 24081ad6265SDimitry Andric } 241*0fca6ea1SDimitry Andric // Remove flags not for DXIL. 242*0fca6ea1SDimitry Andric cleanModuleFlags(M); 24381ad6265SDimitry Andric return true; 24481ad6265SDimitry Andric } 24581ad6265SDimitry Andric 24681ad6265SDimitry Andric DXILPrepareModule() : ModulePass(ID) {} 247*0fca6ea1SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override { 248*0fca6ea1SDimitry Andric AU.addPreserved<ShaderFlagsAnalysisWrapper>(); 249*0fca6ea1SDimitry Andric AU.addPreserved<DXILResourceWrapper>(); 250*0fca6ea1SDimitry Andric } 25181ad6265SDimitry Andric static char ID; // Pass identification. 25281ad6265SDimitry Andric }; 25381ad6265SDimitry Andric char DXILPrepareModule::ID = 0; 25481ad6265SDimitry Andric 25581ad6265SDimitry Andric } // end anonymous namespace 25681ad6265SDimitry Andric 25781ad6265SDimitry Andric INITIALIZE_PASS_BEGIN(DXILPrepareModule, DEBUG_TYPE, "DXIL Prepare Module", 25881ad6265SDimitry Andric false, false) 25981ad6265SDimitry Andric INITIALIZE_PASS_END(DXILPrepareModule, DEBUG_TYPE, "DXIL Prepare Module", false, 26081ad6265SDimitry Andric false) 26181ad6265SDimitry Andric 26281ad6265SDimitry Andric ModulePass *llvm::createDXILPrepareModulePass() { 26381ad6265SDimitry Andric return new DXILPrepareModule(); 26481ad6265SDimitry Andric } 265