blob: 9ba6e3cb3d61187c025665e7a8dab1a8849aabae [file] [log] [blame]
///////////////////////////////////////////////////////////////////////////////
// //
// DxilPromoteResourcePasses.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. //
// //
///////////////////////////////////////////////////////////////////////////////
#include "dxc/DXIL/DxilCBuffer.h"
#include "dxc/DXIL/DxilModule.h"
#include "dxc/DXIL/DxilOperations.h"
#include "dxc/DXIL/DxilResource.h"
#include "dxc/DXIL/DxilResourceBase.h"
#include "dxc/DXIL/DxilUtil.h"
#include "dxc/HLSL/DxilGenerationPass.h"
#include "dxc/HLSL/HLModule.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Operator.h"
#include "llvm/Pass.h"
#include "llvm/Transforms/Utils/PromoteMemToReg.h"
#include "llvm/Transforms/Utils/SSAUpdater.h"
#include <unordered_set>
#include <vector>
using namespace llvm;
using namespace hlsl;
// Legalize resource use.
// Map local or static global resource to global resource.
// Require inline for static global resource.
namespace {
static const StringRef kStaticResourceLibErrorMsg =
"non const static global resource use is disallowed in library exports.";
class DxilPromoteStaticResources : public ModulePass {
public:
static char ID; // Pass identification, replacement for typeid
explicit DxilPromoteStaticResources() : ModulePass(ID) {}
StringRef getPassName() const override {
return "DXIL Legalize Static Resource Use";
}
bool runOnModule(Module &M) override {
// Promote static global variables.
return PromoteStaticGlobalResources(M);
}
private:
bool PromoteStaticGlobalResources(Module &M);
};
char DxilPromoteStaticResources::ID = 0;
class DxilPromoteLocalResources : public FunctionPass {
void getAnalysisUsage(AnalysisUsage &AU) const override;
public:
static char ID; // Pass identification, replacement for typeid
explicit DxilPromoteLocalResources() : FunctionPass(ID) {}
StringRef getPassName() const override {
return "DXIL Legalize Resource Use";
}
bool runOnFunction(Function &F) override {
// Promote local resource first.
return PromoteLocalResource(F);
}
private:
bool PromoteLocalResource(Function &F);
};
char DxilPromoteLocalResources::ID = 0;
} // namespace
void DxilPromoteLocalResources::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<AssumptionCacheTracker>();
AU.addRequired<DominatorTreeWrapperPass>();
AU.setPreservesAll();
}
bool DxilPromoteLocalResources::PromoteLocalResource(Function &F) {
bool bModified = false;
std::vector<AllocaInst *> Allocas;
DominatorTree *DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
AssumptionCache &AC =
getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F);
BasicBlock &BB = F.getEntryBlock();
unsigned allocaSize = 0;
while (1) {
Allocas.clear();
// Find allocas that are safe to promote, by looking at all instructions in
// the entry node
for (BasicBlock::iterator I = BB.begin(), E = --BB.end(); I != E; ++I)
if (AllocaInst *AI = dyn_cast<AllocaInst>(I)) { // Is it an alloca?
if (dxilutil::IsHLSLObjectType(
dxilutil::GetArrayEltTy(AI->getAllocatedType()))) {
if (isAllocaPromotable(AI))
Allocas.push_back(AI);
}
}
if (Allocas.empty())
break;
// No update.
// Report error and break.
if (allocaSize == Allocas.size()) {
// TODO: Add test for this instance of the error: "local resource not
// guaranteed to map to unique global resource." No test currently
// exists.
dxilutil::EmitErrorOnContext(F.getContext(),
dxilutil::kResourceMapErrorMsg);
break;
}
allocaSize = Allocas.size();
PromoteMemToReg(Allocas, *DT, nullptr, &AC);
bModified = true;
}
return bModified;
}
FunctionPass *llvm::createDxilPromoteLocalResources() {
return new DxilPromoteLocalResources();
}
INITIALIZE_PASS_BEGIN(DxilPromoteLocalResources,
"hlsl-dxil-promote-local-resources",
"DXIL promote local resource use", false, true)
INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker)
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
INITIALIZE_PASS_END(DxilPromoteLocalResources,
"hlsl-dxil-promote-local-resources",
"DXIL promote local resource use", false, true)
bool DxilPromoteStaticResources::PromoteStaticGlobalResources(Module &M) {
if (M.GetOrCreateHLModule().GetShaderModel()->IsLib()) {
// Read/write to global static resource is disallowed for libraries:
// Resource use needs to be resolved to a single real global resource,
// but it may not be possible since any external function call may re-enter
// at any other library export, which could modify the global static
// between write and read.
// While it could work for certain cases, describing the boundary at
// the HLSL level is difficult, so at this point it's better to disallow.
// example of what could work:
// After inlining, exported functions must have writes to static globals
// before reads, and must not have any external function calls between
// writes and subsequent reads, such that the static global may be
// optimized away for the exported function.
for (auto &GV : M.globals()) {
if (GV.getLinkage() == GlobalVariable::LinkageTypes::InternalLinkage &&
!GV.isConstant() &&
dxilutil::IsHLSLObjectType(dxilutil::GetArrayEltTy(GV.getType()))) {
if (!GV.user_empty()) {
if (Instruction *I = dyn_cast<Instruction>(*GV.user_begin())) {
dxilutil::EmitErrorOnInstruction(I, kStaticResourceLibErrorMsg);
break;
}
}
}
}
return false;
}
bool bModified = false;
std::set<GlobalVariable *> staticResources;
for (auto &GV : M.globals()) {
if (GV.getLinkage() == GlobalVariable::LinkageTypes::InternalLinkage &&
dxilutil::IsHLSLObjectType(dxilutil::GetArrayEltTy(GV.getType()))) {
staticResources.insert(&GV);
}
}
SSAUpdater SSA;
SmallVector<Instruction *, 4> Insts;
// Make sure every resource load has mapped to global variable.
while (!staticResources.empty()) {
bool bUpdated = false;
for (auto it = staticResources.begin(); it != staticResources.end();) {
GlobalVariable *GV = *(it++);
// Build list of instructions to promote.
for (User *U : GV->users()) {
if (isa<LoadInst>(U) || isa<StoreInst>(U)) {
Insts.emplace_back(cast<Instruction>(U));
} else if (GEPOperator *GEP = dyn_cast<GEPOperator>(U)) {
for (User *gepU : GEP->users()) {
DXASSERT_NOMSG(isa<LoadInst>(gepU) || isa<StoreInst>(gepU));
if (isa<LoadInst>(gepU) || isa<StoreInst>(gepU))
Insts.emplace_back(cast<Instruction>(gepU));
}
} else {
DXASSERT(false, "Unhandled user of resource static global");
}
}
LoadAndStorePromoter(Insts, SSA).run(Insts);
GV->removeDeadConstantUsers();
if (GV->user_empty()) {
bUpdated = true;
staticResources.erase(GV);
}
Insts.clear();
}
if (!bUpdated) {
// TODO: Add test for this instance of the error: "local resource not
// guaranteed to map to unique global resource." No test currently
// exists.
dxilutil::EmitErrorOnContext(M.getContext(),
dxilutil::kResourceMapErrorMsg);
break;
}
bModified = true;
}
return bModified;
}
ModulePass *llvm::createDxilPromoteStaticResources() {
return new DxilPromoteStaticResources();
}
INITIALIZE_PASS(DxilPromoteStaticResources,
"hlsl-dxil-promote-static-resources",
"DXIL promote static resource use", false, false)
// Mutate high-level resource type into handle.
// This is used for SM 6.6+, on libraries only, where
// CreateHandleForLib is eliminated, and high-level resource
// types are only preserved in metadata for reflection purposes.
namespace {
// Overview
// 1. collectCandidates - collect to MutateValSet
// Start from resource global variable, function parameter/ret, alloca.
// Propagate to all insts, GEP/ld/st/phi/select/called functions.
// 2. mutateCandidates
// Mutate all non-function value types.
// Mutate functions by creating new function with new type, then
// splice original function blocks into new function, and
// replace old argument uses with new function's arguments.
class DxilMutateResourceToHandle : public ModulePass {
public:
static char ID; // Pass identification, replacement for typeid
explicit DxilMutateResourceToHandle() : ModulePass(ID) {}
StringRef getPassName() const override {
return "DXIL Mutate resource to handle";
}
bool runOnModule(Module &M) override {
if (M.HasHLModule()) {
auto &HLM = M.GetHLModule();
if (!HLM.GetShaderModel()->IsSM66Plus())
return false;
hlslOP = HLM.GetOP();
pTypeSys = &HLM.GetTypeSystem();
} else if (M.HasDxilModule()) {
auto &DM = M.GetDxilModule();
if (!DM.GetShaderModel()->IsSM66Plus())
return false;
hlslOP = DM.GetOP();
pTypeSys = &DM.GetTypeSystem();
} else {
return false;
}
hdlTy = hlslOP->GetHandleType();
if (hlslOP->IsDxilOpUsed(DXIL::OpCode::CreateHandleForLib)) {
createHandleForLibOnHandle =
hlslOP->GetOpFunc(DXIL::OpCode::CreateHandleForLib, hdlTy);
}
collectCandidates(M);
mutateCandidates(M);
// Remove cast to handle.
return !MutateValSet.empty();
}
private:
Type *mutateToHandleTy(Type *Ty, bool bResType = false);
bool mutateTypesToHandleTy(SmallVector<Type *, 4> &Tys);
void collectGlobalResource(DxilResourceBase *Res,
SmallVector<Value *, 8> &WorkList);
void collectAlloca(Function &F, SmallVector<Value *, 8> &WorkList);
SmallVector<Value *, 8> collectHlslObjects(Module &M);
void collectCandidates(Module &M);
void mutateCandidates(Module &M);
Type *hdlTy = nullptr;
hlsl::OP *hlslOP = nullptr;
Function *createHandleForLibOnHandle = nullptr;
DxilTypeSystem *pTypeSys;
DenseSet<Value *> MutateValSet;
DenseMap<Type *, Type *> MutateTypeMap;
};
char DxilMutateResourceToHandle::ID = 0;
Type *DxilMutateResourceToHandle::mutateToHandleTy(Type *Ty, bool bResType) {
auto it = MutateTypeMap.find(Ty);
if (it != MutateTypeMap.end())
return it->second;
Type *ResultTy = nullptr;
if (ArrayType *AT = dyn_cast<ArrayType>(Ty)) {
SmallVector<unsigned, 2> nestedSize;
Type *EltTy = Ty;
while (ArrayType *NestAT = dyn_cast<ArrayType>(EltTy)) {
nestedSize.emplace_back(NestAT->getNumElements());
EltTy = NestAT->getElementType();
}
Type *mutatedTy = mutateToHandleTy(EltTy, bResType);
if (mutatedTy == EltTy) {
ResultTy = Ty;
} else {
Type *newAT = mutatedTy;
for (auto it = nestedSize.rbegin(), E = nestedSize.rend(); it != E; ++it)
newAT = ArrayType::get(newAT, *it);
ResultTy = newAT;
}
} else if (PointerType *PT = dyn_cast<PointerType>(Ty)) {
Type *EltTy = PT->getElementType();
Type *mutatedTy = mutateToHandleTy(EltTy, bResType);
if (mutatedTy == EltTy)
ResultTy = Ty;
else
ResultTy = mutatedTy->getPointerTo(PT->getAddressSpace());
} else if (dxilutil::IsHLSLResourceType(Ty)) {
ResultTy = hdlTy;
} else if (StructType *ST = dyn_cast<StructType>(Ty)) {
if (bResType) {
// For top-level resource GV type, the first struct type is the resource
// type to be changed into handle.
ResultTy = hdlTy;
} else if (!ST->isOpaque()) {
SmallVector<Type *, 4> Elts(ST->element_begin(), ST->element_end());
if (!mutateTypesToHandleTy(Elts)) {
ResultTy = Ty;
} else {
ResultTy = StructType::create(Elts, ST->getName().str() + ".hdl");
}
} else {
// FIXME: Opaque type "ConstantBuffer" is only used for an empty cbuffer.
// We should never use an empty cbuffer, and we should try to get rid of
// the need for this type in the first place, like using nullptr for the
// DxilResourceBase::m_pSymbol, since this resource should get deleted
// before final dxil in all cases.
if (ST->getName() == "ConstantBuffer")
ResultTy = hdlTy;
else
ResultTy = Ty;
}
} else if (FunctionType *FT = dyn_cast<FunctionType>(Ty)) {
Type *RetTy = FT->getReturnType();
SmallVector<Type *, 4> Args(FT->param_begin(), FT->param_end());
Type *mutatedRetTy = mutateToHandleTy(RetTy);
if (!mutateTypesToHandleTy(Args) && RetTy == mutatedRetTy) {
ResultTy = Ty;
} else {
ResultTy = FunctionType::get(mutatedRetTy, Args, FT->isVarArg());
}
} else {
ResultTy = Ty;
}
MutateTypeMap[Ty] = ResultTy;
return ResultTy;
}
bool DxilMutateResourceToHandle::mutateTypesToHandleTy(
SmallVector<Type *, 4> &Tys) {
bool bMutated = false;
for (size_t i = 0; i < Tys.size(); i++) {
Type *Ty = Tys[i];
Type *mutatedTy = mutateToHandleTy(Ty);
if (Ty != mutatedTy) {
Tys[i] = mutatedTy;
bMutated = true;
}
}
return bMutated;
}
void DxilMutateResourceToHandle::collectGlobalResource(
DxilResourceBase *Res, SmallVector<Value *, 8> &WorkList) {
Value *GV = Res->GetGlobalSymbol();
// If already handle, don't overwrite HLSL type.
// It's still posible that load users have a wrong type (invalid IR) due to
// linking mixed targets. But in that case, we need to start at the
// non-handle overloads of CreateHandleForLib and mutate/rewrite from there.
// That's because we may have an already translated GV, but some load and
// CreateHandleForLib calls use the wrong type from linked code.
Type *MTy = mutateToHandleTy(GV->getType(), /*bResType*/ true);
if (GV->getType() != MTy) {
// Save hlsl type before mutate to handle.
Res->SetHLSLType(GV->getType());
WorkList.emplace_back(GV);
}
}
void DxilMutateResourceToHandle::collectAlloca(
Function &F, SmallVector<Value *, 8> &WorkList) {
if (F.isDeclaration())
return;
for (Instruction &I : F.getEntryBlock()) {
AllocaInst *AI = dyn_cast<AllocaInst>(&I);
if (!AI)
continue;
Type *Ty = AI->getType();
Type *MTy = mutateToHandleTy(Ty);
if (Ty == MTy)
continue;
WorkList.emplace_back(AI);
}
}
} // namespace
SmallVector<Value *, 8>
DxilMutateResourceToHandle::collectHlslObjects(Module &M) {
// Add all global/function/argument/alloca has resource type.
SmallVector<Value *, 8> WorkList;
// Global resources.
if (M.HasHLModule()) {
auto &HLM = M.GetHLModule();
for (auto &Res : HLM.GetCBuffers()) {
collectGlobalResource(Res.get(), WorkList);
}
for (auto &Res : HLM.GetSRVs()) {
collectGlobalResource(Res.get(), WorkList);
}
for (auto &Res : HLM.GetUAVs()) {
collectGlobalResource(Res.get(), WorkList);
}
for (auto &Res : HLM.GetSamplers()) {
collectGlobalResource(Res.get(), WorkList);
}
} else {
auto &DM = M.GetDxilModule();
for (auto &Res : DM.GetCBuffers()) {
collectGlobalResource(Res.get(), WorkList);
}
for (auto &Res : DM.GetSRVs()) {
collectGlobalResource(Res.get(), WorkList);
}
for (auto &Res : DM.GetUAVs()) {
collectGlobalResource(Res.get(), WorkList);
}
for (auto &Res : DM.GetSamplers()) {
collectGlobalResource(Res.get(), WorkList);
}
}
// Assume this is after SROA so no struct for global/alloca.
// Functions.
for (Function &F : M) {
if (hlslOP && hlslOP->IsDxilOpFunc(&F)) {
DXIL::OpCodeClass OpcodeClass;
if (hlslOP->GetOpCodeClass(&F, OpcodeClass)) {
if (OpcodeClass == DXIL::OpCodeClass::CreateHandleForLib &&
&F != createHandleForLibOnHandle) {
WorkList.emplace_back(&F);
MutateTypeMap[F.getFunctionType()->getFunctionParamType(1)] = hdlTy;
continue;
}
}
}
collectAlloca(F, WorkList);
FunctionType *FT = F.getFunctionType();
FunctionType *MFT = cast<FunctionType>(mutateToHandleTy(FT));
if (FT == MFT)
continue;
WorkList.emplace_back(&F);
// Check args.
for (Argument &Arg : F.args()) {
Type *Ty = Arg.getType();
Type *MTy = mutateToHandleTy(Ty);
if (Ty == MTy)
continue;
WorkList.emplace_back(&Arg);
}
}
// Static globals.
for (GlobalVariable &GV : M.globals()) {
if (!dxilutil::IsStaticGlobal(&GV))
continue;
Type *Ty = dxilutil::GetArrayEltTy(GV.getValueType());
if (!dxilutil::IsHLSLObjectType(Ty))
continue;
WorkList.emplace_back(&GV);
}
return WorkList;
}
void DxilMutateResourceToHandle::collectCandidates(Module &M) {
SmallVector<Value *, 8> WorkList = collectHlslObjects(M);
// Propagate candidates.
while (!WorkList.empty()) {
Value *V = WorkList.pop_back_val();
MutateValSet.insert(V);
for (User *U : V->users()) {
// collect in a user.
SmallVector<Value *, 2> newCandidates;
// Should only used by ld/st/sel/phi/gep/call.
if (LoadInst *LI = dyn_cast<LoadInst>(U)) {
newCandidates.emplace_back(LI);
} else if (StoreInst *SI = dyn_cast<StoreInst>(U)) {
Value *Ptr = SI->getPointerOperand();
Value *Val = SI->getValueOperand();
if (V == Ptr)
newCandidates.emplace_back(Val);
else
newCandidates.emplace_back(Ptr);
} else if (GEPOperator *GEP = dyn_cast<GEPOperator>(U)) {
// If result type of GEP not related to resource type, skip.
Type *Ty = GEP->getType();
Type *MTy = mutateToHandleTy(Ty);
if (MTy == Ty) {
// Don't recurse, but still need to mutate GEP.
MutateValSet.insert(GEP);
continue;
}
newCandidates.emplace_back(GEP);
} else if (PHINode *Phi = dyn_cast<PHINode>(U)) {
// Propagate all operands.
newCandidates.emplace_back(Phi);
for (Use &PhiOp : Phi->incoming_values()) {
if (V == PhiOp)
continue;
newCandidates.emplace_back(PhiOp);
}
} else if (SelectInst *Sel = dyn_cast<SelectInst>(U)) {
// Propagate other result.
newCandidates.emplace_back(Sel);
Value *TrueV = Sel->getTrueValue();
Value *FalseV = Sel->getFalseValue();
if (TrueV == V)
newCandidates.emplace_back(FalseV);
else
newCandidates.emplace_back(TrueV);
} else if (BitCastOperator *BCO = dyn_cast<BitCastOperator>(U)) {
// Make sure only used for lifetime intrinsic.
for (User *BCUser : BCO->users()) {
if (ConstantArray *CA = dyn_cast<ConstantArray>(BCUser)) {
// For llvm.used.
if (CA->hasOneUse()) {
Value *CAUser = CA->user_back();
if (GlobalVariable *GV = dyn_cast<GlobalVariable>(CAUser)) {
if (GV->getName() == "llvm.used")
continue;
}
} else if (CA->user_empty()) {
continue;
}
}
CallInst *CI = cast<CallInst>(BCUser);
Function *F = CI->getCalledFunction();
Intrinsic::ID ID = F->getIntrinsicID();
if (ID != Intrinsic::lifetime_start &&
ID != Intrinsic::lifetime_end) {
DXASSERT(false, "unexpected resource object user");
}
}
} else {
CallInst *CI = cast<CallInst>(U);
Type *Ty = CI->getType();
Type *MTy = mutateToHandleTy(Ty);
if (Ty != MTy)
newCandidates.emplace_back(CI);
SmallVector<Value *, 4> Args(CI->arg_operands().begin(),
CI->arg_operands().end());
for (Value *Arg : Args) {
if (Arg == V)
continue;
Type *Ty = Arg->getType();
Type *MTy = mutateToHandleTy(Ty);
if (Ty == MTy)
continue;
newCandidates.emplace_back(Arg);
}
}
for (Value *Val : newCandidates) {
// New candidate find.
if (MutateValSet.insert(Val).second) {
WorkList.emplace_back(Val);
}
}
}
}
}
void DxilMutateResourceToHandle::mutateCandidates(Module &M) {
SmallVector<Function *, 2> CandidateFns;
for (Value *V : MutateValSet) {
if (Function *F = dyn_cast<Function>(V)) {
CandidateFns.emplace_back(F);
continue;
}
Type *Ty = V->getType();
Type *MTy = mutateToHandleTy(Ty);
if (AllocaInst *AI = dyn_cast<AllocaInst>(V)) {
AI->setAllocatedType(MTy->getPointerElementType());
} else if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(V)) {
Type *MResultEltTy = mutateToHandleTy(GEP->getResultElementType());
GEP->setResultElementType(MResultEltTy);
Type *MSrcEltTy = mutateToHandleTy(GEP->getSourceElementType());
GEP->setSourceElementType(MSrcEltTy);
} else if (GEPOperator *GEPO = dyn_cast<GEPOperator>(V)) {
// GEP operator not support setSourceElementType.
// Create a new GEP here.
Constant *C = cast<Constant>(GEPO->getPointerOperand());
IRBuilder<> B(C->getContext());
// Make sure C is mutated so the GEP get correct sourceElementType.
C->mutateType(mutateToHandleTy(C->getType()));
// Collect user of GEPs, then replace all use with undef.
SmallVector<Use *, 2> Uses;
for (Use &U : GEPO->uses()) {
Uses.emplace_back(&U);
}
SmallVector<Value *, 2> idxList(GEPO->idx_begin(), GEPO->idx_end());
Type *Ty = GEPO->getType();
GEPO->replaceAllUsesWith(UndefValue::get(Ty));
StringRef Name = GEPO->getName();
// GO and newGO will be same constant except has different
// sourceElementType. ConstantMap think they're the same constant. Have to
// remove GO first before create newGO.
C->removeDeadConstantUsers();
Value *newGO = B.CreateGEP(C, idxList, Name);
// update uses.
for (Use *U : Uses) {
U->set(newGO);
}
continue;
}
V->mutateType(MTy);
}
// Mutate functions.
for (Function *F : CandidateFns) {
Function *MF = nullptr;
if (hlslOP) {
if (hlslOP->IsDxilOpFunc(F)) {
DXIL::OpCodeClass OpcodeClass;
if (hlslOP->GetOpCodeClass(F, OpcodeClass)) {
if (OpcodeClass == DXIL::OpCodeClass::CreateHandleForLib) {
MF = createHandleForLibOnHandle;
}
}
}
}
if (hlsl::GetHLOpcodeGroup(F) == HLOpcodeGroup::HLCast) {
// Eliminate pass-through cast
for (auto it = F->user_begin(); it != F->user_end();) {
CallInst *CI = cast<CallInst>(*(it++));
CI->replaceAllUsesWith(CI->getArgOperand(1));
CI->eraseFromParent();
}
continue;
}
if (!MF) {
FunctionType *FT = F->getFunctionType();
FunctionType *MFT = cast<FunctionType>(MutateTypeMap[FT]);
MF = Function::Create(MFT, F->getLinkage(), "", &M);
MF->takeName(F);
// Copy calling conv.
MF->setCallingConv(F->getCallingConv());
// Copy attributes.
AttributeSet AS = F->getAttributes();
MF->setAttributes(AS);
// Annotation.
if (DxilFunctionAnnotation *FnAnnot =
pTypeSys->GetFunctionAnnotation(F)) {
DxilFunctionAnnotation *newFnAnnot =
pTypeSys->AddFunctionAnnotation(MF);
DxilParameterAnnotation &RetAnnot = newFnAnnot->GetRetTypeAnnotation();
RetAnnot = FnAnnot->GetRetTypeAnnotation();
for (unsigned i = 0; i < FnAnnot->GetNumParameters(); i++) {
newFnAnnot->GetParameterAnnotation(i) =
FnAnnot->GetParameterAnnotation(i);
}
}
// Update function debug info.
if (DISubprogram *funcDI = getDISubprogram(F))
funcDI->replaceFunction(MF);
}
for (auto it = F->user_begin(); it != F->user_end();) {
CallInst *CI = cast<CallInst>(*(it++));
CI->setCalledFunction(MF);
}
if (F->isDeclaration()) {
F->eraseFromParent();
continue;
}
// Take body of F.
// Splice the body of the old function right into the new function.
MF->getBasicBlockList().splice(MF->begin(), F->getBasicBlockList());
// Replace use of arg.
auto argIt = F->arg_begin();
for (auto MArgIt = MF->arg_begin(); MArgIt != MF->arg_end();) {
Argument *Arg = (argIt++);
Argument *MArg = (MArgIt++);
Arg->replaceAllUsesWith(MArg);
}
}
}
ModulePass *llvm::createDxilMutateResourceToHandlePass() {
return new DxilMutateResourceToHandle();
}
INITIALIZE_PASS(DxilMutateResourceToHandle, "hlsl-dxil-resources-to-handle",
"Mutate resource to handle", false, false)