471 строка
15 KiB
C++
471 строка
15 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// 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 <memory>
|
|
#include <unordered_set>
|
|
|
|
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<const Function *, DISubprogram *> &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<GetElementPtrInst>(U)) {
|
|
bool allImmIndex = true;
|
|
for (auto Idx = GEP->idx_begin(), E = GEP->idx_end(); Idx != E; Idx++) {
|
|
if (!isa<ConstantInt>(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<Value *, 8> 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<Function>::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<std::pair<unsigned, MDNode*>, 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<GlobalVariable *> 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<StoreInst>(user))
|
|
continue;
|
|
if (isa<ConstantExpr>(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<Instruction>(User)) {
|
|
I->eraseFromParent();
|
|
} else {
|
|
ConstantExpr *CE = cast<ConstantExpr>(User);
|
|
CE->dropAllReferences();
|
|
}
|
|
}
|
|
GV->eraseFromParent();
|
|
}
|
|
}
|
|
}
|
|
|
|
void RemoveStoreUndefOutput(Module &M, hlsl::OP *hlslOP) {
|
|
for (iplist<Function>::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<CallInst>(*(it++));
|
|
if (!CI)
|
|
continue;
|
|
|
|
Value *V = CI->getArgOperand(DXIL::OperandIndex::kStoreOutputValOpIdx);
|
|
// Remove the store of undef.
|
|
if (isa<UndefValue>(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<const Function *, DISubprogram *> 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<Function *> entries;
|
|
for (iplist<Function>::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)
|