276 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file This file contains passes and utilities to convert a modern LLVM10/// module into a module compatible with the LLVM 3.7-based DirectX Intermediate11/// Language (DXIL).12//===----------------------------------------------------------------------===//13 14#include "DXILRootSignature.h"15#include "DXILShaderFlags.h"16#include "DirectX.h"17#include "DirectXIRPasses/PointerTypeAnalysis.h"18#include "llvm/ADT/STLExtras.h"19#include "llvm/ADT/StringSet.h"20#include "llvm/Analysis/DXILMetadataAnalysis.h"21#include "llvm/Analysis/DXILResource.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/VersionTuple.h"30 31#define DEBUG_TYPE "dxil-prepare"32 33using namespace llvm;34using namespace llvm::dxil;35 36namespace {37 38constexpr bool isValidForDXIL(Attribute::AttrKind Attr) {39 return is_contained({Attribute::Alignment,40 Attribute::AlwaysInline,41 Attribute::Builtin,42 Attribute::ByVal,43 Attribute::InAlloca,44 Attribute::Cold,45 Attribute::Convergent,46 Attribute::InlineHint,47 Attribute::InReg,48 Attribute::JumpTable,49 Attribute::MinSize,50 Attribute::Naked,51 Attribute::Nest,52 Attribute::NoAlias,53 Attribute::NoBuiltin,54 Attribute::NoDuplicate,55 Attribute::NoImplicitFloat,56 Attribute::NoInline,57 Attribute::NonLazyBind,58 Attribute::NonNull,59 Attribute::Dereferenceable,60 Attribute::DereferenceableOrNull,61 Attribute::Memory,62 Attribute::NoRedZone,63 Attribute::NoReturn,64 Attribute::NoUnwind,65 Attribute::OptimizeForSize,66 Attribute::OptimizeNone,67 Attribute::ReadNone,68 Attribute::ReadOnly,69 Attribute::Returned,70 Attribute::ReturnsTwice,71 Attribute::SExt,72 Attribute::StackAlignment,73 Attribute::StackProtect,74 Attribute::StackProtectReq,75 Attribute::StackProtectStrong,76 Attribute::SafeStack,77 Attribute::StructRet,78 Attribute::SanitizeAddress,79 Attribute::SanitizeThread,80 Attribute::SanitizeMemory,81 Attribute::UWTable,82 Attribute::ZExt},83 Attr);84}85 86static void collectDeadStringAttrs(AttributeMask &DeadAttrs, AttributeSet &&AS,87 const StringSet<> &LiveKeys,88 bool AllowExperimental) {89 for (auto &Attr : AS) {90 if (!Attr.isStringAttribute())91 continue;92 StringRef Key = Attr.getKindAsString();93 if (LiveKeys.contains(Key))94 continue;95 if (AllowExperimental && Key.starts_with("exp-"))96 continue;97 DeadAttrs.addAttribute(Key);98 }99}100 101static void removeStringFunctionAttributes(Function &F,102 bool AllowExperimental) {103 AttributeList Attrs = F.getAttributes();104 const StringSet<> LiveKeys = {"waveops-include-helper-lanes",105 "fp32-denorm-mode"};106 // Collect DeadKeys in FnAttrs.107 AttributeMask DeadAttrs;108 collectDeadStringAttrs(DeadAttrs, Attrs.getFnAttrs(), LiveKeys,109 AllowExperimental);110 collectDeadStringAttrs(DeadAttrs, Attrs.getRetAttrs(), LiveKeys,111 AllowExperimental);112 113 F.removeFnAttrs(DeadAttrs);114 F.removeRetAttrs(DeadAttrs);115}116 117class DXILPrepareModule : public ModulePass {118 119 static Value *maybeGenerateBitcast(IRBuilder<> &Builder,120 PointerTypeMap &PointerTypes,121 Instruction &Inst, Value *Operand,122 Type *Ty) {123 // Omit bitcasts if the incoming value matches the instruction type.124 auto It = PointerTypes.find(Operand);125 if (It != PointerTypes.end()) {126 auto *OpTy = cast<TypedPointerType>(It->second)->getElementType();127 if (OpTy == Ty)128 return nullptr;129 }130 131 Type *ValTy = Operand->getType();132 // Also omit the bitcast for matching global array types133 if (auto *GlobalVar = dyn_cast<GlobalVariable>(Operand))134 ValTy = GlobalVar->getValueType();135 136 if (auto *AI = dyn_cast<AllocaInst>(Operand))137 ValTy = AI->getAllocatedType();138 139 if (auto *ArrTy = dyn_cast<ArrayType>(ValTy)) {140 Type *ElTy = ArrTy->getElementType();141 if (ElTy == Ty)142 return nullptr;143 }144 145 // finally, drill down GEP instructions until we get the array146 // that is being accessed, and compare element types147 if (ConstantExpr *GEPInstr = dyn_cast<ConstantExpr>(Operand)) {148 while (GEPInstr->getOpcode() == Instruction::GetElementPtr) {149 Value *OpArg = GEPInstr->getOperand(0);150 if (ConstantExpr *NewGEPInstr = dyn_cast<ConstantExpr>(OpArg)) {151 GEPInstr = NewGEPInstr;152 continue;153 }154 155 if (auto *GlobalVar = dyn_cast<GlobalVariable>(OpArg))156 ValTy = GlobalVar->getValueType();157 if (auto *AI = dyn_cast<AllocaInst>(Operand))158 ValTy = AI->getAllocatedType();159 if (auto *ArrTy = dyn_cast<ArrayType>(ValTy)) {160 Type *ElTy = ArrTy->getElementType();161 if (ElTy == Ty)162 return nullptr;163 }164 break;165 }166 }167 168 // Insert bitcasts where we are removing the instruction.169 Builder.SetInsertPoint(&Inst);170 // This code only gets hit in opaque-pointer mode, so the type of the171 // pointer doesn't matter.172 PointerType *PtrTy = cast<PointerType>(Operand->getType());173 return Builder.Insert(174 CastInst::Create(Instruction::BitCast, Operand,175 Builder.getPtrTy(PtrTy->getAddressSpace())));176 }177 178public:179 bool runOnModule(Module &M) override {180 PointerTypeMap PointerTypes = PointerTypeAnalysis::run(M);181 AttributeMask AttrMask;182 for (Attribute::AttrKind I = Attribute::None; I != Attribute::EndAttrKinds;183 I = Attribute::AttrKind(I + 1)) {184 if (!isValidForDXIL(I))185 AttrMask.addAttribute(I);186 }187 188 const dxil::ModuleMetadataInfo MetadataInfo =189 getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();190 VersionTuple ValVer = MetadataInfo.ValidatorVersion;191 bool AllowExperimental = ValVer.getMajor() == 0 && ValVer.getMinor() == 0;192 193 for (auto &F : M.functions()) {194 F.removeFnAttrs(AttrMask);195 F.removeRetAttrs(AttrMask);196 // Only remove string attributes if we are not skipping validation.197 // This will reserve the experimental attributes when validation version198 // is 0.0 for experiment mode.199 removeStringFunctionAttributes(F, AllowExperimental);200 for (size_t Idx = 0, End = F.arg_size(); Idx < End; ++Idx)201 F.removeParamAttrs(Idx, AttrMask);202 203 for (auto &BB : F) {204 IRBuilder<> Builder(&BB);205 for (auto &I : make_early_inc_range(BB)) {206 207 if (auto *CB = dyn_cast<CallBase>(&I)) {208 CB->removeFnAttrs(AttrMask);209 CB->removeRetAttrs(AttrMask);210 for (size_t Idx = 0, End = CB->arg_size(); Idx < End; ++Idx)211 CB->removeParamAttrs(Idx, AttrMask);212 continue;213 }214 215 // Emtting NoOp bitcast instructions allows the ValueEnumerator to be216 // unmodified as it reserves instruction IDs during contruction.217 if (auto *LI = dyn_cast<LoadInst>(&I)) {218 if (Value *NoOpBitcast = maybeGenerateBitcast(219 Builder, PointerTypes, I, LI->getPointerOperand(),220 LI->getType())) {221 LI->replaceAllUsesWith(222 Builder.CreateLoad(LI->getType(), NoOpBitcast));223 LI->eraseFromParent();224 }225 continue;226 }227 if (auto *SI = dyn_cast<StoreInst>(&I)) {228 if (Value *NoOpBitcast = maybeGenerateBitcast(229 Builder, PointerTypes, I, SI->getPointerOperand(),230 SI->getValueOperand()->getType())) {231 232 SI->replaceAllUsesWith(233 Builder.CreateStore(SI->getValueOperand(), NoOpBitcast));234 SI->eraseFromParent();235 }236 continue;237 }238 if (auto *GEP = dyn_cast<GetElementPtrInst>(&I)) {239 if (Value *NoOpBitcast = maybeGenerateBitcast(240 Builder, PointerTypes, I, GEP->getPointerOperand(),241 GEP->getSourceElementType()))242 GEP->setOperand(0, NoOpBitcast);243 continue;244 }245 }246 }247 }248 249 return true;250 }251 252 DXILPrepareModule() : ModulePass(ID) {}253 void getAnalysisUsage(AnalysisUsage &AU) const override {254 AU.addRequired<DXILMetadataAnalysisWrapperPass>();255 256 AU.addPreserved<DXILMetadataAnalysisWrapperPass>();257 AU.addPreserved<DXILResourceWrapperPass>();258 AU.addPreserved<RootSignatureAnalysisWrapper>();259 AU.addPreserved<ShaderFlagsAnalysisWrapper>();260 }261 static char ID; // Pass identification.262};263char DXILPrepareModule::ID = 0;264 265} // end anonymous namespace266 267INITIALIZE_PASS_BEGIN(DXILPrepareModule, DEBUG_TYPE, "DXIL Prepare Module",268 false, false)269INITIALIZE_PASS_DEPENDENCY(DXILMetadataAnalysisWrapperPass)270INITIALIZE_PASS_END(DXILPrepareModule, DEBUG_TYPE, "DXIL Prepare Module", false,271 false)272 273ModulePass *llvm::createDXILPrepareModulePass() {274 return new DXILPrepareModule();275}276