blob: 4ec5c6ccf97076a435743a22d073c1005c7b2d28 [file] [edit]
///////////////////////////////////////////////////////////////////////////////
// //
// DxilGenerationPass.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. //
// //
// DxilGenerationPass implementation. //
// //
///////////////////////////////////////////////////////////////////////////////
#include "dxc/HLSL/DxilGenerationPass.h"
#include "dxc/HLSL/DxilOperations.h"
#include "dxc/HLSL/DxilModule.h"
#include "dxc/HLSL/HLModule.h"
#include "dxc/HLSL/HLOperations.h"
#include "dxc/HLSL/HLMatrixLowerHelper.h"
#include "dxc/HlslIntrinsicOp.h"
#include "dxc/Support/Global.h"
#include "dxc/HLSL/DxilTypeSystem.h"
#include "dxc/HLSL/HLOperationLower.h"
#include "HLSignatureLower.h"
#include "dxc/HLSL/DxilUtil.h"
#include "dxc/Support/exception.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/SSAUpdater.h"
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/Transforms/Utils/PromoteMemToReg.h"
#include <memory>
#include <unordered_set>
#include <iterator>
using namespace llvm;
using namespace hlsl;
// TODO: use hlsl namespace for the most of this file.
namespace {
// Collect unused phi of resources and remove them.
class ResourceRemover : public LoadAndStorePromoter {
AllocaInst *AI;
mutable std::unordered_set<PHINode *> unusedPhis;
public:
ResourceRemover(ArrayRef<Instruction *> Insts, SSAUpdater &S)
: LoadAndStorePromoter(Insts, S), AI(nullptr) {}
void run(AllocaInst *AI, const SmallVectorImpl<Instruction *> &Insts) {
// Remember which alloca we're promoting (for isInstInList).
this->AI = AI;
LoadAndStorePromoter::run(Insts);
for (PHINode *P : unusedPhis) {
P->eraseFromParent();
}
}
bool
isInstInList(Instruction *I,
const SmallVectorImpl<Instruction *> &Insts) const override {
if (LoadInst *LI = dyn_cast<LoadInst>(I))
return LI->getOperand(0) == AI;
return cast<StoreInst>(I)->getPointerOperand() == AI;
}
void replaceLoadWithValue(LoadInst *LI, Value *V) const override {
if (PHINode *PHI = dyn_cast<PHINode>(V)) {
if (PHI->user_empty())
unusedPhis.insert(PHI);
}
LI->replaceAllUsesWith(UndefValue::get(LI->getType()));
}
};
void InitResourceBase(const DxilResourceBase *pSource, DxilResourceBase *pDest) {
DXASSERT_NOMSG(pSource->GetClass() == pDest->GetClass());
pDest->SetKind(pSource->GetKind());
pDest->SetID(pSource->GetID());
pDest->SetSpaceID(pSource->GetSpaceID());
pDest->SetLowerBound(pSource->GetLowerBound());
pDest->SetRangeSize(pSource->GetRangeSize());
pDest->SetGlobalSymbol(pSource->GetGlobalSymbol());
pDest->SetGlobalName(pSource->GetGlobalName());
pDest->SetHandle(pSource->GetHandle());
}
void InitResource(const DxilResource *pSource, DxilResource *pDest) {
pDest->SetCompType(pSource->GetCompType());
pDest->SetSampleCount(pSource->GetSampleCount());
pDest->SetElementStride(pSource->GetElementStride());
pDest->SetGloballyCoherent(pSource->IsGloballyCoherent());
pDest->SetHasCounter(pSource->HasCounter());
pDest->SetRW(pSource->IsRW());
pDest->SetROV(pSource->IsROV());
InitResourceBase(pSource, pDest);
}
void InitDxilModuleFromHLModule(HLModule &H, DxilModule &M, DxilEntrySignature *pSig, bool HasDebugInfo) {
std::unique_ptr<DxilEntrySignature> pSigPtr(pSig);
// Subsystems.
unsigned ValMajor, ValMinor;
H.GetValidatorVersion(ValMajor, ValMinor);
M.SetValidatorVersion(ValMajor, ValMinor);
M.SetShaderModel(H.GetShaderModel());
// Entry function.
Function *EntryFn = H.GetEntryFunction();
DxilFunctionProps *FnProps = H.HasDxilFunctionProps(EntryFn) ? &H.GetDxilFunctionProps(EntryFn) : nullptr;
M.SetEntryFunction(EntryFn);
M.SetEntryFunctionName(H.GetEntryFunctionName());
std::vector<GlobalVariable* > &LLVMUsed = M.GetLLVMUsed();
// Resources
for (auto && C : H.GetCBuffers()) {
auto b = make_unique<DxilCBuffer>();
InitResourceBase(C.get(), b.get());
b->SetSize(C->GetSize());
if (HasDebugInfo)
LLVMUsed.emplace_back(cast<GlobalVariable>(b->GetGlobalSymbol()));
b->SetGlobalSymbol(UndefValue::get(b->GetGlobalSymbol()->getType()));
M.AddCBuffer(std::move(b));
}
for (auto && C : H.GetUAVs()) {
auto b = make_unique<DxilResource>();
InitResource(C.get(), b.get());
if (HasDebugInfo)
LLVMUsed.emplace_back(cast<GlobalVariable>(b->GetGlobalSymbol()));
b->SetGlobalSymbol(UndefValue::get(b->GetGlobalSymbol()->getType()));
M.AddUAV(std::move(b));
}
for (auto && C : H.GetSRVs()) {
auto b = make_unique<DxilResource>();
InitResource(C.get(), b.get());
if (HasDebugInfo)
LLVMUsed.emplace_back(cast<GlobalVariable>(b->GetGlobalSymbol()));
b->SetGlobalSymbol(UndefValue::get(b->GetGlobalSymbol()->getType()));
M.AddSRV(std::move(b));
}
for (auto && C : H.GetSamplers()) {
auto b = make_unique<DxilSampler>();
InitResourceBase(C.get(), b.get());
b->SetSamplerKind(C->GetSamplerKind());
if (HasDebugInfo)
LLVMUsed.emplace_back(cast<GlobalVariable>(b->GetGlobalSymbol()));
b->SetGlobalSymbol(UndefValue::get(b->GetGlobalSymbol()->getType()));
M.AddSampler(std::move(b));
}
// Signatures.
M.ResetEntrySignature(pSigPtr.release());
M.ResetRootSignature(H.ReleaseRootSignature());
// Shader properties.
//bool m_bDisableOptimizations;
M.m_ShaderFlags.SetDisableOptimizations(H.GetHLOptions().bDisableOptimizations);
//bool m_bDisableMathRefactoring;
//bool m_bEnableDoublePrecision;
//bool m_bEnableDoubleExtensions;
//M.CollectShaderFlags();
//bool m_bForceEarlyDepthStencil;
//bool m_bEnableRawAndStructuredBuffers;
//bool m_bEnableMSAD;
//M.m_ShaderFlags.SetAllResourcesBound(H.GetHLOptions().bAllResourcesBound);
M.m_ShaderFlags.SetUseNativeLowPrecision(!H.GetHLOptions().bUseMinPrecision);
if (FnProps)
M.SetShaderProperties(FnProps);
// Move function props.
if (M.GetShaderModel()->IsLib())
M.ResetFunctionPropsMap(H.ReleaseFunctionPropsMap());
// DXIL type system.
M.ResetTypeSystem(H.ReleaseTypeSystem());
// Dxil OP.
M.ResetOP(H.ReleaseOP());
// Keep llvm used.
M.EmitLLVMUsed();
M.m_ShaderFlags.SetAllResourcesBound(H.GetHLOptions().bAllResourcesBound);
// Update Validator Version
M.UpgradeToMinValidatorVersion();
}
class DxilGenerationPass : public ModulePass {
HLModule *m_pHLModule;
bool m_HasDbgInfo;
HLSLExtensionsCodegenHelper *m_extensionsCodegenHelper;
public:
static char ID; // Pass identification, replacement for typeid
explicit DxilGenerationPass(bool NoOpt = false)
: ModulePass(ID), m_pHLModule(nullptr), NotOptimized(NoOpt), m_extensionsCodegenHelper(nullptr) {}
const char *getPassName() const override { return "DXIL Generator"; }
void SetExtensionsHelper(HLSLExtensionsCodegenHelper *helper) {
m_extensionsCodegenHelper = helper;
}
bool runOnModule(Module &M) override {
m_pHLModule = &M.GetOrCreateHLModule();
const ShaderModel *SM = m_pHLModule->GetShaderModel();
// Load up debug information, to cross-reference values and the instructions
// used to load them.
m_HasDbgInfo = getDebugMetadataVersionFromModule(M) != 0;
std::unique_ptr<DxilEntrySignature> pSig =
llvm::make_unique<DxilEntrySignature>(SM->GetKind(), M.GetHLModule().GetHLOptions().bUseMinPrecision);
// EntrySig for shader functions.
std::unordered_map<llvm::Function *, std::unique_ptr<DxilEntrySignature>>
DxilEntrySignatureMap;
if (!SM->IsLib()) {
HLSignatureLower sigLower(m_pHLModule->GetEntryFunction(), *m_pHLModule,
*pSig);
sigLower.Run();
} else {
for (auto It = M.begin(); It != M.end();) {
Function &F = *(It++);
// Lower signature for each entry function.
if (m_pHLModule->HasDxilFunctionProps(&F)) {
DxilFunctionProps &props = m_pHLModule->GetDxilFunctionProps(&F);
std::unique_ptr<DxilEntrySignature> pSig =
llvm::make_unique<DxilEntrySignature>(props.shaderKind, m_pHLModule->GetHLOptions().bUseMinPrecision);
HLSignatureLower sigLower(&F, *m_pHLModule, *pSig);
sigLower.Run();
DxilEntrySignatureMap[&F] = std::move(pSig);
}
}
}
std::unordered_set<LoadInst *> UpdateCounterSet;
std::unordered_set<Value *> NonUniformSet;
GenerateDxilOperations(M, UpdateCounterSet, NonUniformSet);
std::unordered_map<Instruction *, Value *> handleMap;
GenerateDxilCBufferHandles(NonUniformSet);
GenerateParamDxilResourceHandles(handleMap);
GenerateDxilResourceHandles(UpdateCounterSet, NonUniformSet);
AddCreateHandleForPhiNodeAndSelect(m_pHLModule->GetOP());
// For module which not promote mem2reg.
// Remove local resource alloca/load/store/phi.
for (auto It = M.begin(); It != M.end();) {
Function &F = *(It++);
if (!F.isDeclaration()) {
RemoveLocalDxilResourceAllocas(&F);
if (hlsl::GetHLOpcodeGroupByName(&F) == HLOpcodeGroup::HLCreateHandle) {
if (F.user_empty()) {
F.eraseFromParent();
} else {
M.getContext().emitError("Fail to lower createHandle.");
}
}
}
}
// Translate precise on allocas into function call to keep the information after mem2reg.
// The function calls will be removed after propagate precise attribute.
TranslatePreciseAttribute();
// Change struct type to legacy layout for cbuf and struct buf for min precision data types.
if (M.GetHLModule().GetHLOptions().bUseMinPrecision)
UpdateStructTypeForLegacyLayout();
// High-level metadata should now be turned into low-level metadata.
const bool SkipInit = true;
hlsl::DxilModule &DxilMod = M.GetOrCreateDxilModule(SkipInit);
InitDxilModuleFromHLModule(*m_pHLModule, DxilMod, pSig.release(),
m_HasDbgInfo);
if (SM->IsLib())
DxilMod.ResetEntrySignatureMap(std::move(DxilEntrySignatureMap));
HLModule::ClearHLMetadata(M);
M.ResetHLModule();
// We now have a DXIL representation - record this.
SetPauseResumePasses(M, "hlsl-dxilemit", "hlsl-dxilload");
// Remove debug code when not debug info.
if (!m_HasDbgInfo)
DxilMod.StripDebugRelatedCode();
return true;
}
private:
void RemoveLocalDxilResourceAllocas(Function *F);
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);
// Generate DXIL cbuffer handles.
void
GenerateDxilCBufferHandles(std::unordered_set<Value *> &NonUniformSet);
// change built-in funtion into DXIL operations
void GenerateDxilOperations(Module &M,
std::unordered_set<LoadInst *> &UpdateCounterSet,
std::unordered_set<Value *> &NonUniformSet);
// Change struct type to legacy layout for cbuf and struct buf.
void UpdateStructTypeForLegacyLayout();
// Translate precise attribute into HL function call.
void TranslatePreciseAttribute();
// Input module is not optimized.
bool NotOptimized;
};
}
static Value *MergeImmResClass(Value *resClass) {
if (ConstantInt *Imm = dyn_cast<ConstantInt>(resClass)) {
return resClass;
} else {
PHINode *phi = cast<PHINode>(resClass);
Value *immResClass = MergeImmResClass(phi->getIncomingValue(0));
unsigned numOperands = phi->getNumOperands();
for (unsigned i=0;i<numOperands;i++)
phi->setIncomingValue(i, immResClass);
return immResClass;
}
}
static const StringRef kResourceMapErrorMsg = "local resource not guaranteed to map to unique global resource.";
static void EmitResMappingError(Instruction *Res) {
const DebugLoc &DL = Res->getDebugLoc();
if (DL.get()) {
Res->getContext().emitError("line:" + std::to_string(DL.getLine()) +
" col:" + std::to_string(DL.getCol()) + " " +
Twine(kResourceMapErrorMsg));
} else {
Res->getContext().emitError(Twine(kResourceMapErrorMsg) + " With /Zi to show more information.");
}
}
static Value *SelectOnOperand(Value *Cond, CallInst *CIT, CallInst *CIF,
unsigned idx, IRBuilder<> &Builder) {
Value *OpT = CIT->getArgOperand(idx);
Value *OpF = CIF->getArgOperand(idx);
Value *OpSel = OpT;
if (OpT != OpF) {
OpSel = Builder.CreateSelect(Cond, OpT, OpF);
}
return OpSel;
}
static void ReplaceResourceUserWithHandle(LoadInst *Res, Value *handle) {
for (auto resUser = Res->user_begin(); resUser != Res->user_end();) {
CallInst *CI = dyn_cast<CallInst>(*(resUser++));
DXASSERT(GetHLOpcodeGroupByName(CI->getCalledFunction()) ==
HLOpcodeGroup::HLCreateHandle,
"must be createHandle");
CI->replaceAllUsesWith(handle);
CI->eraseFromParent();
}
Res->eraseFromParent();
}
static bool IsResourceType(Type *Ty) {
bool isResource = HLModule::IsHLSLObjectType(Ty);
if (ArrayType *AT = dyn_cast<ArrayType>(Ty)) {
Type *EltTy = AT->getElementType();
while (isa<ArrayType>(EltTy)) {
EltTy = EltTy->getArrayElementType();
}
isResource = HLModule::IsHLSLObjectType(EltTy);
// TODO: support local resource array.
DXASSERT(!isResource, "local resource array");
}
return isResource;
}
void DxilGenerationPass::RemoveLocalDxilResourceAllocas(Function *F) {
BasicBlock &BB = F->getEntryBlock(); // Get the entry node for the function
std::unordered_set<AllocaInst *> localResources;
for (BasicBlock::iterator I = BB.begin(), E = --BB.end(); I != E; ++I)
if (AllocaInst *AI = dyn_cast<AllocaInst>(I)) { // Is it an alloca?
if (IsResourceType(AI->getAllocatedType())) {
localResources.insert(AI);
}
}
SSAUpdater SSA;
SmallVector<Instruction *, 4> Insts;
for (AllocaInst *AI : localResources) {
// Build list of instructions to promote.
for (User *U : AI->users())
Insts.emplace_back(cast<Instruction>(U));
ResourceRemover(Insts, SSA).run(AI, Insts);
Insts.clear();
}
}
void DxilGenerationPass::TranslateParamDxilResourceHandles(Function *F, std::unordered_map<Instruction *, Value *> &handleMap) {
Type *handleTy = m_pHLModule->GetOP()->GetHandleType();
IRBuilder<> Builder(F->getEntryBlock().getFirstInsertionPt());
for (Argument &arg : F->args()) {
Type *Ty = arg.getType();
if (isa<PointerType>(Ty))
Ty = Ty->getPointerElementType();
SmallVector<unsigned,4> arraySizeList;
while (isa<ArrayType>(Ty)) {
arraySizeList.push_back(Ty->getArrayNumElements());
Ty = Ty->getArrayElementType();
}
DXIL::ResourceClass RC = m_pHLModule->GetResourceClass(Ty);
if (RC != DXIL::ResourceClass::Invalid) {
Type *curTy = handleTy;
for (auto it = arraySizeList.rbegin(), E = arraySizeList.rend(); it != E;
it++) {
curTy = ArrayType::get(curTy, *it);
}
curTy = PointerType::get(curTy, 0);
CallInst *castToHandle = cast<CallInst>(HLModule::EmitHLOperationCall(
Builder, HLOpcodeGroup::HLCast, 0, curTy,
{UndefValue::get(arg.getType())}, *F->getParent()));
for (User *U : arg.users()) {
Instruction *I = cast<Instruction>(U);
IRBuilder<> userBuilder(I);
if (LoadInst *ldInst = dyn_cast<LoadInst>(U)) {
Value *handleLd = userBuilder.CreateLoad(castToHandle);
handleMap[ldInst] = handleLd;
} else if (StoreInst *stInst = dyn_cast<StoreInst>(U)) {
Value *res = stInst->getValueOperand();
Value *handle = HLModule::EmitHLOperationCall(
userBuilder, HLOpcodeGroup::HLCast, 0, handleTy, {res},
*F->getParent());
userBuilder.CreateStore(handle, castToHandle);
} else if (CallInst *CI = dyn_cast<CallInst>(U)) {
// Don't flatten argument here.
continue;
} else {
DXASSERT(
dyn_cast<GEPOperator>(U) != nullptr,
"else AddOpcodeParamForIntrinsic in CodeGen did not patch uses "
"to only have ld/st refer to temp object");
GEPOperator *GEP = cast<GEPOperator>(U);
std::vector<Value *> idxList(GEP->idx_begin(), GEP->idx_end());
Value *handleGEP = userBuilder.CreateGEP(castToHandle, idxList);
for (auto GEPU : GEP->users()) {
Instruction *GEPI = cast<Instruction>(GEPU);
IRBuilder<> gepUserBuilder(GEPI);
if (LoadInst *ldInst = dyn_cast<LoadInst>(GEPU)) {
handleMap[ldInst] = gepUserBuilder.CreateLoad(handleGEP);
} else {
StoreInst *stInst = cast<StoreInst>(GEPU);
Value *res = stInst->getValueOperand();
Value *handle = HLModule::EmitHLOperationCall(
gepUserBuilder, HLOpcodeGroup::HLCast, 0, handleTy, {res},
*F->getParent());
gepUserBuilder.CreateStore(handle, handleGEP);
}
}
}
}
castToHandle->setArgOperand(0, &arg);
}
}
}
void DxilGenerationPass::GenerateParamDxilResourceHandles(
std::unordered_map<Instruction *, Value *> &handleMap) {
Module &M = *m_pHLModule->GetModule();
for (Function &F : M.functions()) {
if (!F.isDeclaration())
TranslateParamDxilResourceHandles(&F, handleMap);
}
}
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.
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);
Value *resClassArg = hlslOP->GetU8Const(
static_cast<std::underlying_type<DxilResourceBase::Class>::type>(
DXIL::ResourceClass::CBuffer));
for (size_t i = 0; i < m_pHLModule->GetCBuffers().size(); i++) {
DxilCBuffer &CB = m_pHLModule->GetCBuffer(i);
GlobalVariable *GV = cast<GlobalVariable>(CB.GetGlobalSymbol());
// Remove GEP created in HLObjectOperationLowerHelper::UniformCbPtr.
GV->removeDeadConstantUsers();
std::string handleName = std::string(GV->getName()) + "_buffer";
Value *args[] = {opArg, resClassArg, nullptr, nullptr,
hlslOP->GetI1Const(0)};
DIVariable *DIV = nullptr;
DILocation *DL = nullptr;
if (m_HasDbgInfo) {
DebugInfoFinder &Finder = m_pHLModule->GetOrCreateDebugInfoFinder();
DIV = HLModule::FindGlobalVariableDebugInfo(GV, Finder);
if (DIV)
// TODO: how to get col?
DL = DILocation::get(createHandle->getContext(), DIV->getLine(), 1,
DIV->getScope());
}
Value *resIDArg = hlslOP->GetU32Const(CB.GetID());
args[DXIL::OperandIndex::kCreateHandleResIDOpIdx] = resIDArg;
// resLowerBound will be added after allocation in DxilCondenseResources.
Value *resLowerBound = hlslOP->GetU32Const(0);
if (CB.GetRangeSize() == 1) {
args[DXIL::OperandIndex::kCreateHandleResIndexOpIdx] = resLowerBound;
for (auto U = GV->user_begin(); U != GV->user_end(); ) {
// Must HLCreateHandle.
CallInst *CI = cast<CallInst>(*(U++));
// Put createHandle to entry block.
auto InsertPt =
CI->getParent()->getParent()->getEntryBlock().getFirstInsertionPt();
IRBuilder<> Builder(InsertPt);
CallInst *handle = Builder.CreateCall(createHandle, args, handleName);
if (m_HasDbgInfo) {
// TODO: add debug info.
//handle->setDebugLoc(DL);
}
CI->replaceAllUsesWith(handle);
CI->eraseFromParent();
}
} else {
for (auto U = GV->user_begin(); U != GV->user_end(); ) {
// Must HLCreateHandle.
CallInst *CI = cast<CallInst>(*(U++));
IRBuilder<> Builder(CI);
Value *CBIndex = CI->getArgOperand(HLOperandIndex::kCreateHandleIndexOpIdx);
args[DXIL::OperandIndex::kCreateHandleResIndexOpIdx] =
CBIndex;
if (isa<ConstantInt>(CBIndex)) {
// Put createHandle to entry block for const index.
auto InsertPt = CI->getParent()
->getParent()
->getEntryBlock()
.getFirstInsertionPt();
Builder.SetInsertPoint(InsertPt);
}
if (!NonUniformSet.count(CBIndex))
args[DXIL::OperandIndex::kCreateHandleIsUniformOpIdx] =
hlslOP->GetI1Const(0);
else
args[DXIL::OperandIndex::kCreateHandleIsUniformOpIdx] =
hlslOP->GetI1Const(1);
CallInst *handle = Builder.CreateCall(createHandle, args, handleName);
CI->replaceAllUsesWith(handle);
CI->eraseFromParent();
}
}
}
}
void DxilGenerationPass::GenerateDxilOperations(
Module &M, std::unordered_set<LoadInst *> &UpdateCounterSet,
std::unordered_set<Value *> &NonUniformSet) {
// remove all functions except entry function
Function *entry = m_pHLModule->GetEntryFunction();
const ShaderModel *pSM = m_pHLModule->GetShaderModel();
Function *patchConstantFunc = nullptr;
if (pSM->IsHS()) {
DxilFunctionProps &funcProps = m_pHLModule->GetDxilFunctionProps(entry);
patchConstantFunc = funcProps.ShaderProps.HS.patchConstantFunc;
}
if (!pSM->IsLib()) {
for (auto F = M.begin(); F != M.end();) {
Function *func = F++;
if (func->isDeclaration())
continue;
if (func == entry)
continue;
if (func == patchConstantFunc)
continue;
if (func->user_empty())
func->eraseFromParent();
}
}
TranslateBuiltinOperations(*m_pHLModule, m_extensionsCodegenHelper,
UpdateCounterSet, NonUniformSet);
// Remove unused HL Operation functions.
std::vector<Function *> deadList;
for (iplist<Function>::iterator F : M.getFunctionList()) {
hlsl::HLOpcodeGroup group = hlsl::GetHLOpcodeGroupByName(F);
if (group != HLOpcodeGroup::NotHL || F->isIntrinsic())
if (F->user_empty())
deadList.emplace_back(F);
}
for (Function *F : deadList)
F->eraseFromParent();
}
static void TranslatePreciseAttributeOnFunction(Function &F, Module &M) {
BasicBlock &BB = F.getEntryBlock(); // Get the entry node for the function
// Find allocas that has precise attribute, by looking at all instructions in
// the entry node
for (BasicBlock::iterator I = BB.begin(), E = BB.end(); I != E;) {
Instruction *Inst = (I++);
if (AllocaInst *AI = dyn_cast<AllocaInst>(Inst)) {
if (HLModule::HasPreciseAttributeWithMetadata(AI)) {
HLModule::MarkPreciseAttributeOnPtrWithFunctionCall(AI, M);
}
} else {
DXASSERT(!HLModule::HasPreciseAttributeWithMetadata(Inst), "Only alloca can has precise metadata.");
}
}
FastMathFlags FMF;
FMF.setUnsafeAlgebra();
// Set fast math for all FPMathOperators.
// Already set FastMath in options. But that only enable things like fadd.
// Every inst which type is float can be cast to FPMathOperator.
for (Function::iterator BBI = F.begin(), BBE = F.end(); BBI != BBE; ++BBI) {
BasicBlock *BB = BBI;
for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ++I) {
if (FPMathOperator *FPMath = dyn_cast<FPMathOperator>(I)) {
// Set precise fast math on those instructions that support it.
if (DxilModule::PreservesFastMathFlags(I))
I->copyFastMathFlags(FMF);
}
}
}
}
void DxilGenerationPass::TranslatePreciseAttribute() {
bool bIEEEStrict = m_pHLModule->GetHLOptions().bIEEEStrict;
// If IEEE strict, everying is precise, don't need to mark it.
if (bIEEEStrict)
return;
Module &M = *m_pHLModule->GetModule();
// TODO: If not inline every function, for function has call site with precise
// argument and call site without precise argument, need to clone the function
// to propagate the precise for the precise call site.
// This should be done at CGMSHLSLRuntime::FinishCodeGen.
Function *EntryFn = m_pHLModule->GetEntryFunction();
if (!m_pHLModule->GetShaderModel()->IsLib()) {
TranslatePreciseAttributeOnFunction(*EntryFn, M);
}
if (m_pHLModule->GetShaderModel()->IsHS()) {
DxilFunctionProps &EntryQual = m_pHLModule->GetDxilFunctionProps(EntryFn);
Function *patchConstantFunc = EntryQual.ShaderProps.HS.patchConstantFunc;
TranslatePreciseAttributeOnFunction(*patchConstantFunc, M);
}
}
char DxilGenerationPass::ID = 0;
ModulePass *llvm::createDxilGenerationPass(bool NotOptimized, hlsl::HLSLExtensionsCodegenHelper *extensionsHelper) {
DxilGenerationPass *dxilPass = new DxilGenerationPass(NotOptimized);
dxilPass->SetExtensionsHelper(extensionsHelper);
return dxilPass;
}
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);
}
///////////////////////////////////////////////////////////////////////////////
namespace {
class HLEmitMetadata : public ModulePass {
public:
static char ID; // Pass identification, replacement for typeid
explicit HLEmitMetadata() : ModulePass(ID) {}
const char *getPassName() const override { return "HLSL High-Level Metadata Emit"; }
bool runOnModule(Module &M) override {
if (M.HasHLModule()) {
HLModule::ClearHLMetadata(M);
M.GetHLModule().EmitHLMetadata();
return true;
}
return false;
}
};
}
char HLEmitMetadata::ID = 0;
ModulePass *llvm::createHLEmitMetadataPass() {
return new HLEmitMetadata();
}
INITIALIZE_PASS(HLEmitMetadata, "hlsl-hlemit", "HLSL High-Level Metadata Emit", false, false)
///////////////////////////////////////////////////////////////////////////////
namespace {
class HLEnsureMetadata : public ModulePass {
public:
static char ID; // Pass identification, replacement for typeid
explicit HLEnsureMetadata() : ModulePass(ID) {}
const char *getPassName() const override { return "HLSL High-Level Metadata Ensure"; }
bool runOnModule(Module &M) override {
if (!M.HasHLModule()) {
M.GetOrCreateHLModule();
return true;
}
return false;
}
};
}
char HLEnsureMetadata::ID = 0;
ModulePass *llvm::createHLEnsureMetadataPass() {
return new HLEnsureMetadata();
}
INITIALIZE_PASS(HLEnsureMetadata, "hlsl-hlensure", "HLSL High-Level Metadata Ensure", false, false)
///////////////////////////////////////////////////////////////////////////////
// Precise propagate.
namespace {
class DxilPrecisePropagatePass : public ModulePass {
HLModule *m_pHLModule;
public:
static char ID; // Pass identification, replacement for typeid
explicit DxilPrecisePropagatePass() : ModulePass(ID), m_pHLModule(nullptr) {}
const char *getPassName() const override { return "DXIL Precise Propagate"; }
bool runOnModule(Module &M) override {
DxilModule &dxilModule = M.GetOrCreateDxilModule();
DxilTypeSystem &typeSys = dxilModule.GetTypeSystem();
std::unordered_set<Instruction*> processedSet;
std::vector<Function*> deadList;
for (Function &F : M.functions()) {
if (HLModule::HasPreciseAttribute(&F)) {
PropagatePreciseOnFunctionUser(F, typeSys, processedSet);
deadList.emplace_back(&F);
}
}
for (Function *F : deadList)
F->eraseFromParent();
return true;
}
private:
void PropagatePreciseOnFunctionUser(
Function &F, DxilTypeSystem &typeSys,
std::unordered_set<Instruction *> &processedSet);
};
char DxilPrecisePropagatePass::ID = 0;
}
static void PropagatePreciseAttribute(Instruction *I, DxilTypeSystem &typeSys,
std::unordered_set<Instruction *> &processedSet);
static void PropagatePreciseAttributeOnOperand(
Value *V, DxilTypeSystem &typeSys, LLVMContext &Context,
std::unordered_set<Instruction *> &processedSet) {
Instruction *I = dyn_cast<Instruction>(V);
// Skip none inst.
if (!I)
return;
FPMathOperator *FPMath = dyn_cast<FPMathOperator>(I);
// Skip none FPMath
if (!FPMath)
return;
// Skip inst already marked.
if (processedSet.count(I) > 0)
return;
// TODO: skip precise on integer type, sample instruction...
processedSet.insert(I);
// Set precise fast math on those instructions that support it.
if (DxilModule::PreservesFastMathFlags(I))
DxilModule::SetPreciseFastMathFlags(I);
// Fast math not work on call, use metadata.
if (CallInst *CI = dyn_cast<CallInst>(I))
HLModule::MarkPreciseAttributeWithMetadata(CI);
PropagatePreciseAttribute(I, typeSys, processedSet);
}
static void PropagatePreciseAttributeOnPointer(
Value *Ptr, DxilTypeSystem &typeSys, LLVMContext &Context,
std::unordered_set<Instruction *> &processedSet) {
// Find all store and propagate on the val operand of store.
// For CallInst, if Ptr is used as out parameter, mark it.
for (User *U : Ptr->users()) {
Instruction *user = cast<Instruction>(U);
if (StoreInst *stInst = dyn_cast<StoreInst>(user)) {
Value *val = stInst->getValueOperand();
PropagatePreciseAttributeOnOperand(val, typeSys, Context, processedSet);
} else if (CallInst *CI = dyn_cast<CallInst>(user)) {
bool bReadOnly = true;
Function *F = CI->getCalledFunction();
const DxilFunctionAnnotation *funcAnnotation =
typeSys.GetFunctionAnnotation(F);
for (unsigned i = 0; i < CI->getNumArgOperands(); ++i) {
if (Ptr != CI->getArgOperand(i))
continue;
const DxilParameterAnnotation &paramAnnotation =
funcAnnotation->GetParameterAnnotation(i);
// OutputPatch and OutputStream will be checked after scalar repl.
// Here only check out/inout
if (paramAnnotation.GetParamInputQual() == DxilParamInputQual::Out ||
paramAnnotation.GetParamInputQual() == DxilParamInputQual::Inout) {
bReadOnly = false;
break;
}
}
if (!bReadOnly)
PropagatePreciseAttributeOnOperand(CI, typeSys, Context, processedSet);
}
}
}
static void
PropagatePreciseAttribute(Instruction *I, DxilTypeSystem &typeSys,
std::unordered_set<Instruction *> &processedSet) {
LLVMContext &Context = I->getContext();
if (AllocaInst *AI = dyn_cast<AllocaInst>(I)) {
PropagatePreciseAttributeOnPointer(AI, typeSys, Context, processedSet);
} else if (CallInst *CI = dyn_cast<CallInst>(I)) {
// Propagate every argument.
// TODO: only propagate precise argument.
for (Value *src : I->operands())
PropagatePreciseAttributeOnOperand(src, typeSys, Context, processedSet);
} else if (FPMathOperator *FPMath = dyn_cast<FPMathOperator>(I)) {
// TODO: only propagate precise argument.
for (Value *src : I->operands())
PropagatePreciseAttributeOnOperand(src, typeSys, Context, processedSet);
} else if (LoadInst *ldInst = dyn_cast<LoadInst>(I)) {
Value *Ptr = ldInst->getPointerOperand();
PropagatePreciseAttributeOnPointer(Ptr, typeSys, Context, processedSet);
} else if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(I))
PropagatePreciseAttributeOnPointer(GEP, typeSys, Context, processedSet);
// TODO: support more case which need
}
void DxilPrecisePropagatePass::PropagatePreciseOnFunctionUser(
Function &F, DxilTypeSystem &typeSys,
std::unordered_set<Instruction *> &processedSet) {
LLVMContext &Context = F.getContext();
for (auto U = F.user_begin(), E = F.user_end(); U != E;) {
CallInst *CI = cast<CallInst>(*(U++));
Value *V = CI->getArgOperand(0);
PropagatePreciseAttributeOnOperand(V, typeSys, Context, processedSet);
CI->eraseFromParent();
}
}
ModulePass *llvm::createDxilPrecisePropagatePass() {
return new DxilPrecisePropagatePass();
}
INITIALIZE_PASS(DxilPrecisePropagatePass, "hlsl-dxil-precise", "DXIL precise attribute propagate", false, false)
///////////////////////////////////////////////////////////////////////////////
namespace {
class HLDeadFunctionElimination : public ModulePass {
public:
static char ID; // Pass identification, replacement for typeid
explicit HLDeadFunctionElimination () : ModulePass(ID) {}
const char *getPassName() const override { return "Remove all unused function except entry from HLModule"; }
bool runOnModule(Module &M) override {
if (M.HasHLModule()) {
HLModule &HLM = M.GetHLModule();
bool IsLib = HLM.GetShaderModel()->IsLib();
// Remove unused functions except entry and patch constant func.
// For library profile, only remove unused external functions.
Function *EntryFunc = HLM.GetEntryFunction();
Function *PatchConstantFunc = HLM.GetPatchConstantFunction();
return dxilutil::RemoveUnusedFunctions(M, EntryFunc, PatchConstantFunc,
IsLib);
}
return false;
}
};
}
char HLDeadFunctionElimination::ID = 0;
ModulePass *llvm::createHLDeadFunctionEliminationPass() {
return new HLDeadFunctionElimination();
}
INITIALIZE_PASS(HLDeadFunctionElimination, "hl-dfe", "Remove all unused function except entry from HLModule", false, false)
///////////////////////////////////////////////////////////////////////////////
// Legalize resource use.
// Map local or static global resource to global resource.
// Require inline for static global resource.
namespace {
class DxilLegalizeStaticResourceUsePass : public ModulePass {
public:
static char ID; // Pass identification, replacement for typeid
explicit DxilLegalizeStaticResourceUsePass()
: ModulePass(ID) {}
const char *getPassName() const override {
return "DXIL Legalize Static Resource Use";
}
bool runOnModule(Module &M) override {
HLModule &HLM = M.GetOrCreateHLModule();
OP *hlslOP = HLM.GetOP();
Type *HandleTy = hlslOP->GetHandleType();
// Promote static global variables.
PromoteStaticGlobalResources(M);
// Lower handle cast.
for (Function &F : M.functions()) {
if (!F.isDeclaration())
continue;
HLOpcodeGroup group = hlsl::GetHLOpcodeGroupByName(&F);
if (group != HLOpcodeGroup::HLCast)
continue;
Type *Ty = F.getFunctionType()->getReturnType();
if (Ty->isPointerTy())
Ty = Ty->getPointerElementType();
if (HLModule::IsHLSLObjectType(Ty)) {
TransformHandleCast(F);
}
}
Value *UndefHandle = UndefValue::get(HandleTy);
if (!UndefHandle->user_empty()) {
for (User *U : UndefHandle->users()) {
// Report error if undef handle used for function call.
if (isa<CallInst>(U)) {
if (Instruction *UI = dyn_cast<Instruction>(U))
EmitResMappingError(UI);
else
M.getContext().emitError(kResourceMapErrorMsg);
}
}
}
return true;
}
private:
void PromoteStaticGlobalResources(Module &M);
void TransformHandleCast(Function &F);
};
char DxilLegalizeStaticResourceUsePass::ID = 0;
class DxilLegalizeResourceUsePass : public FunctionPass {
HLModule *m_pHLModule;
void getAnalysisUsage(AnalysisUsage &AU) const override;
public:
static char ID; // Pass identification, replacement for typeid
explicit DxilLegalizeResourceUsePass()
: FunctionPass(ID), m_pHLModule(nullptr) {}
const char *getPassName() const override {
return "DXIL Legalize Resource Use";
}
bool runOnFunction(Function &F) override {
// Promote local resource first.
PromoteLocalResource(F);
return true;
}
private:
void PromoteLocalResource(Function &F);
};
char DxilLegalizeResourceUsePass::ID = 0;
}
void DxilLegalizeResourceUsePass::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<AssumptionCacheTracker>();
AU.addRequired<DominatorTreeWrapperPass>();
AU.setPreservesAll();
}
void DxilLegalizeResourceUsePass::PromoteLocalResource(Function &F) {
std::vector<AllocaInst *> Allocas;
DominatorTree *DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
AssumptionCache &AC =
getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F);
HLModule &HLM = F.getParent()->GetOrCreateHLModule();
OP *hlslOP = HLM.GetOP();
Type *HandleTy = hlslOP->GetHandleType();
bool IsLib = HLM.GetShaderModel()->IsLib();
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 (HandleTy == dxilutil::GetArrayEltTy(AI->getAllocatedType())) {
// Skip for unpromotable for lib.
if (!isAllocaPromotable(AI) && IsLib)
continue;
if (!isAllocaPromotable(AI)) {
static const StringRef kNonPromotableLocalResourceErrorMsg =
"non-promotable local resource found.";
F.getContext().emitError(kNonPromotableLocalResourceErrorMsg);
throw hlsl::Exception(DXC_E_ABORT_COMPILATION_ERROR,
kNonPromotableLocalResourceErrorMsg);
continue;
}
Allocas.push_back(AI);
}
}
if (Allocas.empty())
break;
// No update.
// Report error and break.
if (allocaSize == Allocas.size()) {
F.getContext().emitError(kResourceMapErrorMsg);
break;
}
allocaSize = Allocas.size();
PromoteMemToReg(Allocas, *DT, nullptr, &AC);
}
return;
}
FunctionPass *llvm::createDxilLegalizeResourceUsePass() {
return new DxilLegalizeResourceUsePass();
}
INITIALIZE_PASS_BEGIN(DxilLegalizeResourceUsePass,
"hlsl-dxil-legalize-resource-use",
"DXIL legalize resource use", false, true)
INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker)
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
INITIALIZE_PASS_END(DxilLegalizeResourceUsePass,
"hlsl-dxil-legalize-resource-use",
"DXIL legalize resource use", false, true)
void DxilLegalizeStaticResourceUsePass::PromoteStaticGlobalResources(
Module &M) {
HLModule &HLM = M.GetOrCreateHLModule();
Type *HandleTy = HLM.GetOP()->GetHandleType();
std::set<GlobalVariable *> staticResources;
for (auto &GV : M.globals()) {
if (GV.getLinkage() == GlobalValue::LinkageTypes::InternalLinkage &&
HandleTy == 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()) {
Instruction *I = cast<Instruction>(U);
Insts.emplace_back(I);
}
LoadAndStorePromoter(Insts, SSA).run(Insts);
if (GV->user_empty()) {
bUpdated = true;
staticResources.erase(GV);
}
Insts.clear();
}
if (!bUpdated) {
M.getContext().emitError(kResourceMapErrorMsg);
break;
}
}
}
static void ReplaceResUseWithHandle(Instruction *Res, Value *Handle) {
Type *HandleTy = Handle->getType();
for (auto ResU = Res->user_begin(); ResU != Res->user_end();) {
Instruction *I = cast<Instruction>(*(ResU++));
if (isa<LoadInst>(I)) {
ReplaceResUseWithHandle(I, Handle);
} else if (isa<CallInst>(I)) {
if (I->getType() == HandleTy)
I->replaceAllUsesWith(Handle);
else
DXASSERT(0, "must createHandle here");
} else {
DXASSERT(0, "should only used by load and createHandle");
}
if (I->user_empty()) {
I->eraseFromParent();
}
}
}
void DxilLegalizeStaticResourceUsePass::TransformHandleCast(Function &F) {
for (auto U = F.user_begin(); U != F.user_end(); ) {
CallInst *CI = cast<CallInst>(*(U++));
Value *Handle = CI->getArgOperand(HLOperandIndex::kUnaryOpSrc0Idx);
ReplaceResUseWithHandle(CI, Handle);
if (CI->user_empty())
CI->eraseFromParent();
}
}
ModulePass *llvm::createDxilLegalizeStaticResourceUsePass() {
return new DxilLegalizeStaticResourceUsePass();
}
INITIALIZE_PASS(DxilLegalizeStaticResourceUsePass,
"hlsl-dxil-legalize-static-resource-use",
"DXIL legalize static resource use", false, false)
///////////////////////////////////////////////////////////////////////////////
// Legalize EvalOperations.
// Make sure src of EvalOperations are from function parameter.
// This is needed in order to translate EvaluateAttribute operations that traces
// back to LoadInput operations during translation stage. Promoting load/store
// instructions beforehand will allow us to easily trace back to loadInput from
// function call.
namespace {
class DxilLegalizeEvalOperations : public ModulePass {
public:
static char ID; // Pass identification, replacement for typeid
explicit DxilLegalizeEvalOperations() : ModulePass(ID) {}
const char *getPassName() const override {
return "DXIL Legalize EvalOperations";
}
bool runOnModule(Module &M) override {
for (Function &F : M.getFunctionList()) {
hlsl::HLOpcodeGroup group = hlsl::GetHLOpcodeGroup(&F);
if (group != HLOpcodeGroup::NotHL) {
std::vector<CallInst *> EvalFunctionCalls;
// Find all EvaluateAttribute calls
for (User *U : F.users()) {
if (CallInst *CI = dyn_cast<CallInst>(U)) {
IntrinsicOp evalOp =
static_cast<IntrinsicOp>(hlsl::GetHLOpcode(CI));
if (evalOp == IntrinsicOp::IOP_EvaluateAttributeAtSample ||
evalOp == IntrinsicOp::IOP_EvaluateAttributeCentroid ||
evalOp == IntrinsicOp::IOP_EvaluateAttributeSnapped) {
EvalFunctionCalls.push_back(CI);
}
}
}
if (EvalFunctionCalls.empty()) {
continue;
}
// Start from the call instruction, find all allocas that this call
// uses.
std::unordered_set<AllocaInst *> allocas;
for (CallInst *CI : EvalFunctionCalls) {
FindAllocasForEvalOperations(CI, allocas);
}
SSAUpdater SSA;
SmallVector<Instruction *, 4> Insts;
for (AllocaInst *AI : allocas) {
for (User *user : AI->users()) {
if (isa<LoadInst>(user) || isa<StoreInst>(user)) {
Insts.emplace_back(cast<Instruction>(user));
}
}
LoadAndStorePromoter(Insts, SSA).run(Insts);
Insts.clear();
}
}
}
return true;
}
private:
void FindAllocasForEvalOperations(Value *val,
std::unordered_set<AllocaInst *> &allocas);
};
char DxilLegalizeEvalOperations::ID = 0;
// Find allocas for EvaluateAttribute operations
void DxilLegalizeEvalOperations::FindAllocasForEvalOperations(
Value *val, std::unordered_set<AllocaInst *> &allocas) {
Value *CurVal = val;
while (!isa<AllocaInst>(CurVal)) {
if (CallInst *CI = dyn_cast<CallInst>(CurVal)) {
CurVal = CI->getOperand(HLOperandIndex::kUnaryOpSrc0Idx);
} else if (InsertElementInst *IE = dyn_cast<InsertElementInst>(CurVal)) {
Value *arg0 =
IE->getOperand(0); // Could be another insertelement or undef
Value *arg1 = IE->getOperand(1);
FindAllocasForEvalOperations(arg0, allocas);
CurVal = arg1;
} else if (ShuffleVectorInst *SV = dyn_cast<ShuffleVectorInst>(CurVal)) {
Value *arg0 = SV->getOperand(0);
Value *arg1 = SV->getOperand(1);
FindAllocasForEvalOperations(
arg0, allocas); // Shuffle vector could come from different allocas
CurVal = arg1;
} else if (ExtractElementInst *EE = dyn_cast<ExtractElementInst>(CurVal)) {
CurVal = EE->getOperand(0);
} else if (LoadInst *LI = dyn_cast<LoadInst>(CurVal)) {
CurVal = LI->getOperand(0);
} else {
break;
}
}
if (AllocaInst *AI = dyn_cast<AllocaInst>(CurVal)) {
allocas.insert(AI);
}
}
} // namespace
ModulePass *llvm::createDxilLegalizeEvalOperationsPass() {
return new DxilLegalizeEvalOperations();
}
INITIALIZE_PASS(DxilLegalizeEvalOperations,
"hlsl-dxil-legalize-eval-operations",
"DXIL legalize eval operations", false, false)
///////////////////////////////////////////////////////////////////////////////
// Translate RawBufferLoad/RawBufferStore
// This pass is to make sure that we generate correct buffer load for DXIL
// For DXIL < 1.2, rawBufferLoad will be translated to BufferLoad instruction
// without mask.
// For DXIL >= 1.2, if min precision is enabled, currently generation pass is
// producing i16/f16 return type for min precisions. For rawBuffer, we will
// change this so that min precisions are returning its actual scalar type (i32/f32)
// and will be truncated to their corresponding types after loading / before storing.
namespace {
class DxilTranslateRawBuffer : public ModulePass {
public:
static char ID;
explicit DxilTranslateRawBuffer() : ModulePass(ID) {}
bool runOnModule(Module &M) {
unsigned major, minor;
M.GetDxilModule().GetDxilVersion(major, minor);
DxilModule::ShaderFlags flag = M.GetDxilModule().m_ShaderFlags;
if (major == 1 && minor < 2) {
for (auto F = M.functions().begin(), E = M.functions().end(); F != E;) {
Function *func = &*(F++);
if (func->hasName()) {
if (func->getName().startswith("dx.op.rawBufferLoad")) {
ReplaceRawBufferLoad(func, M);
func->eraseFromParent();
} else if (func->getName().startswith("dx.op.rawBufferStore")) {
ReplaceRawBufferStore(func, M);
func->eraseFromParent();
}
}
}
} else if (!flag.GetUseNativeLowPrecision()) {
for (auto F = M.functions().begin(), E = M.functions().end(); F != E;) {
Function *func = &*(F++);
if (func->hasName()) {
if (func->getName().startswith("dx.op.rawBufferLoad")) {
ReplaceMinPrecisionRawBufferLoad(func, M);
} else if (func->getName().startswith("dx.op.rawBufferStore")) {
ReplaceMinPrecisionRawBufferStore(func, M);
}
}
}
}
return true;
}
private:
// Replace RawBufferLoad/Store to BufferLoad/Store for DXIL < 1.2
void ReplaceRawBufferLoad(Function *F, Module &M);
void ReplaceRawBufferStore(Function *F, Module &M);
// Replace RawBufferLoad/Store of min-precision types to have its actual storage size
void ReplaceMinPrecisionRawBufferLoad(Function *F, Module &M);
void ReplaceMinPrecisionRawBufferStore(Function *F, Module &M);
void ReplaceMinPrecisionRawBufferLoadByType(Function *F, Type *FromTy,
Type *ToTy, OP *Op,
const DataLayout &DL);
};
} // namespace
void DxilTranslateRawBuffer::ReplaceRawBufferLoad(Function *F,
Module &M) {
OP *op = M.GetDxilModule().GetOP();
Type *RTy = F->getReturnType();
if (StructType *STy = dyn_cast<StructType>(RTy)) {
Type *ETy = STy->getElementType(0);
Function *newFunction = op->GetOpFunc(hlsl::DXIL::OpCode::BufferLoad, ETy);
for (auto U = F->user_begin(), E = F->user_end(); U != E;) {
User *user = *(U++);
if (CallInst *CI = dyn_cast<CallInst>(user)) {
IRBuilder<> Builder(CI);
SmallVector<Value *, 4> args;
args.emplace_back(op->GetI32Const((unsigned)DXIL::OpCode::BufferLoad));
for (unsigned i = 1; i < 4; ++i) {
args.emplace_back(CI->getArgOperand(i));
}
CallInst *newCall = Builder.CreateCall(newFunction, args);
CI->replaceAllUsesWith(newCall);
CI->eraseFromParent();
} else {
DXASSERT(false, "function can only be used with call instructions.");
}
}
} else {
DXASSERT(false, "RawBufferLoad should return struct type.");
}
}
void DxilTranslateRawBuffer::ReplaceRawBufferStore(Function *F,
Module &M) {
OP *op = M.GetDxilModule().GetOP();
DXASSERT(F->getReturnType()->isVoidTy(), "rawBufferStore should return a void type.");
Type *ETy = F->getFunctionType()->getParamType(4); // value
Function *newFunction = op->GetOpFunc(hlsl::DXIL::OpCode::BufferStore, ETy);
for (auto U = F->user_begin(), E = F->user_end(); U != E;) {
User *user = *(U++);
if (CallInst *CI = dyn_cast<CallInst>(user)) {
IRBuilder<> Builder(CI);
SmallVector<Value *, 4> args;
args.emplace_back(op->GetI32Const((unsigned)DXIL::OpCode::BufferStore));
for (unsigned i = 1; i < 9; ++i) {
args.emplace_back(CI->getArgOperand(i));
}
Builder.CreateCall(newFunction, args);
CI->eraseFromParent();
}
else {
DXASSERT(false, "function can only be used with call instructions.");
}
}
}
void DxilTranslateRawBuffer::ReplaceMinPrecisionRawBufferLoad(Function *F,
Module &M) {
OP *Op = M.GetDxilModule().GetOP();
Type *RetTy = F->getReturnType();
if (StructType *STy = dyn_cast<StructType>(RetTy)) {
Type *EltTy = STy->getElementType(0);
if (EltTy->isHalfTy()) {
ReplaceMinPrecisionRawBufferLoadByType(F, Type::getHalfTy(M.getContext()),
Type::getFloatTy(M.getContext()),
Op, M.getDataLayout());
} else if (EltTy == Type::getInt16Ty(M.getContext())) {
ReplaceMinPrecisionRawBufferLoadByType(
F, Type::getInt16Ty(M.getContext()), Type::getInt32Ty(M.getContext()),
Op, M.getDataLayout());
}
} else {
DXASSERT(false, "RawBufferLoad should return struct type.");
}
}
void DxilTranslateRawBuffer::ReplaceMinPrecisionRawBufferStore(Function *F,
Module &M) {
DXASSERT(F->getReturnType()->isVoidTy(), "rawBufferStore should return a void type.");
Type *ETy = F->getFunctionType()->getParamType(4); // value
Type *NewETy;
if (ETy->isHalfTy()) {
NewETy = Type::getFloatTy(M.getContext());
}
else if (ETy == Type::getInt16Ty(M.getContext())) {
NewETy = Type::getInt32Ty(M.getContext());
}
else {
return; // not a min precision type
}
Function *newFunction = M.GetDxilModule().GetOP()->GetOpFunc(
DXIL::OpCode::RawBufferStore, NewETy);
// for each function
// add argument 4-7 to its upconverted values
// replace function call
for (auto FuncUser = F->user_begin(), FuncEnd = F->user_end(); FuncUser != FuncEnd;) {
CallInst *CI = dyn_cast<CallInst>(*(FuncUser++));
DXASSERT(CI, "function user must be a call instruction.");
IRBuilder<> CIBuilder(CI);
SmallVector<Value *, 9> Args;
for (unsigned i = 0; i < 4; ++i) {
Args.emplace_back(CI->getArgOperand(i));
}
// values to store should be converted to its higher precision types
if (ETy->isHalfTy()) {
for (unsigned i = 4; i < 8; ++i) {
Value *NewV = CIBuilder.CreateFPExt(CI->getArgOperand(i),
Type::getFloatTy(M.getContext()));
Args.emplace_back(NewV);
}
}
else if (ETy == Type::getInt16Ty(M.getContext())) {
// This case only applies to typed buffer since Store operation of byte
// address buffer for min precision is handled by implicit conversion on
// intrinsic call. Since we are extending integer, we have to know if we
// should sign ext or zero ext. We can do this by iterating checking the
// size of the element at struct type and comp type at type annotation
CallInst *handleCI = dyn_cast<CallInst>(CI->getArgOperand(1));
DXASSERT(handleCI, "otherwise handle was not an argument to buffer store.");
ConstantInt *resClass = dyn_cast<ConstantInt>(handleCI->getArgOperand(1));
DXASSERT_LOCALVAR(resClass, resClass && resClass->getSExtValue() ==
(unsigned)DXIL::ResourceClass::UAV,
"otherwise buffer store called on non uav kind.");
ConstantInt *rangeID = dyn_cast<ConstantInt>(handleCI->getArgOperand(2)); // range id or idx?
DXASSERT(rangeID, "wrong createHandle call.");
DxilResource dxilRes = M.GetDxilModule().GetUAV(rangeID->getSExtValue());
StructType *STy = dyn_cast<StructType>(dxilRes.GetRetType());
DxilStructAnnotation *SAnnot = M.GetDxilModule().GetTypeSystem().GetStructAnnotation(STy);
ConstantInt *offsetInt = dyn_cast<ConstantInt>(CI->getArgOperand(3));
unsigned offset = offsetInt->getSExtValue();
unsigned currentOffset = 0;
for (DxilStructTypeIterator iter = begin(STy, SAnnot), ItEnd = end(STy, SAnnot); iter != ItEnd; ++iter) {
std::pair<Type *, DxilFieldAnnotation*> pair = *iter;
currentOffset += M.getDataLayout().getTypeAllocSize(pair.first);
if (currentOffset > offset) {
if (pair.second->GetCompType().IsUIntTy()) {
for (unsigned i = 4; i < 8; ++i) {
Value *NewV = CIBuilder.CreateZExt(CI->getArgOperand(i), Type::getInt32Ty(M.getContext()));
Args.emplace_back(NewV);
}
break;
}
else if (pair.second->GetCompType().IsIntTy()) {
for (unsigned i = 4; i < 8; ++i) {
Value *NewV = CIBuilder.CreateSExt(CI->getArgOperand(i), Type::getInt32Ty(M.getContext()));
Args.emplace_back(NewV);
}
break;
}
else {
DXASSERT(false, "Invalid comp type");
}
}
}
}
// mask
Args.emplace_back(CI->getArgOperand(8));
// alignment
Args.emplace_back(M.GetDxilModule().GetOP()->GetI32Const(
M.getDataLayout().getTypeAllocSize(NewETy)));
CIBuilder.CreateCall(newFunction, Args);
CI->eraseFromParent();
}
}
void DxilTranslateRawBuffer::ReplaceMinPrecisionRawBufferLoadByType(
Function *F, Type *FromTy, Type *ToTy, OP *Op, const DataLayout &DL) {
Function *newFunction = Op->GetOpFunc(DXIL::OpCode::RawBufferLoad, ToTy);
for (auto FUser = F->user_begin(), FEnd = F->user_end(); FUser != FEnd;) {
User *UserCI = *(FUser++);
if (CallInst *CI = dyn_cast<CallInst>(UserCI)) {
IRBuilder<> CIBuilder(CI);
SmallVector<Value *, 5> newFuncArgs;
// opcode, handle, index, elementOffset, mask
// Compiler is generating correct element offset even for min precision types
// So no need to recalculate here
for (unsigned i = 0; i < 5; ++i) {
newFuncArgs.emplace_back(CI->getArgOperand(i));
}
// new alignment for new type
newFuncArgs.emplace_back(Op->GetI32Const(DL.getTypeAllocSize(ToTy)));
CallInst *newCI = CIBuilder.CreateCall(newFunction, newFuncArgs);
for (auto CIUser = CI->user_begin(), CIEnd = CI->user_end();
CIUser != CIEnd;) {
User *UserEV = *(CIUser++);
if (ExtractValueInst *EV = dyn_cast<ExtractValueInst>(UserEV)) {
IRBuilder<> EVBuilder(EV);
ArrayRef<unsigned> Indices = EV->getIndices();
DXASSERT(Indices.size() == 1, "Otherwise we have wrong extract value.");
Value *newEV = EVBuilder.CreateExtractValue(newCI, Indices);
Value *newTruncV;
if (4 == Indices[0]) { // Don't truncate status
newTruncV = newEV;
}
else if (FromTy->isHalfTy()) {
newTruncV = EVBuilder.CreateFPTrunc(newEV, FromTy);
} else if (FromTy->isIntegerTy()) {
newTruncV = EVBuilder.CreateTrunc(newEV, FromTy);
} else {
DXASSERT(false, "unexpected type conversion");
}
EV->replaceAllUsesWith(newTruncV);
EV->eraseFromParent();
}
}
CI->eraseFromParent();
}
}
F->eraseFromParent();
}
char DxilTranslateRawBuffer::ID = 0;
ModulePass *llvm::createDxilTranslateRawBuffer() {
return new DxilTranslateRawBuffer();
}
INITIALIZE_PASS(DxilTranslateRawBuffer, "hlsl-translate-dxil-raw-buffer",
"Translate raw buffer load", false, false)