Mutate resource to handle for shader model 6.6+. (#3374)

* Mutate resource to handle for sm_6_6+.

* Use bitcast to save global symbol and HLSL type for DxilResourceBase.
This commit is contained in:
Xiang Li 2021-01-21 17:16:06 -08:00 коммит произвёл GitHub
Родитель 0ec5839e5e
Коммит 797d4b8adc
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
25 изменённых файлов: 602 добавлений и 65 удалений

Просмотреть файл

@ -18,6 +18,7 @@
namespace llvm {
class Value;
class Constant;
class Type;
}
@ -41,6 +42,7 @@ public:
unsigned GetUpperBound() const;
unsigned GetRangeSize() const;
llvm::Constant *GetGlobalSymbol() const;
llvm::Type *GetHLSLType() const;
const std::string &GetGlobalName() const;
llvm::Value *GetHandle() const;
bool IsAllocated() const;
@ -53,6 +55,7 @@ public:
void SetGlobalSymbol(llvm::Constant *pGV);
void SetGlobalName(const std::string &Name);
void SetHandle(llvm::Value *pHandle);
void SetHLSLType(llvm::Type *Ty);
// TODO: check whether we can make this a protected method.
void SetID(unsigned ID);
@ -76,6 +79,7 @@ private:
llvm::Constant *m_pSymbol; // Global variable.
std::string m_Name; // Unmangled name of the global variable.
llvm::Value *m_pHandle; // Cached resource handle for SM5.0- (and maybe SM5.1).
llvm::Type *m_pHLSLTy; // The original hlsl type for reflection.
};
const char *GetResourceKindName(DXIL::ResourceKind K);

Просмотреть файл

@ -131,4 +131,7 @@ void initializeDxilNoOptLegalizePass(llvm::PassRegistry&);
ModulePass *createDxilNoOptSimplifyInstructionsPass();
void initializeDxilNoOptSimplifyInstructionsPass(llvm::PassRegistry&);
ModulePass *createDxilMutateResourceToHandlePass();
void initializeDxilMutateResourceToHandlePass(llvm::PassRegistry&);
}

Просмотреть файл

@ -27,6 +27,7 @@
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/DenseSet.h"
#include <array>
@ -594,7 +595,16 @@ void DxilMDHelper::GetDxilResources(const MDOperand &MDO, const MDTuple *&pSRVs,
void DxilMDHelper::EmitDxilResourceBase(const DxilResourceBase &R, Metadata *ppMDVals[]) {
ppMDVals[kDxilResourceBaseID ] = Uint32ToConstMD(R.GetID());
ppMDVals[kDxilResourceBaseVariable ] = ValueAsMetadata::get(R.GetGlobalSymbol());
Constant *GlobalSymbol = R.GetGlobalSymbol();
// For sm66+, global symbol will be mutated into handle type.
// Save hlsl type by generate bitcast on global symbol.
if (m_pSM->IsSM66Plus()) {
Type *HLSLTy = R.GetHLSLType();
if (HLSLTy && HLSLTy != GlobalSymbol->getType())
GlobalSymbol = cast<Constant>(
ConstantExpr::getCast(Instruction::BitCast, GlobalSymbol, HLSLTy));
}
ppMDVals[kDxilResourceBaseVariable ] = ValueAsMetadata::get(GlobalSymbol);
ppMDVals[kDxilResourceBaseName ] = MDString::get(m_Ctx, R.GetGlobalName());
ppMDVals[kDxilResourceBaseSpaceID ] = Uint32ToConstMD(R.GetSpaceID());
ppMDVals[kDxilResourceBaseLowerBound] = Uint32ToConstMD(R.GetLowerBound());
@ -608,7 +618,20 @@ void DxilMDHelper::LoadDxilResourceBase(const MDOperand &MDO, DxilResourceBase &
IFTBOOL(pTupleMD->getNumOperands() >= kDxilResourceBaseNumFields, DXC_E_INCORRECT_DXIL_METADATA);
R.SetID(ConstMDToUint32(pTupleMD->getOperand(kDxilResourceBaseID)));
R.SetGlobalSymbol(dyn_cast<Constant>(ValueMDToValue(pTupleMD->getOperand(kDxilResourceBaseVariable))));
Constant *GlobalSymbol = dyn_cast<Constant>(ValueMDToValue(pTupleMD->getOperand(kDxilResourceBaseVariable)));
// For sm66+, global symbol will be mutated into handle type.
// Read hlsl type and global symbol from bitcast.
if (m_pSM->IsSM66Plus()) {
// Before mutate, there's no bitcast. After GlobalSymbol changed into undef,
// there's no bitcast too. Bitcast will only exist when global symbol is
// mutated into handle and not changed into undef for lib linking.
if (BitCastOperator *BCO = dyn_cast<BitCastOperator>(GlobalSymbol)) {
GlobalSymbol = cast<Constant>(BCO->getOperand(0));
R.SetHLSLType(BCO->getType());
}
}
R.SetGlobalSymbol(GlobalSymbol);
R.SetGlobalName(StringMDToString(pTupleMD->getOperand(kDxilResourceBaseName)));
R.SetSpaceID(ConstMDToUint32(pTupleMD->getOperand(kDxilResourceBaseSpaceID)));
R.SetLowerBound(ConstMDToUint32(pTupleMD->getOperand(kDxilResourceBaseLowerBound)));

Просмотреть файл

@ -49,7 +49,7 @@ void DxilResource::SetCompType(const CompType CT) {
}
Type *DxilResource::GetRetType() const {
Type *Ty = GetGlobalSymbol()->getType()->getPointerElementType();
Type *Ty = GetHLSLType()->getPointerElementType();
// For resource array, use element type.
while (Ty->isArrayTy())
Ty = Ty->getArrayElementType();

Просмотреть файл

@ -9,6 +9,7 @@
#include "dxc/DXIL/DxilResourceBase.h"
#include "dxc/Support/Global.h"
#include "llvm/IR/Constant.h"
namespace hlsl {
@ -26,7 +27,8 @@ DxilResourceBase::DxilResourceBase(Class C)
, m_LowerBound(0)
, m_RangeSize(0)
, m_pSymbol(nullptr)
, m_pHandle(nullptr) {
, m_pHandle(nullptr)
, m_pHLSLTy(nullptr) {
}
DxilResourceBase::Class DxilResourceBase::GetClass() const { return m_Class; }
@ -43,7 +45,13 @@ unsigned DxilResourceBase::GetUpperBound() const { return m_RangeSize != UINT_M
unsigned DxilResourceBase::GetRangeSize() const { return m_RangeSize; }
llvm::Constant *DxilResourceBase::GetGlobalSymbol() const { return m_pSymbol; }
const std::string &DxilResourceBase::GetGlobalName() const { return m_Name; }
llvm::Value *DxilResourceBase::GetHandle() const { return m_pHandle; }
llvm::Value *DxilResourceBase::GetHandle() const { return m_pHandle; }
// If m_pHLSLTy is nullptr, HLSL type is the type of m_pSymbol.
// In sm6.6, type of m_pSymbol will be mutated to handleTy, m_pHLSLTy will save
// the original HLSL type.
llvm::Type *DxilResourceBase::GetHLSLType() const {
return m_pHLSLTy == nullptr ? m_pSymbol->getType() : m_pHLSLTy;
}
bool DxilResourceBase::IsAllocated() const { return m_LowerBound != UINT_MAX; }
bool DxilResourceBase::IsUnbounded() const { return m_RangeSize == UINT_MAX; }
@ -55,6 +63,7 @@ void DxilResourceBase::SetRangeSize(unsigned RangeSize) { m_RangeSize
void DxilResourceBase::SetGlobalSymbol(llvm::Constant *pGV) { m_pSymbol = pGV; }
void DxilResourceBase::SetGlobalName(const std::string &Name) { m_Name = Name; }
void DxilResourceBase::SetHandle(llvm::Value *pHandle) { m_pHandle = pHandle; }
void DxilResourceBase::SetHLSLType(llvm::Type *pTy) { m_pHLSLTy = pTy; }
static const char *s_ResourceClassNames[] = {
"texture", "UAV", "cbuffer", "sampler"

Просмотреть файл

@ -109,6 +109,7 @@ HRESULT SetupRegistryPassForHLSL() {
initializeDxilLoopDeletionPass(Registry);
initializeDxilLoopUnrollPass(Registry);
initializeDxilLowerCreateHandleForLibPass(Registry);
initializeDxilMutateResourceToHandlePass(Registry);
initializeDxilNoOptLegalizePass(Registry);
initializeDxilNoOptSimplifyInstructionsPass(Registry);
initializeDxilPrecisePropagatePassPass(Registry);

Просмотреть файл

@ -1893,7 +1893,7 @@ StructType *UpdateStructTypeForLegacyLayout(StructType *ST,
void UpdateStructTypeForLegacyLayout(DxilResourceBase &Res,
DxilTypeSystem &TypeSys, Module &M) {
Constant *Symbol = Res.GetGlobalSymbol();
Type *ElemTy = Symbol->getType()->getPointerElementType();
Type *ElemTy = Res.GetHLSLType()->getPointerElementType();
// Support Array of ConstantBuffer/StructuredBuffer.
llvm::SmallVector<unsigned, 4> arrayDims;
ElemTy = dxilutil::StripArrayTypes(ElemTy, &arrayDims);
@ -2307,6 +2307,7 @@ void InitTBuffer(const DxilCBuffer *pSource, DxilResource *pDest) {
pDest->SetGlobalSymbol(pSource->GetGlobalSymbol());
pDest->SetGlobalName(pSource->GetGlobalName());
pDest->SetHandle(pSource->GetHandle());
pDest->SetHLSLType(pSource->GetHLSLType());
}
void PatchTBufferLoad(CallInst *handle, DxilModule &DM,

Просмотреть файл

@ -1228,7 +1228,7 @@ void CShaderReflectionConstantBuffer::Initialize(
// For ConstantBuffer<> buf[2], the array size is in Resource binding count
// part.
Type *Ty = dxilutil::StripArrayTypes(
CB.GetGlobalSymbol()->getType()->getPointerElementType());
CB.GetHLSLType()->getPointerElementType());
DxilTypeSystem &typeSys = M.GetTypeSystem();
StructType *ST = cast<StructType>(Ty);
@ -1265,8 +1265,7 @@ void CShaderReflectionConstantBuffer::Initialize(
DXASSERT(pVarType->m_Desc.Elements == 0,
"otherwise, assumption is wrong");
pVarType->m_Desc.Elements = 1;
} else if (CB.GetGlobalSymbol()
->getType()
} else if (CB.GetHLSLType()
->getPointerElementType()
->isArrayTy() &&
CB.GetRangeSize() == 1) {
@ -1315,7 +1314,7 @@ static unsigned CalcTypeSize(Type *Ty, unsigned &alignment) {
static unsigned CalcResTypeSize(DxilModule &M, DxilResource &R) {
UNREFERENCED_PARAMETER(M);
Type *Ty = R.GetGlobalSymbol()->getType()->getPointerElementType();
Type *Ty = R.GetHLSLType()->getPointerElementType();
if (R.IsStructuredBuffer()) {
Ty = dxilutil::StripArrayTypes(Ty);
}
@ -1351,7 +1350,7 @@ void CShaderReflectionConstantBuffer::InitializeStructuredBuffer(
// Create reflection type, if we have the necessary annotation info
// Extract the `struct` that wraps element type of the buffer resource
Type *Ty = R.GetGlobalSymbol()->getType()->getPointerElementType();
Type *Ty = R.GetHLSLType()->getPointerElementType();
SmallVector<unsigned, 4> arrayDims;
Ty = dxilutil::StripArrayTypes(Ty, &arrayDims);
for (unsigned i = 0; i < arrayDims.size(); ++i) {
@ -1396,7 +1395,7 @@ void CShaderReflectionConstantBuffer::InitializeTBuffer(
m_Desc.Type = D3D11_CT_TBUFFER;
m_Desc.uFlags = 0;
Type *Ty = R.GetGlobalSymbol()->getType()->getPointerElementType();
Type *Ty = R.GetHLSLType()->getPointerElementType();
DxilTypeSystem &typeSys = M.GetTypeSystem();
StructType *ST = cast<StructType>(Ty);

Просмотреть файл

@ -76,6 +76,7 @@ void InitResourceBase(const DxilResourceBase *pSource,
pDest->SetGlobalSymbol(pSource->GetGlobalSymbol());
pDest->SetGlobalName(pSource->GetGlobalName());
pDest->SetHandle(pSource->GetHandle());
pDest->SetHLSLType(pSource->GetHLSLType());
if (GlobalVariable *GV = dyn_cast<GlobalVariable>(pSource->GetGlobalSymbol()))
SimplifyGlobalSymbol(GV);

Просмотреть файл

@ -481,8 +481,8 @@ bool IsMatchedType(Type *Ty0, Type *Ty) {
bool DxilLinkJob::AddResource(DxilResourceBase *res, llvm::GlobalVariable *GV) {
if (m_resourceMap.count(res->GetGlobalName())) {
DxilResourceBase *res0 = m_resourceMap[res->GetGlobalName()].first;
Type *Ty0 = res0->GetGlobalSymbol()->getType()->getPointerElementType();
Type *Ty = res->GetGlobalSymbol()->getType()->getPointerElementType();
Type *Ty0 = res0->GetHLSLType()->getPointerElementType();
Type *Ty = res->GetHLSLType()->getPointerElementType();
// Make sure res0 match res.
bool bMatch = IsMatchedType(Ty0, Ty);
if (!bMatch) {
@ -1217,6 +1217,10 @@ void DxilLinkJob::FixShaderModelMismatch(llvm::Module &M) {
void DxilLinkJob::RunPreparePass(Module &M) {
StripDeadDebugInfo(M);
FixShaderModelMismatch(M);
DxilModule &DM = M.GetDxilModule();
const ShaderModel *pSM = DM.GetShaderModel();
legacy::PassManager PM;
PM.add(createAlwaysInlinerPass(/*InsertLifeTime*/ false));
@ -1247,6 +1251,9 @@ void DxilLinkJob::RunPreparePass(Module &M) {
PM.add(createDeadCodeEliminationPass());
PM.add(createGlobalDCEPass());
if (pSM->IsSM66Plus() && pSM->IsLib())
PM.add(createDxilMutateResourceToHandlePass());
PM.add(createDxilLowerCreateHandleForLibPass());
PM.add(createDxilTranslateRawBuffer());
PM.add(createDxilFinalizeModulePass());

Просмотреть файл

@ -777,7 +777,7 @@ bool DxilPatchShaderRecordBindings::GetHandleInfo(
shaderRegister = Resource->GetLowerBound();
kind = Resource->GetKind();
resClass = Resource->GetClass();
resType = Resource->GetGlobalSymbol()->getType()->getPointerElementType();
resType = Resource->GetHLSLType()->getPointerElementType();
}
return Resource != nullptr;
}

Просмотреть файл

@ -10,15 +10,25 @@
#include "dxc/DXIL/DxilUtil.h"
#include "dxc/HLSL/DxilGenerationPass.h"
#include "dxc/HLSL/HLModule.h"
#include "dxc/DXIL/DxilResourceBase.h"
#include "dxc/DXIL/DxilResource.h"
#include "dxc/DXIL/DxilCBuffer.h"
#include "dxc/DXIL/DxilOperations.h"
#include "dxc/DXIL/DxilModule.h"
#include "llvm/Pass.h"
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/ADT/DenseSet.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/Attributes.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/Operator.h"
#include "llvm/Transforms/Utils/PromoteMemToReg.h"
#include "llvm/Transforms/Utils/SSAUpdater.h"
#include <unordered_set>
@ -223,3 +233,474 @@ ModulePass *llvm::createDxilPromoteStaticResources() {
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) {}
const char *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;
hdlTy = HLM.GetOP()->GetHandleType();
pTypeSys = &HLM.GetTypeSystem();
} else if (M.HasDxilModule()) {
auto &DM = M.GetDxilModule();
if (!DM.GetShaderModel()->IsSM66Plus())
return false;
hdlTy = DM.GetOP()->GetHandleType();
pTypeSys = &DM.GetTypeSystem();
} else {
return false;
}
collectCandidates(M);
mutateCandidates(M);
// Remvoe cast to handle.
return !MutateValSet.empty();
}
private:
Type *mutateToHandleTy(Type *Ty);
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;
DxilTypeSystem *pTypeSys;
DenseSet<Value *> MutateValSet;
DenseMap<Type *, Type *> MutateTypeMap;
};
char DxilMutateResourceToHandle::ID = 0;
Type *DxilMutateResourceToHandle::mutateToHandleTy(Type *Ty) {
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);
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);
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 (!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 {
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();
// Save hlsl type before mutate to handle.
Res->SetHLSLType(GV->getType());
mutateToHandleTy(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);
}
}
}
SmallVector<Value *, 8>
DxilMutateResourceToHandle::collectHlslObjects(Module &M) {
// Add all global/function/argument/alloca has resource type.
SmallVector<Value *, 8> WorkList;
// Assume this is after SROA so no struct for global/alloca.
// Functions.
for (Function &F : M) {
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);
}
// 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);
}
}
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)
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();
V->dump();
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);
V->dump();
}
Function *createHandleForLibOnHandle = nullptr;
hlsl::OP *hlslOP = nullptr;
if (M.HasDxilModule()) {
auto &DM = M.GetDxilModule();
hlslOP = DM.GetOP();
if (hlslOP->IsDxilOpUsed(DXIL::OpCode::CreateHandleForLib)) {
createHandleForLibOnHandle =
hlslOP->GetOpFunc(DXIL::OpCode::CreateHandleForLib, hdlTy);
}
}
// 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 (!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)

Просмотреть файл

@ -388,6 +388,7 @@ struct ValidationContext {
Module &M;
Module *pDebugModule;
DxilModule &DxilMod;
const Type *HandleTy;
const DataLayout &DL;
DebugLoc LastDebugLocEmit;
ValidationRule LastRuleEmit;
@ -421,6 +422,7 @@ struct ValidationContext {
kLLVMLoopMDKind(llvmModule.getContext().getMDKindID("llvm.loop")),
slotTracker(&llvmModule, true) {
DxilMod.GetDxilVersion(m_DxilMajor, m_DxilMinor);
HandleTy = DxilMod.GetOP()->GetHandleType();
for (Function &F : llvmModule.functions()) {
if (DxilMod.HasDxilEntryProps(&F)) {
@ -481,6 +483,9 @@ struct ValidationContext {
}
} else if (LoadInst *LI = dyn_cast<LoadInst>(U)) {
PropagateResMap(U, Res);
} else if (isa<BitCastOperator>(U) && U->user_empty()) {
// For hlsl type.
continue;
} else {
EmitResourceError(Res, ValidationRule::InstrResourceUser);
}
@ -570,8 +575,7 @@ struct ValidationContext {
}
ConstantInt *cIndex = dyn_cast<ConstantInt>(hdl.get_index());
if (!Res->GetGlobalSymbol()
->getType()
if (!Res->GetHLSLType()
->getPointerElementType()
->isArrayTy()) {
if (!cIndex) {
@ -2722,6 +2726,9 @@ static bool ValidateType(Type *Ty, ValidationContext &ValCtx, bool bInner = fals
StringRef Name = ST->getName();
if (Name.startswith("dx.")) {
// Allow handle type.
if (ValCtx.HandleTy == Ty)
return true;
hlsl::OP *hlslOP = ValCtx.DxilMod.GetOP();
if (IsDxilBuiltinStructType(ST, hlslOP)) {
ValCtx.EmitTypeError(Ty, ValidationRule::InstrDxilStructUser);
@ -4128,7 +4135,7 @@ CollectCBufferRanges(DxilStructAnnotation *annotation,
}
static void ValidateCBuffer(DxilCBuffer &cb, ValidationContext &ValCtx) {
Type *Ty = cb.GetGlobalSymbol()->getType()->getPointerElementType();
Type *Ty = cb.GetHLSLType()->getPointerElementType();
if (cb.GetRangeSize() != 1 || Ty->isArrayTy()) {
Ty = Ty->getArrayElementType();
}

Просмотреть файл

@ -278,6 +278,7 @@ static void addHLSLPasses(bool HLSLHighLevel, unsigned OptLevel, bool OnlyWarnOn
MPM.add(createDxilPromoteLocalResources());
MPM.add(createDxilPromoteStaticResources());
// Verify no undef resource again after promotion
MPM.add(createInvalidateUndefResourcesPass());
@ -374,6 +375,7 @@ void PassManagerBuilder::populateModulePassManager(
MPM.add(createMultiDimArrayToOneDimArrayPass());
MPM.add(createDeadCodeEliminationPass());
MPM.add(createGlobalDCEPass());
MPM.add(createDxilMutateResourceToHandlePass());
MPM.add(createDxilLowerCreateHandleForLibPass());
MPM.add(createDxilTranslateRawBuffer());
MPM.add(createDxilLegalizeSampleOffsetPass());
@ -675,6 +677,7 @@ void PassManagerBuilder::populateModulePassManager(
MPM.add(createDxilRemoveDeadBlocksPass());
MPM.add(createDeadCodeEliminationPass());
MPM.add(createGlobalDCEPass());
MPM.add(createDxilMutateResourceToHandlePass());
MPM.add(createDxilLowerCreateHandleForLibPass());
MPM.add(createDxilTranslateRawBuffer());
// Always try to legalize sample offsets as loop unrolling

Просмотреть файл

@ -1805,7 +1805,7 @@ unsigned AllocateDxilConstantBuffer(
continue;
unsigned size = C->GetRangeSize();
llvm::Type *Ty = C->GetGlobalSymbol()->getType()->getPointerElementType();
llvm::Type *Ty = C->GetHLSLType()->getPointerElementType();
auto fieldAnnotation = constVarAnnotationMap.at(C->GetGlobalSymbol());
bool bRowMajor = HLMatrixType::isa(Ty)
? fieldAnnotation.GetMatrixAnnotation().Orientation ==
@ -1906,7 +1906,7 @@ bool CreateCBufferVariable(HLCBuffer &CB, HLModule &HLM, llvm::Type *HandleTy) {
if (!GV->use_empty())
bUsed = true;
// Global variable must be pointer type.
llvm::Type *Ty = GV->getType()->getPointerElementType();
llvm::Type *Ty = C->GetHLSLType()->getPointerElementType();
Elements.emplace_back(Ty);
}
// Don't create CBuffer variable for unused cbuffer.
@ -1942,9 +1942,8 @@ bool CreateCBufferVariable(HLCBuffer &CB, HLModule &HLM, llvm::Type *HandleTy) {
// array.
DXASSERT(CB.GetConstants().size() == 1,
"ConstantBuffer should have 1 constant");
Value *GV = CB.GetConstants()[0]->GetGlobalSymbol();
llvm::Type *CBEltTy =
GV->getType()->getPointerElementType()->getArrayElementType();
CB.GetConstants()[0]->GetHLSLType()->getPointerElementType()->getArrayElementType();
cbIndexDepth = 1;
while (CBEltTy->isArrayTy()) {
CBEltTy = CBEltTy->getArrayElementType();

Просмотреть файл

@ -1,8 +1,8 @@
// RUN: %dxc -T lib_6_x -auto-binding-space 11 %s | FileCheck %s
// lib_6_x does not reduce phi/select of resource or handle in lib.
// CHECK: phi %"class.RWBuffer
// CHECK: select i1 %{{[^,]+}}, %"class.
// CHECK: phi %dx.types.Handle
// CHECK: select i1 %{{[^,]+}}, %dx.types.Handle
// CHECK: ret <4 x float>
RWBuffer<float4> BufArray[2][2][3];

Просмотреть файл

@ -4,14 +4,13 @@
// phi/select of handle should not be produced in this case, but would be allowed if it were.
// CHECK: define <4 x float> @"\01?test@@YA?AV?$vector@M$03@@HHH@Z"(i32 %i, i32 %j, i32 %m)
// CHECK-NOT: phi %dx.types.Handle
// CHECK-NOT: select i1 %{{[^,]+}}, %dx.types.Handle
// CHECK: phi %"class.
// CHECK-NOT: phi %dx.types.Handle
// CHECK-NOT: select i1 %{{[^,]+}}, %dx.types.Handle
// CHECK: select i1 %{{[^,]+}}, %"class.
// CHECK-NOT: phi %dx.types.Handle
// CHECK-NOT: select i1 %{{[^,]+}}, %dx.types.Handle
// CHECK: phi %dx.types.Handle
// CHECK: phi %dx.types.Handle
// CHECK: phi %dx.types.Handle
// CHECK: select i1 %{{[^,]+}}, %dx.types.Handle
// CHECK: phi %dx.types.Handle
// CHECK: ret <4 x float>
RWBuffer<float4> BufArray[2][2][3];

Просмотреть файл

@ -4,14 +4,11 @@
// phi/select of handle should not be produced in this case, but would be allowed if it were.
// CHECK: define i32 @"\01?test@@YAIHHH@Z"(i32 %i, i32 %j, i32 %m)
// CHECK-NOT: phi %dx.types.Handle
// CHECK-NOT: select i1 %{{[^,]+}}, %dx.types.Handle
// CHECK: phi %"class.
// CHECK-NOT: phi %dx.types.Handle
// CHECK-NOT: select i1 %{{[^,]+}}, %dx.types.Handle
// CHECK: select i1 %{{[^,]+}}, %"class.
// CHECK-NOT: phi %dx.types.Handle
// CHECK-NOT: select i1 %{{[^,]+}}, %dx.types.Handle
// CHECK: phi %dx.types.Handle
// CHECK: select i1 %{{[^,]+}}, %dx.types.Handle
// CHECK: phi %dx.types.Handle
// CHECK: select i1 %{{[^,]+}}, %dx.types.Handle
// Make sure get dimensions returns 24
// CHECK: ret i32 24

Просмотреть файл

@ -4,14 +4,11 @@
// phi/select of handle should not be produced in this case, but would be allowed if it were.
// CHECK: define i32 @"\01?test@@YAIHHH@Z"(i32 %i, i32 %j, i32 %m)
// CHECK-NOT: phi %dx.types.Handle
// CHECK-NOT: select i1 %{{[^,]+}}, %dx.types.Handle
// CHECK: phi %"class.
// CHECK-NOT: phi %dx.types.Handle
// CHECK-NOT: select i1 %{{[^,]+}}, %dx.types.Handle
// CHECK: select i1 %{{[^,]+}}, %"class.
// CHECK-NOT: phi %dx.types.Handle
// CHECK-NOT: select i1 %{{[^,]+}}, %dx.types.Handle
// CHECK: phi %dx.types.Handle
// CHECK: phi %dx.types.Handle
// CHECK: select i1 %{{[^,]+}}, %dx.types.Handle
// CHECK: phi %dx.types.Handle
// CHECK: select i1 %{{[^,]+}}, %dx.types.Handle
// Make sure get dimensions returns 24
// CHECK: ret i32 24

Просмотреть файл

@ -1,12 +1,16 @@
// RUN: %dxc -T lib_6_x %s | FileCheck %s
// resources in return/params allowed for lib_6_x
// CHECK: alloca %struct.T
// CHECK: store %struct.RWByteAddressBuffer
// CHECK: call void @"\01?resStruct@@YA?AUT2@@UT@@V?$vector@I$01@@@Z"(%struct.T2
// CHECK: %[[ptr:[^, ]+]] = getelementptr inbounds %struct.T2
// CHECK: %[[val:[^, ]+]] = load %"class.RWStructuredBuffer<D>", %"class.RWStructuredBuffer<D>"* %[[ptr]]
// CHECK: call %dx.types.Handle @"dx.op.createHandleForLib.class.RWStructuredBuffer<D>"(i32 160, %"class.RWStructuredBuffer<D>" %[[val]])
// CHECK: alloca %struct.T.hdl
// CHECK: store %dx.types.Handle
// CHECK: call void @"\01?resStruct@@YA?AUT2@@UT@@V?$vector@I$01@@@Z"(%struct.T2.hdl
// CHECK: %[[ptr:[^, ]+]] = getelementptr inbounds %struct.T2.hdl
// CHECK: %[[val:[^, ]+]] = load %dx.types.Handle, %dx.types.Handle* %[[ptr]]
// CHECK: call %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32 160, %dx.types.Handle %[[val]])
// Make sure save bitcast for global symbol and HLSL type.
// CHECK:i32 0, %struct.RWByteAddressBuffer* bitcast (%dx.types.Handle* @"\01?outputBuffer@@3URWByteAddressBuffer@@A" to %struct.RWByteAddressBuffer*), !"outputBuffer"
// CHECK:i32 1, %struct.RWByteAddressBuffer* bitcast (%dx.types.Handle* @"\01?outputBuffer2@@3URWByteAddressBuffer@@A" to %struct.RWByteAddressBuffer*), !"outputBuffer2"
struct T {

Просмотреть файл

@ -1,7 +1,7 @@
// RUN: %dxc -T lib_6_x -Od %s | FileCheck %s
// lib_6_x allows phi on resource, targeting offline linking only.
// CHECK: phi %struct.ByteAddressBuffer
// CHECK: phi %dx.types.Handle
ByteAddressBuffer firstBuffer, secondBuffer;
uint firstBufferSize;

Просмотреть файл

@ -1,7 +1,7 @@
// RUN: %dxc -T lib_6_x -auto-binding-space 11 %s | FileCheck %s
// lib_6_x allows select on resource, targeting offline linking only.
// CHECK: select i1 %{{[^, ]+}}, %struct.ByteAddressBuffer
// CHECK: select i1 %{{[^, ]+}}, %dx.types.Handle
RWByteAddressBuffer outputBuffer : register(u0);
ByteAddressBuffer ReadBuffer : register(t0);

Просмотреть файл

@ -954,7 +954,7 @@ void PrintStructBufferDefinition(DxilResource *buf,
llvm::Type *RetTy = buf->GetRetType();
// Skip none struct type.
if (!RetTy->isStructTy() || HLMatrixType::isa(RetTy)) {
llvm::Type *Ty = buf->GetGlobalSymbol()->getType()->getPointerElementType();
llvm::Type *Ty = buf->GetHLSLType()->getPointerElementType();
// For resource array, use element type.
if (Ty->isArrayTy())
Ty = Ty->getArrayElementType();
@ -993,7 +993,7 @@ void PrintStructBufferDefinition(DxilResource *buf,
void PrintTBufferDefinition(DxilResource *buf, DxilTypeSystem &typeSys,
raw_string_ostream &OS, StringRef comment) {
const unsigned offsetIndent = 50;
llvm::Type *Ty = buf->GetGlobalSymbol()->getType()->getPointerElementType();
llvm::Type *Ty = buf->GetHLSLType()->getPointerElementType();
// For TextureBuffer<> buf[2], the array size is in Resource binding count
// part.
if (Ty->isArrayTy())
@ -1019,7 +1019,7 @@ void PrintTBufferDefinition(DxilResource *buf, DxilTypeSystem &typeSys,
void PrintCBufferDefinition(DxilCBuffer *buf, DxilTypeSystem &typeSys,
raw_string_ostream &OS, StringRef comment) {
const unsigned offsetIndent = 50;
llvm::Type *Ty = buf->GetGlobalSymbol()->getType()->getPointerElementType();
llvm::Type *Ty = buf->GetHLSLType()->getPointerElementType();
// For ConstantBuffer<> buf[2], the array size is in Resource binding count
// part.
if (Ty->isArrayTy())

Просмотреть файл

@ -307,14 +307,15 @@ TEST_F(LinkerTest, RunLinkGlobalInit) {
}
TEST_F(LinkerTest, RunLinkFailReDefineGlobal) {
LPCWSTR option[] = { L"-default-linkage", L"external" };
CComPtr<IDxcBlob> pEntryLib;
CompileLib(L"..\\CodeGenHLSL\\lib_global2.hlsl", &pEntryLib);
CompileLib(L"..\\CodeGenHLSL\\lib_global2.hlsl", &pEntryLib, option, L"lib_6_3");
CComPtr<IDxcBlob> pLib0;
CompileLib(L"..\\CodeGenHLSL\\lib_global3.hlsl", &pLib0);
CompileLib(L"..\\CodeGenHLSL\\lib_global3.hlsl", &pLib0, option, L"lib_6_3");
CComPtr<IDxcBlob> pLib1;
CompileLib(L"..\\CodeGenHLSL\\lib_global4.hlsl", &pLib1);
CompileLib(L"..\\CodeGenHLSL\\lib_global4.hlsl", &pLib1, option, L"lib_6_3");
CComPtr<IDxcLinker> pLinker;
CreateLinker(&pLinker);
@ -688,14 +689,14 @@ TEST_F(LinkerTest, RunLinkToLibWithNoExports) {
}
TEST_F(LinkerTest, RunLinkWithPotentialIntrinsicNameCollisions) {
LPCWSTR option[] = { L"-Zi", L"-Qembed_debug" };
LPCWSTR option[] = { L"-Zi", L"-Qembed_debug", L"-default-linkage", L"external" };
CComPtr<IDxcBlob> pLib1;
CompileLib(L"..\\CodeGenHLSL\\linker\\createHandle_multi.hlsl",
&pLib1, option);
&pLib1, option, L"lib_6_3");
CComPtr<IDxcBlob> pLib2;
CompileLib(L"..\\CodeGenHLSL\\linker\\createHandle_multi2.hlsl",
&pLib2, option);
&pLib2, option, L"lib_6_3");
CComPtr<IDxcLinker> pLinker;
CreateLinker(&pLinker);

Просмотреть файл

@ -2136,6 +2136,7 @@ class db_dxil(object):
{'n':'from-binding', 'i':'FromBinding', 't':'bool', 'c':1, 'd':'Append binding to name when bound'},
{'n':'keep-name', 'i':'KeepName', 't':'bool', 'c':1, 'd':'Keep name when appending binding'},
])
add_pass('hlsl-dxil-resources-to-handle', 'DxilMutateResourceToHandle', 'Mutate resource to handle',[])
category_lib="llvm"
add_pass('ipsccp', 'IPSCCP', 'Interprocedural Sparse Conditional Constant Propagation', [])