|
|
|
@ -270,6 +270,7 @@ public:
|
|
|
|
|
GenerateDxilCBufferHandles(NonUniformSet);
|
|
|
|
|
MarkUpdateCounter(UpdateCounterSet);
|
|
|
|
|
LowerHLCreateHandle();
|
|
|
|
|
MarkNonUniform(NonUniformSet);
|
|
|
|
|
|
|
|
|
|
// For module which not promote mem2reg.
|
|
|
|
|
// Remove local resource alloca/load/store/phi.
|
|
|
|
@ -314,14 +315,6 @@ public:
|
|
|
|
|
private:
|
|
|
|
|
void RemoveLocalDxilResourceAllocas(Function *F);
|
|
|
|
|
void MarkUpdateCounter(std::unordered_set<LoadInst *> &UpdateCounterSet);
|
|
|
|
|
void
|
|
|
|
|
TranslateDxilResourceUses(DxilResourceBase &res,
|
|
|
|
|
std::unordered_set<LoadInst *> &UpdateCounterSet,
|
|
|
|
|
std::unordered_set<Value *> &NonUniformSet);
|
|
|
|
|
void
|
|
|
|
|
GenerateDxilResourceHandles(std::unordered_set<LoadInst *> &UpdateCounterSet,
|
|
|
|
|
std::unordered_set<Value *> &NonUniformSet);
|
|
|
|
|
void AddCreateHandleForPhiNodeAndSelect(OP *hlslOP);
|
|
|
|
|
void TranslateParamDxilResourceHandles(Function *F, std::unordered_map<Instruction *, Value *> &handleMap);
|
|
|
|
|
void GenerateParamDxilResourceHandles(
|
|
|
|
|
std::unordered_map<Instruction *, Value *> &handleMap);
|
|
|
|
@ -334,8 +327,7 @@ private:
|
|
|
|
|
std::unordered_set<LoadInst *> &UpdateCounterSet,
|
|
|
|
|
std::unordered_set<Value *> &NonUniformSet);
|
|
|
|
|
void LowerHLCreateHandle();
|
|
|
|
|
// Change struct type to legacy layout for cbuf and struct buf.
|
|
|
|
|
void UpdateStructTypeForLegacyLayout();
|
|
|
|
|
void MarkNonUniform(std::unordered_set<Value *> &NonUniformSet);
|
|
|
|
|
|
|
|
|
|
// Translate precise attribute into HL function call.
|
|
|
|
|
void TranslatePreciseAttribute();
|
|
|
|
@ -395,6 +387,17 @@ void DxilGenerationPass::LowerHLCreateHandle() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DxilGenerationPass::MarkNonUniform(
|
|
|
|
|
std::unordered_set<Value *> &NonUniformSet) {
|
|
|
|
|
for (Value *V : NonUniformSet) {
|
|
|
|
|
for (User *U : V->users()) {
|
|
|
|
|
if (Instruction *I = dyn_cast<Instruction>(U)) {
|
|
|
|
|
DxilMDHelper::MarkNonUniform(I);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Value *MergeImmResClass(Value *resClass) {
|
|
|
|
|
if (ConstantInt *Imm = dyn_cast<ConstantInt>(resClass)) {
|
|
|
|
|
return resClass;
|
|
|
|
@ -603,403 +606,6 @@ void DxilGenerationPass::MarkUpdateCounter(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DxilGenerationPass::TranslateDxilResourceUses(
|
|
|
|
|
DxilResourceBase &res, std::unordered_set<LoadInst *> &UpdateCounterSet,
|
|
|
|
|
std::unordered_set<Value *> &NonUniformSet) {
|
|
|
|
|
OP *hlslOP = m_pHLModule->GetOP();
|
|
|
|
|
Function *createHandle = hlslOP->GetOpFunc(
|
|
|
|
|
OP::OpCode::CreateHandle, llvm::Type::getVoidTy(m_pHLModule->GetCtx()));
|
|
|
|
|
Value *opArg = hlslOP->GetU32Const((unsigned)OP::OpCode::CreateHandle);
|
|
|
|
|
bool isViewResource = res.GetClass() == DXIL::ResourceClass::SRV || res.GetClass() == DXIL::ResourceClass::UAV;
|
|
|
|
|
bool isROV = isViewResource && static_cast<DxilResource &>(res).IsROV();
|
|
|
|
|
std::string handleName = (res.GetGlobalName() + Twine("_") + Twine(res.GetResClassName())).str();
|
|
|
|
|
if (isViewResource)
|
|
|
|
|
handleName += (Twine("_") + Twine(res.GetResDimName())).str();
|
|
|
|
|
if (isROV)
|
|
|
|
|
handleName += "_ROV";
|
|
|
|
|
|
|
|
|
|
Value *resClassArg = hlslOP->GetU8Const(
|
|
|
|
|
static_cast<std::underlying_type<DxilResourceBase::Class>::type>(
|
|
|
|
|
res.GetClass()));
|
|
|
|
|
Value *resIDArg = hlslOP->GetU32Const(res.GetID());
|
|
|
|
|
// resLowerBound will be added after allocation in DxilCondenseResources.
|
|
|
|
|
Value *resLowerBound = hlslOP->GetU32Const(0);
|
|
|
|
|
// TODO: Set Non-uniform resource bit based on whether index comes from IOP_NonUniformResourceIndex.
|
|
|
|
|
Value *isUniformRes = hlslOP->GetI1Const(0);
|
|
|
|
|
|
|
|
|
|
Value *GV = res.GetGlobalSymbol();
|
|
|
|
|
Module *pM = m_pHLModule->GetModule();
|
|
|
|
|
// TODO: add debug info to create handle.
|
|
|
|
|
DIVariable *DIV = nullptr;
|
|
|
|
|
DILocation *DL = nullptr;
|
|
|
|
|
if (m_HasDbgInfo) {
|
|
|
|
|
DebugInfoFinder &Finder = m_pHLModule->GetOrCreateDebugInfoFinder();
|
|
|
|
|
DIV =
|
|
|
|
|
HLModule::FindGlobalVariableDebugInfo(cast<GlobalVariable>(GV), Finder);
|
|
|
|
|
if (DIV)
|
|
|
|
|
// TODO: how to get col?
|
|
|
|
|
DL =
|
|
|
|
|
DILocation::get(pM->getContext(), DIV->getLine(), 1, DIV->getScope());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isResArray = res.GetRangeSize() > 1;
|
|
|
|
|
std::unordered_map<Function *, Instruction *> handleMapOnFunction;
|
|
|
|
|
|
|
|
|
|
Value *createHandleArgs[] = {opArg, resClassArg, resIDArg, resLowerBound,
|
|
|
|
|
isUniformRes};
|
|
|
|
|
|
|
|
|
|
for (iplist<Function>::iterator F : pM->getFunctionList()) {
|
|
|
|
|
if (!F->isDeclaration()) {
|
|
|
|
|
if (!isResArray) {
|
|
|
|
|
IRBuilder<> Builder(F->getEntryBlock().getFirstInsertionPt());
|
|
|
|
|
if (m_HasDbgInfo) {
|
|
|
|
|
// TODO: set debug info.
|
|
|
|
|
//Builder.SetCurrentDebugLocation(DL);
|
|
|
|
|
}
|
|
|
|
|
handleMapOnFunction[F] = Builder.CreateCall(createHandle, createHandleArgs, handleName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto U = GV->user_begin(), E = GV->user_end(); U != E; ) {
|
|
|
|
|
User *user = *(U++);
|
|
|
|
|
// Skip unused user.
|
|
|
|
|
if (user->user_empty())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (LoadInst *ldInst = dyn_cast<LoadInst>(user)) {
|
|
|
|
|
if (UpdateCounterSet.count(ldInst)) {
|
|
|
|
|
DxilResource *resource = llvm::dyn_cast<DxilResource>(&res);
|
|
|
|
|
DXASSERT_NOMSG(resource);
|
|
|
|
|
DXASSERT_NOMSG(resource->GetClass() == DXIL::ResourceClass::UAV);
|
|
|
|
|
resource->SetHasCounter(true);
|
|
|
|
|
}
|
|
|
|
|
Function *userF = ldInst->getParent()->getParent();
|
|
|
|
|
DXASSERT(handleMapOnFunction.count(userF), "must exist");
|
|
|
|
|
Value *handle = handleMapOnFunction[userF];
|
|
|
|
|
ReplaceResourceUserWithHandle(ldInst, handle);
|
|
|
|
|
} else {
|
|
|
|
|
DXASSERT(dyn_cast<GEPOperator>(user) != nullptr,
|
|
|
|
|
"else AddOpcodeParamForIntrinsic in CodeGen did not patch uses "
|
|
|
|
|
"to only have ld/st refer to temp object");
|
|
|
|
|
GEPOperator *GEP = cast<GEPOperator>(user);
|
|
|
|
|
Value *idx = nullptr;
|
|
|
|
|
if (GEP->getNumIndices() == 2) {
|
|
|
|
|
// one dim array of resource
|
|
|
|
|
idx = (GEP->idx_begin() + 1)->get();
|
|
|
|
|
} else {
|
|
|
|
|
gep_type_iterator GEPIt = gep_type_begin(GEP), E = gep_type_end(GEP);
|
|
|
|
|
// Must be instruction for multi dim array.
|
|
|
|
|
std::unique_ptr<IRBuilder<> > Builder;
|
|
|
|
|
if (GetElementPtrInst *GEPInst = dyn_cast<GetElementPtrInst>(GEP)) {
|
|
|
|
|
Builder = std::make_unique<IRBuilder<> >(GEPInst);
|
|
|
|
|
} else {
|
|
|
|
|
Builder = std::make_unique<IRBuilder<> >(GV->getContext());
|
|
|
|
|
}
|
|
|
|
|
for (; GEPIt != E; ++GEPIt) {
|
|
|
|
|
if (GEPIt->isArrayTy()) {
|
|
|
|
|
unsigned arraySize = GEPIt->getArrayNumElements();
|
|
|
|
|
Value * tmpIdx = GEPIt.getOperand();
|
|
|
|
|
if (idx == nullptr)
|
|
|
|
|
idx = tmpIdx;
|
|
|
|
|
else {
|
|
|
|
|
idx = Builder->CreateMul(idx, Builder->getInt32(arraySize));
|
|
|
|
|
idx = Builder->CreateAdd(idx, tmpIdx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
createHandleArgs[DXIL::OperandIndex::kCreateHandleResIndexOpIdx] = idx;
|
|
|
|
|
if (!NonUniformSet.count(idx))
|
|
|
|
|
createHandleArgs[DXIL::OperandIndex::kCreateHandleIsUniformOpIdx] =
|
|
|
|
|
isUniformRes;
|
|
|
|
|
else
|
|
|
|
|
createHandleArgs[DXIL::OperandIndex::kCreateHandleIsUniformOpIdx] =
|
|
|
|
|
hlslOP->GetI1Const(1);
|
|
|
|
|
|
|
|
|
|
Value *handle = nullptr;
|
|
|
|
|
if (GetElementPtrInst *GEPInst = dyn_cast<GetElementPtrInst>(GEP)) {
|
|
|
|
|
IRBuilder<> Builder = IRBuilder<>(GEPInst);
|
|
|
|
|
handle = Builder.CreateCall(createHandle, createHandleArgs, handleName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto GEPU = GEP->user_begin(), GEPE = GEP->user_end(); GEPU != GEPE; ) {
|
|
|
|
|
// Must be load inst.
|
|
|
|
|
LoadInst *ldInst = cast<LoadInst>(*(GEPU++));
|
|
|
|
|
if (UpdateCounterSet.count(ldInst)) {
|
|
|
|
|
DxilResource *resource = dyn_cast<DxilResource>(&res);
|
|
|
|
|
DXASSERT_NOMSG(resource);
|
|
|
|
|
DXASSERT_NOMSG(resource->GetClass() == DXIL::ResourceClass::UAV);
|
|
|
|
|
resource->SetHasCounter(true);
|
|
|
|
|
}
|
|
|
|
|
if (handle) {
|
|
|
|
|
ReplaceResourceUserWithHandle(ldInst, handle);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
IRBuilder<> Builder = IRBuilder<>(ldInst);
|
|
|
|
|
Value *localHandle = Builder.CreateCall(createHandle, createHandleArgs, handleName);
|
|
|
|
|
ReplaceResourceUserWithHandle(ldInst, localHandle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Erase unused handle.
|
|
|
|
|
for (auto It : handleMapOnFunction) {
|
|
|
|
|
Instruction *I = It.second;
|
|
|
|
|
if (I->user_empty())
|
|
|
|
|
I->eraseFromParent();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DxilGenerationPass::GenerateDxilResourceHandles(
|
|
|
|
|
std::unordered_set<LoadInst *> &UpdateCounterSet,
|
|
|
|
|
std::unordered_set<Value *> &NonUniformSet) {
|
|
|
|
|
// Create sampler handle first, may be used by SRV operations.
|
|
|
|
|
for (size_t i = 0; i < m_pHLModule->GetSamplers().size(); i++) {
|
|
|
|
|
DxilSampler &S = m_pHLModule->GetSampler(i);
|
|
|
|
|
TranslateDxilResourceUses(S, UpdateCounterSet, NonUniformSet);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < m_pHLModule->GetSRVs().size(); i++) {
|
|
|
|
|
HLResource &SRV = m_pHLModule->GetSRV(i);
|
|
|
|
|
TranslateDxilResourceUses(SRV, UpdateCounterSet, NonUniformSet);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < m_pHLModule->GetUAVs().size(); i++) {
|
|
|
|
|
HLResource &UAV = m_pHLModule->GetUAV(i);
|
|
|
|
|
TranslateDxilResourceUses(UAV, UpdateCounterSet, NonUniformSet);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
AddResourceToSet(Instruction *Res, std::unordered_set<Instruction *> &resSet) {
|
|
|
|
|
unsigned startOpIdx = 0;
|
|
|
|
|
// Skip Cond for Select.
|
|
|
|
|
if (isa<SelectInst>(Res))
|
|
|
|
|
startOpIdx = 1;
|
|
|
|
|
else if (!isa<PHINode>(Res))
|
|
|
|
|
// Only check phi and select here.
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Already add.
|
|
|
|
|
if (resSet.count(Res))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
resSet.insert(Res);
|
|
|
|
|
|
|
|
|
|
// Scan operand to add resource node which only used by phi/select.
|
|
|
|
|
unsigned numOperands = Res->getNumOperands();
|
|
|
|
|
for (unsigned i = startOpIdx; i < numOperands; i++) {
|
|
|
|
|
Value *V = Res->getOperand(i);
|
|
|
|
|
if (Instruction *I = dyn_cast<Instruction>(V)) {
|
|
|
|
|
AddResourceToSet(I, resSet);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Transform
|
|
|
|
|
//
|
|
|
|
|
// %g_texture_texture_2d1 = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 0, i32 0, i1 false)
|
|
|
|
|
// %g_texture_texture_2d = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 0, i32 2, i1 false)
|
|
|
|
|
// %13 = select i1 %cmp, %dx.types.Handle %g_texture_texture_2d1, %dx.types.Handle %g_texture_texture_2d
|
|
|
|
|
// Into
|
|
|
|
|
// %11 = select i1 %cmp, i32 0, i32 2
|
|
|
|
|
// %12 = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 0, i32 %11, i1 false)
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
static bool MergeHandleOpWithSameValue(Instruction *HandleOp,
|
|
|
|
|
unsigned startOpIdx,
|
|
|
|
|
unsigned numOperands) {
|
|
|
|
|
Value *op0 = nullptr;
|
|
|
|
|
for (unsigned i = startOpIdx; i < numOperands; i++) {
|
|
|
|
|
Value *op = HandleOp->getOperand(i);
|
|
|
|
|
if (i == startOpIdx) {
|
|
|
|
|
op0 = op;
|
|
|
|
|
} else {
|
|
|
|
|
if (op0 != op)
|
|
|
|
|
op0 = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (op0) {
|
|
|
|
|
HandleOp->replaceAllUsesWith(op0);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
UpdateHandleOperands(Instruction *Res,
|
|
|
|
|
std::unordered_map<Instruction *, CallInst *> &handleMap,
|
|
|
|
|
std::unordered_set<Instruction *> &nonUniformOps) {
|
|
|
|
|
unsigned numOperands = Res->getNumOperands();
|
|
|
|
|
|
|
|
|
|
unsigned startOpIdx = 0;
|
|
|
|
|
// Skip Cond for Select.
|
|
|
|
|
if (SelectInst *Sel = dyn_cast<SelectInst>(Res))
|
|
|
|
|
startOpIdx = 1;
|
|
|
|
|
|
|
|
|
|
CallInst *Handle = handleMap[Res];
|
|
|
|
|
|
|
|
|
|
Instruction *resClass = cast<Instruction>(
|
|
|
|
|
Handle->getArgOperand(DXIL::OperandIndex::kCreateHandleResClassOpIdx));
|
|
|
|
|
Instruction *resID = cast<Instruction>(
|
|
|
|
|
Handle->getArgOperand(DXIL::OperandIndex::kCreateHandleResIDOpIdx));
|
|
|
|
|
Instruction *resAddr = cast<Instruction>(
|
|
|
|
|
Handle->getArgOperand(DXIL::OperandIndex::kCreateHandleResIndexOpIdx));
|
|
|
|
|
|
|
|
|
|
for (unsigned i = startOpIdx; i < numOperands; i++) {
|
|
|
|
|
if (!isa<Instruction>(Res->getOperand(i))) {
|
|
|
|
|
EmitResMappingError(Res);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
Instruction *ResOp = cast<Instruction>(Res->getOperand(i));
|
|
|
|
|
CallInst *HandleOp = dyn_cast<CallInst>(ResOp);
|
|
|
|
|
|
|
|
|
|
if (!HandleOp) {
|
|
|
|
|
if (handleMap.count(ResOp)) {
|
|
|
|
|
EmitResMappingError(Res);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
HandleOp = handleMap[ResOp];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Value *resClassOp =
|
|
|
|
|
HandleOp->getArgOperand(DXIL::OperandIndex::kCreateHandleResClassOpIdx);
|
|
|
|
|
Value *resIDOp =
|
|
|
|
|
HandleOp->getArgOperand(DXIL::OperandIndex::kCreateHandleResIDOpIdx);
|
|
|
|
|
Value *resAddrOp =
|
|
|
|
|
HandleOp->getArgOperand(DXIL::OperandIndex::kCreateHandleResIndexOpIdx);
|
|
|
|
|
|
|
|
|
|
resClass->setOperand(i, resClassOp);
|
|
|
|
|
resID->setOperand(i, resIDOp);
|
|
|
|
|
resAddr->setOperand(i, resAddrOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!MergeHandleOpWithSameValue(resClass, startOpIdx, numOperands))
|
|
|
|
|
nonUniformOps.insert(resClass);
|
|
|
|
|
if (!MergeHandleOpWithSameValue(resID, startOpIdx, numOperands))
|
|
|
|
|
nonUniformOps.insert(resID);
|
|
|
|
|
MergeHandleOpWithSameValue(resAddr, startOpIdx, numOperands);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DxilGenerationPass::AddCreateHandleForPhiNodeAndSelect(OP *hlslOP) {
|
|
|
|
|
Function *createHandle = hlslOP->GetOpFunc(
|
|
|
|
|
OP::OpCode::CreateHandle, llvm::Type::getVoidTy(hlslOP->GetCtx()));
|
|
|
|
|
|
|
|
|
|
std::unordered_set<PHINode *> objPhiList;
|
|
|
|
|
std::unordered_set<SelectInst *> objSelectList;
|
|
|
|
|
std::unordered_set<Instruction *> resSelectSet;
|
|
|
|
|
for (User *U : createHandle->users()) {
|
|
|
|
|
for (User *HandleU : U->users()) {
|
|
|
|
|
Instruction *I = cast<Instruction>(HandleU);
|
|
|
|
|
if (!isa<CallInst>(I))
|
|
|
|
|
AddResourceToSet(I, resSelectSet);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate Handle inst for Res inst.
|
|
|
|
|
FunctionType *FT = createHandle->getFunctionType();
|
|
|
|
|
Value *opArg = hlslOP->GetU32Const((unsigned)OP::OpCode::CreateHandle);
|
|
|
|
|
Type *resClassTy =
|
|
|
|
|
FT->getParamType(DXIL::OperandIndex::kCreateHandleResClassOpIdx);
|
|
|
|
|
Type *resIDTy = FT->getParamType(DXIL::OperandIndex::kCreateHandleResIDOpIdx);
|
|
|
|
|
Type *resAddrTy =
|
|
|
|
|
FT->getParamType(DXIL::OperandIndex::kCreateHandleResIndexOpIdx);
|
|
|
|
|
Value *UndefResClass = UndefValue::get(resClassTy);
|
|
|
|
|
Value *UndefResID = UndefValue::get(resIDTy);
|
|
|
|
|
Value *UndefResAddr = UndefValue::get(resAddrTy);
|
|
|
|
|
|
|
|
|
|
// phi/select node resource is not uniform
|
|
|
|
|
Value *nonUniformRes = hlslOP->GetI1Const(1);
|
|
|
|
|
std::unordered_map<Instruction *, CallInst *> handleMap;
|
|
|
|
|
for (Instruction *Res : resSelectSet) {
|
|
|
|
|
unsigned numOperands = Res->getNumOperands();
|
|
|
|
|
IRBuilder<> Builder(Res);
|
|
|
|
|
|
|
|
|
|
unsigned startOpIdx = 0;
|
|
|
|
|
// Skip Cond for Select.
|
|
|
|
|
if (SelectInst *Sel = dyn_cast<SelectInst>(Res)) {
|
|
|
|
|
startOpIdx = 1;
|
|
|
|
|
Value *Cond = Sel->getCondition();
|
|
|
|
|
|
|
|
|
|
Value *resClassSel =
|
|
|
|
|
Builder.CreateSelect(Cond, UndefResClass, UndefResClass);
|
|
|
|
|
Value *resIDSel = Builder.CreateSelect(Cond, UndefResID, UndefResID);
|
|
|
|
|
Value *resAddrSel =
|
|
|
|
|
Builder.CreateSelect(Cond, UndefResAddr, UndefResAddr);
|
|
|
|
|
|
|
|
|
|
CallInst *HandleSel =
|
|
|
|
|
Builder.CreateCall(createHandle, {opArg, resClassSel, resIDSel,
|
|
|
|
|
resAddrSel, nonUniformRes});
|
|
|
|
|
handleMap[Res] = HandleSel;
|
|
|
|
|
Res->replaceAllUsesWith(HandleSel);
|
|
|
|
|
} else {
|
|
|
|
|
PHINode *Phi = cast<PHINode>(Res); // res class must be same.
|
|
|
|
|
PHINode *resClassPhi = Builder.CreatePHI(resClassTy, numOperands);
|
|
|
|
|
PHINode *resIDPhi = Builder.CreatePHI(resIDTy, numOperands);
|
|
|
|
|
PHINode *resAddrPhi = Builder.CreatePHI(resAddrTy, numOperands);
|
|
|
|
|
for (unsigned i = 0; i < numOperands; i++) {
|
|
|
|
|
BasicBlock *BB = Phi->getIncomingBlock(i);
|
|
|
|
|
resClassPhi->addIncoming(UndefResClass, BB);
|
|
|
|
|
resIDPhi->addIncoming(UndefResID, BB);
|
|
|
|
|
resAddrPhi->addIncoming(UndefResAddr, BB);
|
|
|
|
|
}
|
|
|
|
|
IRBuilder<> HandleBuilder(Phi->getParent()->getFirstNonPHI());
|
|
|
|
|
CallInst *HandlePhi =
|
|
|
|
|
HandleBuilder.CreateCall(createHandle, {opArg, resClassPhi, resIDPhi,
|
|
|
|
|
resAddrPhi, nonUniformRes});
|
|
|
|
|
handleMap[Res] = HandlePhi;
|
|
|
|
|
Res->replaceAllUsesWith(HandlePhi);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update operand for Handle phi/select.
|
|
|
|
|
// If ResClass or ResID is phi/select, save to nonUniformOps.
|
|
|
|
|
std::unordered_set<Instruction *> nonUniformOps;
|
|
|
|
|
for (Instruction *Res : resSelectSet) {
|
|
|
|
|
UpdateHandleOperands(Res, handleMap, nonUniformOps);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool bIsLib = m_pHLModule->GetShaderModel()->IsLib();
|
|
|
|
|
|
|
|
|
|
// ResClass and ResID must be uniform.
|
|
|
|
|
// Try to merge res class, res id into imm.
|
|
|
|
|
while (1) {
|
|
|
|
|
bool bUpdated = false;
|
|
|
|
|
|
|
|
|
|
for (auto It = nonUniformOps.begin(); It != nonUniformOps.end();) {
|
|
|
|
|
Instruction *I = *(It++);
|
|
|
|
|
unsigned numOperands = I->getNumOperands();
|
|
|
|
|
|
|
|
|
|
unsigned startOpIdx = 0;
|
|
|
|
|
// Skip Cond for Select.
|
|
|
|
|
if (SelectInst *Sel = dyn_cast<SelectInst>(I))
|
|
|
|
|
startOpIdx = 1;
|
|
|
|
|
if (MergeHandleOpWithSameValue(I, startOpIdx, numOperands)) {
|
|
|
|
|
nonUniformOps.erase(I);
|
|
|
|
|
bUpdated = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!bUpdated) {
|
|
|
|
|
if (!nonUniformOps.empty() && !bIsLib) {
|
|
|
|
|
for (Instruction *I : nonUniformOps) {
|
|
|
|
|
// Non uniform res class or res id.
|
|
|
|
|
EmitResMappingError(I);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove useless select/phi.
|
|
|
|
|
for (Instruction *Res : resSelectSet) {
|
|
|
|
|
Res->eraseFromParent();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DxilGenerationPass::GenerateDxilCBufferHandles(
|
|
|
|
|
std::unordered_set<Value *> &NonUniformSet) {
|
|
|
|
|
// For CBuffer, handle are mapped to HLCreateHandle.
|
|
|
|
@ -1194,171 +800,6 @@ ModulePass *llvm::createDxilGenerationPass(bool NotOptimized, hlsl::HLSLExtensio
|
|
|
|
|
|
|
|
|
|
INITIALIZE_PASS(DxilGenerationPass, "dxilgen", "HLSL DXIL Generation", false, false)
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
StructType *UpdateStructTypeForLegacyLayout(StructType *ST, bool IsCBuf,
|
|
|
|
|
DxilTypeSystem &TypeSys, Module &M);
|
|
|
|
|
|
|
|
|
|
Type *UpdateFieldTypeForLegacyLayout(Type *Ty, bool IsCBuf, DxilFieldAnnotation &annotation,
|
|
|
|
|
DxilTypeSystem &TypeSys, Module &M) {
|
|
|
|
|
DXASSERT(!Ty->isPointerTy(), "struct field should not be a pointer");
|
|
|
|
|
|
|
|
|
|
if (Ty->isArrayTy()) {
|
|
|
|
|
Type *EltTy = Ty->getArrayElementType();
|
|
|
|
|
Type *UpdatedTy = UpdateFieldTypeForLegacyLayout(EltTy, IsCBuf, annotation, TypeSys, M);
|
|
|
|
|
if (EltTy == UpdatedTy)
|
|
|
|
|
return Ty;
|
|
|
|
|
else
|
|
|
|
|
return ArrayType::get(UpdatedTy, Ty->getArrayNumElements());
|
|
|
|
|
} else if (HLMatrixLower::IsMatrixType(Ty)) {
|
|
|
|
|
DXASSERT(annotation.HasMatrixAnnotation(), "must a matrix");
|
|
|
|
|
unsigned rows, cols;
|
|
|
|
|
Type *EltTy = HLMatrixLower::GetMatrixInfo(Ty, cols, rows);
|
|
|
|
|
|
|
|
|
|
// Get cols and rows from annotation.
|
|
|
|
|
const DxilMatrixAnnotation &matrix = annotation.GetMatrixAnnotation();
|
|
|
|
|
if (matrix.Orientation == MatrixOrientation::RowMajor) {
|
|
|
|
|
rows = matrix.Rows;
|
|
|
|
|
cols = matrix.Cols;
|
|
|
|
|
} else {
|
|
|
|
|
DXASSERT(matrix.Orientation == MatrixOrientation::ColumnMajor, "");
|
|
|
|
|
cols = matrix.Rows;
|
|
|
|
|
rows = matrix.Cols;
|
|
|
|
|
}
|
|
|
|
|
// CBuffer matrix must 4 * 4 bytes align.
|
|
|
|
|
if (IsCBuf)
|
|
|
|
|
cols = 4;
|
|
|
|
|
|
|
|
|
|
EltTy = UpdateFieldTypeForLegacyLayout(EltTy, IsCBuf, annotation, TypeSys, M);
|
|
|
|
|
Type *rowTy = VectorType::get(EltTy, cols);
|
|
|
|
|
return ArrayType::get(rowTy, rows);
|
|
|
|
|
} else if (StructType *ST = dyn_cast<StructType>(Ty)) {
|
|
|
|
|
return UpdateStructTypeForLegacyLayout(ST, IsCBuf, TypeSys, M);
|
|
|
|
|
} else if (Ty->isVectorTy()) {
|
|
|
|
|
Type *EltTy = Ty->getVectorElementType();
|
|
|
|
|
Type *UpdatedTy = UpdateFieldTypeForLegacyLayout(EltTy, IsCBuf, annotation, TypeSys, M);
|
|
|
|
|
if (EltTy == UpdatedTy)
|
|
|
|
|
return Ty;
|
|
|
|
|
else
|
|
|
|
|
return VectorType::get(UpdatedTy, Ty->getVectorNumElements());
|
|
|
|
|
} else {
|
|
|
|
|
Type *i32Ty = Type::getInt32Ty(Ty->getContext());
|
|
|
|
|
// Basic types.
|
|
|
|
|
if (Ty->isHalfTy()) {
|
|
|
|
|
return Type::getFloatTy(Ty->getContext());
|
|
|
|
|
} else if (IntegerType *ITy = dyn_cast<IntegerType>(Ty)) {
|
|
|
|
|
if (ITy->getBitWidth() < 32)
|
|
|
|
|
return i32Ty;
|
|
|
|
|
else
|
|
|
|
|
return Ty;
|
|
|
|
|
} else
|
|
|
|
|
return Ty;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StructType *UpdateStructTypeForLegacyLayout(StructType *ST, bool IsCBuf,
|
|
|
|
|
DxilTypeSystem &TypeSys, Module &M) {
|
|
|
|
|
bool bUpdated = false;
|
|
|
|
|
unsigned fieldsCount = ST->getNumElements();
|
|
|
|
|
std::vector<Type *> fieldTypes(fieldsCount);
|
|
|
|
|
DxilStructAnnotation *SA = TypeSys.GetStructAnnotation(ST);
|
|
|
|
|
DXASSERT(SA, "must have annotation for struct type");
|
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < fieldsCount; i++) {
|
|
|
|
|
Type *EltTy = ST->getElementType(i);
|
|
|
|
|
Type *UpdatedTy =
|
|
|
|
|
UpdateFieldTypeForLegacyLayout(EltTy, IsCBuf, SA->GetFieldAnnotation(i), TypeSys, M);
|
|
|
|
|
fieldTypes[i] = UpdatedTy;
|
|
|
|
|
if (EltTy != UpdatedTy)
|
|
|
|
|
bUpdated = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!bUpdated) {
|
|
|
|
|
return ST;
|
|
|
|
|
} else {
|
|
|
|
|
std::string legacyName = "dx.alignment.legacy." + ST->getName().str();
|
|
|
|
|
if (StructType *legacyST = M.getTypeByName(legacyName))
|
|
|
|
|
return legacyST;
|
|
|
|
|
|
|
|
|
|
StructType *NewST = StructType::create(ST->getContext(), fieldTypes, legacyName);
|
|
|
|
|
DxilStructAnnotation *NewSA = TypeSys.AddStructAnnotation(NewST);
|
|
|
|
|
// Clone annotation.
|
|
|
|
|
*NewSA = *SA;
|
|
|
|
|
return NewST;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UpdateStructTypeForLegacyLayout(DxilResourceBase &Res, DxilTypeSystem &TypeSys, Module &M) {
|
|
|
|
|
GlobalVariable *GV = cast<GlobalVariable>(Res.GetGlobalSymbol());
|
|
|
|
|
Type *Ty = GV->getType()->getPointerElementType();
|
|
|
|
|
bool IsResourceArray = Res.GetRangeSize() != 1;
|
|
|
|
|
if (IsResourceArray) {
|
|
|
|
|
// Support Array of struct buffer.
|
|
|
|
|
if (Ty->isArrayTy())
|
|
|
|
|
Ty = Ty->getArrayElementType();
|
|
|
|
|
}
|
|
|
|
|
StructType *ST = cast<StructType>(Ty);
|
|
|
|
|
if (ST->isOpaque()) {
|
|
|
|
|
DXASSERT(Res.GetClass() == DxilResourceBase::Class::CBuffer,
|
|
|
|
|
"Only cbuffer can have opaque struct.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Type *UpdatedST = UpdateStructTypeForLegacyLayout(ST, IsResourceArray, TypeSys, M);
|
|
|
|
|
if (ST != UpdatedST) {
|
|
|
|
|
Type *Ty = GV->getType()->getPointerElementType();
|
|
|
|
|
if (IsResourceArray) {
|
|
|
|
|
// Support Array of struct buffer.
|
|
|
|
|
if (Ty->isArrayTy()) {
|
|
|
|
|
UpdatedST = ArrayType::get(UpdatedST, Ty->getArrayNumElements());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
GlobalVariable *NewGV = cast<GlobalVariable>(M.getOrInsertGlobal(GV->getName().str() + "_legacy", UpdatedST));
|
|
|
|
|
Res.SetGlobalSymbol(NewGV);
|
|
|
|
|
// Delete old GV.
|
|
|
|
|
for (auto UserIt = GV->user_begin(); UserIt != GV->user_end(); ) {
|
|
|
|
|
Value *User = *(UserIt++);
|
|
|
|
|
if (Instruction *I = dyn_cast<Instruction>(User)) {
|
|
|
|
|
if (!User->user_empty())
|
|
|
|
|
I->replaceAllUsesWith(UndefValue::get(I->getType()));
|
|
|
|
|
|
|
|
|
|
I->eraseFromParent();
|
|
|
|
|
} else {
|
|
|
|
|
ConstantExpr *CE = cast<ConstantExpr>(User);
|
|
|
|
|
if (!CE->user_empty())
|
|
|
|
|
CE->replaceAllUsesWith(UndefValue::get(CE->getType()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
GV->removeDeadConstantUsers();
|
|
|
|
|
GV->eraseFromParent();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UpdateStructTypeForLegacyLayoutOnHLM(HLModule &HLM) {
|
|
|
|
|
DxilTypeSystem &TypeSys = HLM.GetTypeSystem();
|
|
|
|
|
Module &M = *HLM.GetModule();
|
|
|
|
|
for (auto &CBuf : HLM.GetCBuffers()) {
|
|
|
|
|
UpdateStructTypeForLegacyLayout(*CBuf.get(), TypeSys, M);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto &UAV : HLM.GetUAVs()) {
|
|
|
|
|
if (UAV->GetKind() == DxilResourceBase::Kind::StructuredBuffer)
|
|
|
|
|
UpdateStructTypeForLegacyLayout(*UAV.get(), TypeSys, M);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto &SRV : HLM.GetSRVs()) {
|
|
|
|
|
if (SRV->GetKind() == DxilResourceBase::Kind::StructuredBuffer)
|
|
|
|
|
UpdateStructTypeForLegacyLayout(*SRV.get(), TypeSys, M);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DxilGenerationPass::UpdateStructTypeForLegacyLayout() {
|
|
|
|
|
UpdateStructTypeForLegacyLayoutOnHLM(*m_pHLModule);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|