/////////////////////////////////////////////////////////////////////////////// // // // DxilPreparePasses.cpp // // Copyright (C) Microsoft Corporation. All rights reserved. // // This file is distributed under the University of Illinois Open Source // // License. See LICENSE.TXT for details. // // // // Passes to prepare DxilModule. // // // /////////////////////////////////////////////////////////////////////////////// #include "dxc/HLSL/DxilGenerationPass.h" #include "dxc/HLSL/DxilOperations.h" #include "dxc/HLSL/DxilModule.h" #include "dxc/Support/Global.h" #include "dxc/HLSL/DxilTypeSystem.h" #include "dxc/HLSL/DxilUtil.h" #include "dxc/HLSL/DxilFunctionProps.h" #include "llvm/IR/GetElementPtrTypeIterator.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/PassManager.h" #include "llvm/ADT/BitVector.h" #include "llvm/Pass.h" #include "llvm/Transforms/Utils/Local.h" #include "llvm/Analysis/AssumptionCache.h" #include #include using namespace llvm; using namespace hlsl; namespace { class SimplifyInst : public FunctionPass { public: static char ID; SimplifyInst() : FunctionPass(ID) { initializeScalarizerPass(*PassRegistry::getPassRegistry()); } bool runOnFunction(Function &F) override; private: }; } char SimplifyInst::ID = 0; FunctionPass *llvm::createSimplifyInstPass() { return new SimplifyInst(); } INITIALIZE_PASS(SimplifyInst, "simplify-inst", "Simplify Instructions", false, false) bool SimplifyInst::runOnFunction(Function &F) { for (Function::iterator BBI = F.begin(), BBE = F.end(); BBI != BBE; ++BBI) { BasicBlock *BB = BBI; llvm::SimplifyInstructionsInBlock(BB, nullptr); } return true; } /////////////////////////////////////////////////////////////////////////////// namespace { class DxilLoadMetadata : public ModulePass { public: static char ID; // Pass identification, replacement for typeid explicit DxilLoadMetadata () : ModulePass(ID) {} const char *getPassName() const override { return "HLSL load DxilModule from metadata"; } bool runOnModule(Module &M) override { if (!M.HasDxilModule()) { (void)M.GetOrCreateDxilModule(); return true; } return false; } }; } char DxilLoadMetadata::ID = 0; ModulePass *llvm::createDxilLoadMetadataPass() { return new DxilLoadMetadata(); } INITIALIZE_PASS(DxilLoadMetadata, "hlsl-dxilload", "HLSL load DxilModule from metadata", false, false) /////////////////////////////////////////////////////////////////////////////// namespace { class DxilDeadFunctionElimination : public ModulePass { public: static char ID; // Pass identification, replacement for typeid explicit DxilDeadFunctionElimination () : ModulePass(ID) {} const char *getPassName() const override { return "Remove all unused function except entry from DxilModule"; } bool runOnModule(Module &M) override { if (M.HasDxilModule()) { DxilModule &DM = M.GetDxilModule(); bool IsLib = DM.GetShaderModel()->IsLib(); // Remove unused functions except entry and patch constant func. // For library profile, only remove unused external functions. Function *EntryFunc = DM.GetEntryFunction(); Function *PatchConstantFunc = DM.GetPatchConstantFunction(); return dxilutil::RemoveUnusedFunctions(M, EntryFunc, PatchConstantFunc, IsLib); } return false; } }; } char DxilDeadFunctionElimination::ID = 0; ModulePass *llvm::createDxilDeadFunctionEliminationPass() { return new DxilDeadFunctionElimination(); } INITIALIZE_PASS(DxilDeadFunctionElimination, "dxil-dfe", "Remove all unused function except entry from DxilModule", false, false) /////////////////////////////////////////////////////////////////////////////// namespace { Function *StripFunctionParameter(Function *F, DxilModule &DM, DenseMap &FunctionDIs) { Module &M = *DM.GetModule(); Type *VoidTy = Type::getVoidTy(M.getContext()); FunctionType *FT = FunctionType::get(VoidTy, false); for (auto &arg : F->args()) { if (!arg.user_empty()) return nullptr; DbgDeclareInst *DDI = llvm::FindAllocaDbgDeclare(&arg); if (DDI) { DDI->eraseFromParent(); } } Function *NewFunc = Function::Create(FT, F->getLinkage()); M.getFunctionList().insert(F, NewFunc); // Splice the body of the old function right into the new function. NewFunc->getBasicBlockList().splice(NewFunc->begin(), F->getBasicBlockList()); // Keep necessary function attributes AttributeSet attributeSet = F->getAttributes(); if (attributeSet.hasAttribute(AttributeSet::FunctionIndex, DXIL::kFP32DenormKindString)) { Attribute attribute = attributeSet.getAttribute(AttributeSet::FunctionIndex, DXIL::kFP32DenormKindString); DXASSERT(attribute.isStringAttribute(), "otherwise we have wrong fp-denorm-mode attribute."); NewFunc->addFnAttr(attribute.getKindAsString(), attribute.getValueAsString()); } // Patch the pointer to LLVM function in debug info descriptor. auto DI = FunctionDIs.find(F); if (DI != FunctionDIs.end()) { DISubprogram *SP = DI->second; SP->replaceFunction(NewFunc); // Ensure the map is updated so it can be reused on subsequent argument // promotions of the same function. FunctionDIs.erase(DI); FunctionDIs[NewFunc] = SP; } NewFunc->takeName(F); if (DM.HasDxilFunctionProps(F)) { DM.ReplaceDxilEntrySignature(F, NewFunc); DM.ReplaceDxilFunctionProps(F, NewFunc); } DM.GetTypeSystem().EraseFunctionAnnotation(F); F->eraseFromParent(); DM.GetTypeSystem().AddFunctionAnnotation(NewFunc); return NewFunc; } void CheckInBoundForTGSM(GlobalVariable &GV, const DataLayout &DL) { for (User *U : GV.users()) { if (GetElementPtrInst *GEP = dyn_cast(U)) { bool allImmIndex = true; for (auto Idx = GEP->idx_begin(), E = GEP->idx_end(); Idx != E; Idx++) { if (!isa(Idx)) { allImmIndex = false; break; } } if (!allImmIndex) GEP->setIsInBounds(false); else { Value *Ptr = GEP->getPointerOperand(); unsigned size = DL.getTypeAllocSize(Ptr->getType()->getPointerElementType()); unsigned valSize = DL.getTypeAllocSize(GEP->getType()->getPointerElementType()); SmallVector Indices(GEP->idx_begin(), GEP->idx_end()); unsigned offset = DL.getIndexedOffset(GEP->getPointerOperandType(), Indices); if ((offset + valSize) > size) GEP->setIsInBounds(false); } } } } class DxilFinalizeModule : public ModulePass { public: static char ID; // Pass identification, replacement for typeid explicit DxilFinalizeModule() : ModulePass(ID) {} const char *getPassName() const override { return "HLSL DXIL Finalize Module"; } void patchValidation_1_1(Module &M) { for (iplist::iterator F : M.getFunctionList()) { for (Function::iterator BBI = F->begin(), BBE = F->end(); BBI != BBE; ++BBI) { BasicBlock *BB = BBI; for (BasicBlock::iterator II = BB->begin(), IE = BB->end(); II != IE; ++II) { Instruction *I = II; if (I->hasMetadataOtherThanDebugLoc()) { SmallVector, 2> MDs; I->getAllMetadataOtherThanDebugLoc(MDs); for (auto &MD : MDs) { unsigned kind = MD.first; // Remove Metadata which validation_1_0 not allowed. bool bNeedPatch = kind == LLVMContext::MD_tbaa || kind == LLVMContext::MD_prof || (kind > LLVMContext::MD_fpmath && kind <= LLVMContext::MD_dereferenceable_or_null); if (bNeedPatch) I->setMetadata(kind, nullptr); } } } } } } bool runOnModule(Module &M) override { if (M.HasDxilModule()) { DxilModule &DM = M.GetDxilModule(); bool IsLib = DM.GetShaderModel()->IsLib(); // Skip validation patch for lib. if (!IsLib) { unsigned ValMajor = 0; unsigned ValMinor = 0; M.GetDxilModule().GetValidatorVersion(ValMajor, ValMinor); if (ValMajor == 1 && ValMinor <= 1) { patchValidation_1_1(M); } } // Remove store undef output. hlsl::OP *hlslOP = M.GetDxilModule().GetOP(); RemoveStoreUndefOutput(M, hlslOP); RemoveUnusedStaticGlobal(M); // Clear inbound for GEP which has none-const index. LegalizeShareMemoryGEPInbound(M); // Strip parameters of entry function. StripEntryParameters(M, DM, IsLib); // Skip shader flag for library. if (!IsLib) { DM.CollectShaderFlags(); // Update flags to reflect any changes. // Update Validator Version DM.UpgradeToMinValidatorVersion(); } return true; } return false; } private: void RemoveUnusedStaticGlobal(Module &M) { // Remove unused internal global. std::vector staticGVs; for (GlobalVariable &GV : M.globals()) { if (dxilutil::IsStaticGlobal(&GV) || dxilutil::IsSharedMemoryGlobal(&GV)) { staticGVs.emplace_back(&GV); } } for (GlobalVariable *GV : staticGVs) { bool onlyStoreUse = true; for (User *user : GV->users()) { if (isa(user)) continue; if (isa(user) && user->user_empty()) continue; onlyStoreUse = false; break; } if (onlyStoreUse) { for (auto UserIt = GV->user_begin(); UserIt != GV->user_end();) { Value *User = *(UserIt++); if (Instruction *I = dyn_cast(User)) { I->eraseFromParent(); } else { ConstantExpr *CE = cast(User); CE->dropAllReferences(); } } GV->eraseFromParent(); } } } void RemoveStoreUndefOutput(Module &M, hlsl::OP *hlslOP) { for (iplist::iterator F : M.getFunctionList()) { if (!hlslOP->IsDxilOpFunc(F)) continue; DXIL::OpCodeClass opClass; bool bHasOpClass = hlslOP->GetOpCodeClass(F, opClass); DXASSERT_LOCALVAR(bHasOpClass, bHasOpClass, "else not a dxil op func"); if (opClass != DXIL::OpCodeClass::StoreOutput) continue; for (auto it = F->user_begin(); it != F->user_end();) { CallInst *CI = dyn_cast(*(it++)); if (!CI) continue; Value *V = CI->getArgOperand(DXIL::OperandIndex::kStoreOutputValOpIdx); // Remove the store of undef. if (isa(V)) CI->eraseFromParent(); } } } void LegalizeShareMemoryGEPInbound(Module &M) { const DataLayout &DL = M.getDataLayout(); // Clear inbound for GEP which has none-const index. for (GlobalVariable &GV : M.globals()) { if (dxilutil::IsSharedMemoryGlobal(&GV)) { CheckInBoundForTGSM(GV, DL); } } } void StripEntryParameters(Module &M, DxilModule &DM, bool IsLib) { DenseMap FunctionDIs = makeSubprogramMap(M); // Strip parameters of entry function. if (!IsLib) { if (Function *PatchConstantFunc = DM.GetPatchConstantFunction()) { PatchConstantFunc = StripFunctionParameter(PatchConstantFunc, DM, FunctionDIs); if (PatchConstantFunc) DM.SetPatchConstantFunction(PatchConstantFunc); } if (Function *EntryFunc = DM.GetEntryFunction()) { StringRef Name = DM.GetEntryFunctionName(); EntryFunc->setName(Name); EntryFunc = StripFunctionParameter(EntryFunc, DM, FunctionDIs); if (EntryFunc) DM.SetEntryFunction(EntryFunc); } } else { std::vector entries; for (iplist::iterator F : M.getFunctionList()) { if (DM.HasDxilFunctionProps(F)) { entries.emplace_back(F); } } for (Function *entry : entries) { DxilFunctionProps &props = DM.GetDxilFunctionProps(entry); if (props.IsHS()) { // Strip patch constant function first. Function *patchConstFunc = StripFunctionParameter( props.ShaderProps.HS.patchConstantFunc, DM, FunctionDIs); props.ShaderProps.HS.patchConstantFunc = patchConstFunc; } StripFunctionParameter(entry, DM, FunctionDIs); } } } }; } char DxilFinalizeModule::ID = 0; ModulePass *llvm::createDxilFinalizeModulePass() { return new DxilFinalizeModule(); } INITIALIZE_PASS(DxilFinalizeModule, "hlsl-dxilfinalize", "HLSL DXIL Finalize Module", false, false) /////////////////////////////////////////////////////////////////////////////// namespace { class DxilEmitMetadata : public ModulePass { public: static char ID; // Pass identification, replacement for typeid explicit DxilEmitMetadata() : ModulePass(ID) {} const char *getPassName() const override { return "HLSL DXIL Metadata Emit"; } bool runOnModule(Module &M) override { if (M.HasDxilModule()) { DxilModule::ClearDxilMetadata(M); patchIsFrontfaceTy(M); M.GetDxilModule().EmitDxilMetadata(); return true; } return false; } private: void patchIsFrontfaceTy(Module &M); }; void patchIsFrontface(DxilSignatureElement &Elt, bool bForceUint) { // If force to uint, change i1 to u32. // If not force to uint, change u32 to i1. if (bForceUint && Elt.GetCompType() == CompType::Kind::I1) Elt.SetCompType(CompType::Kind::U32); else if (!bForceUint && Elt.GetCompType() == CompType::Kind::U32) Elt.SetCompType(CompType::Kind::I1); } void patchIsFrontface(DxilSignature &sig, bool bForceUint) { for (auto &Elt : sig.GetElements()) { if (Elt->GetSemantic()->GetKind() == Semantic::Kind::IsFrontFace) { patchIsFrontface(*Elt, bForceUint); } } } void DxilEmitMetadata::patchIsFrontfaceTy(Module &M) { DxilModule &DM = M.GetDxilModule(); const ShaderModel *pSM = DM.GetShaderModel(); if (!pSM->IsGS() && !pSM->IsPS()) return; unsigned ValMajor, ValMinor; DM.GetValidatorVersion(ValMajor, ValMinor); bool bForceUint = ValMajor >= 1 && ValMinor >= 2; if (pSM->IsPS()) { patchIsFrontface(DM.GetInputSignature(), bForceUint); } else if (pSM->IsGS()) { patchIsFrontface(DM.GetOutputSignature(), bForceUint); } } } char DxilEmitMetadata::ID = 0; ModulePass *llvm::createDxilEmitMetadataPass() { return new DxilEmitMetadata(); } INITIALIZE_PASS(DxilEmitMetadata, "hlsl-dxilemit", "HLSL DXIL Metadata Emit", false, false)