| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152 |
- ///////////////////////////////////////////////////////////////////////////////
- // //
- // DxilCondenseResources.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. //
- // //
- // Provides a pass to make resource IDs zero-based and dense. //
- // //
- ///////////////////////////////////////////////////////////////////////////////
- #include "dxc/HLSL/DxilGenerationPass.h"
- #include "dxc/HLSL/DxilOperations.h"
- #include "dxc/HLSL/DxilSignatureElement.h"
- #include "dxc/HLSL/DxilModule.h"
- #include "dxc/Support/Global.h"
- #include "dxc/HLSL/DxilTypeSystem.h"
- #include "dxc/HLSL/DxilInstructions.h"
- #include "dxc/HLSL/DxilSpanAllocator.h"
- #include "dxc/HLSL/HLMatrixLowerHelper.h"
- #include "dxc/HLSL/DxilUtil.h"
- #include "dxc/HLSL/HLModule.h"
- #include "llvm/IR/Instructions.h"
- #include "llvm/IR/IntrinsicInst.h"
- #include "llvm/IR/InstIterator.h"
- #include "llvm/IR/Module.h"
- #include "llvm/IR/PassManager.h"
- #include "llvm/IR/DebugInfo.h"
- #include "llvm/ADT/BitVector.h"
- #include "llvm/ADT/SetVector.h"
- #include "llvm/Pass.h"
- #include "llvm/Transforms/Utils/Local.h"
- #include <memory>
- #include <unordered_set>
- using namespace llvm;
- using namespace hlsl;
- // Resource rangeID remap.
- namespace {
- struct ResourceID {
- DXIL::ResourceClass Class; // Resource class.
- unsigned ID; // Resource ID, as specified on entry.
- bool operator<(const ResourceID &other) const {
- if (Class < other.Class)
- return true;
- if (Class > other.Class)
- return false;
- if (ID < other.ID)
- return true;
- return false;
- }
- };
- struct RemapEntry {
- ResourceID ResID; // Resource identity, as specified on entry.
- DxilResourceBase *Resource; // In-memory resource representation.
- unsigned Index; // Index in resource vector - new ID for the resource.
- };
- typedef std::map<ResourceID, RemapEntry> RemapEntryCollection;
- template <typename TResource>
- void BuildRewrites(const std::vector<std::unique_ptr<TResource>> &Rs,
- RemapEntryCollection &C) {
- const unsigned s = (unsigned)Rs.size();
- for (unsigned i = 0; i < s; ++i) {
- const std::unique_ptr<TResource> &R = Rs[i];
- if (R->GetID() != i) {
- ResourceID RId = {R->GetClass(), R->GetID()};
- RemapEntry RE = {RId, R.get(), i};
- C[RId] = RE;
- }
- }
- }
- // Build m_rewrites, returns 'true' if any rewrites are needed.
- bool BuildRewriteMap(RemapEntryCollection &rewrites, DxilModule &DM) {
- BuildRewrites(DM.GetCBuffers(), rewrites);
- BuildRewrites(DM.GetSRVs(), rewrites);
- BuildRewrites(DM.GetUAVs(), rewrites);
- BuildRewrites(DM.GetSamplers(), rewrites);
- return !rewrites.empty();
- }
- void ApplyRewriteMapOnResTable(RemapEntryCollection &rewrites, DxilModule &DM) {
- for (auto &entry : rewrites) {
- entry.second.Resource->SetID(entry.second.Index);
- }
- }
- } // namespace
- // Resource lowerBound allocation.
- namespace {
- template <typename T>
- static bool
- AllocateDxilResource(const std::vector<std::unique_ptr<T>> &resourceList,
- LLVMContext &Ctx, unsigned AutoBindingSpace=0) {
- bool bChanged = false;
- SpacesAllocator<unsigned, T> SAlloc;
- for (auto &res : resourceList) {
- const unsigned space = res->GetSpaceID();
- typename SpacesAllocator<unsigned, T>::Allocator &alloc = SAlloc.Get(space);
- if (res->IsAllocated()) {
- const unsigned reg = res->GetLowerBound();
- const T *conflict = nullptr;
- if (res->IsUnbounded()) {
- const T *unbounded = alloc.GetUnbounded();
- if (unbounded) {
- Ctx.emitError(Twine("more than one unbounded resource (") +
- unbounded->GetGlobalName() + (" and ") +
- res->GetGlobalName() + (") in space ") + Twine(space));
- } else {
- conflict = alloc.Insert(res.get(), reg, res->GetUpperBound());
- if (!conflict)
- alloc.SetUnbounded(res.get());
- }
- } else {
- conflict = alloc.Insert(res.get(), reg, res->GetUpperBound());
- }
- if (conflict) {
- Ctx.emitError(((res->IsUnbounded()) ? Twine("unbounded ") : Twine("")) +
- Twine("resource ") + res->GetGlobalName() +
- Twine(" at register ") + Twine(reg) +
- Twine(" overlaps with resource ") +
- conflict->GetGlobalName() + Twine(" at register ") +
- Twine(conflict->GetLowerBound()) + Twine(", space ") +
- Twine(space));
- }
- }
- }
- // Allocate.
- const unsigned space = AutoBindingSpace;
- typename SpacesAllocator<unsigned, T>::Allocator &alloc0 = SAlloc.Get(space);
- for (auto &res : resourceList) {
- if (!res->IsAllocated()) {
- DXASSERT(res->GetSpaceID() == 0,
- "otherwise non-zero space has no user register assignment");
- unsigned reg = 0;
- bool success = false;
- if (res->IsUnbounded()) {
- const T *unbounded = alloc0.GetUnbounded();
- if (unbounded) {
- Ctx.emitError(Twine("more than one unbounded resource (") +
- unbounded->GetGlobalName() + Twine(" and ") +
- res->GetGlobalName() + Twine(") in space ") +
- Twine(space));
- } else {
- success = alloc0.AllocateUnbounded(res.get(), reg);
- if (success)
- alloc0.SetUnbounded(res.get());
- }
- } else {
- success = alloc0.Allocate(res.get(), res->GetRangeSize(), reg);
- }
- if (success) {
- res->SetLowerBound(reg);
- res->SetSpaceID(space);
- bChanged = true;
- } else {
- Ctx.emitError(((res->IsUnbounded()) ? Twine("unbounded ") : Twine("")) +
- Twine("resource ") + res->GetGlobalName() +
- Twine(" could not be allocated"));
- }
- }
- }
- return bChanged;
- }
- bool AllocateDxilResources(DxilModule &DM) {
- uint32_t AutoBindingSpace = DM.GetAutoBindingSpace();
- if (AutoBindingSpace == UINT_MAX) {
- // For libraries, we don't allocate unless AutoBindingSpace is set.
- if (DM.GetShaderModel()->IsLib())
- return false;
- // For shaders, we allocate in space 0 by default.
- AutoBindingSpace = 0;
- }
- bool bChanged = false;
- bChanged |= AllocateDxilResource(DM.GetCBuffers(), DM.GetCtx(), AutoBindingSpace);
- bChanged |= AllocateDxilResource(DM.GetSamplers(), DM.GetCtx(), AutoBindingSpace);
- bChanged |= AllocateDxilResource(DM.GetUAVs(), DM.GetCtx(), AutoBindingSpace);
- bChanged |= AllocateDxilResource(DM.GetSRVs(), DM.GetCtx(), AutoBindingSpace);
- return bChanged;
- }
- } // namespace
- class DxilCondenseResources : public ModulePass {
- private:
- RemapEntryCollection m_rewrites;
- public:
- static char ID; // Pass identification, replacement for typeid
- explicit DxilCondenseResources() : ModulePass(ID) {}
- const char *getPassName() const override { return "DXIL Condense Resources"; }
- bool runOnModule(Module &M) override {
- DxilModule &DM = M.GetOrCreateDxilModule();
- // Skip lib.
- if (DM.GetShaderModel()->IsLib())
- return false;
- // Remove unused resource.
- DM.RemoveUnusedResources();
- // Make sure all resource types are dense; build a map of rewrites.
- if (BuildRewriteMap(m_rewrites, DM)) {
- // Rewrite all instructions that refer to resources in the map.
- ApplyRewriteMap(DM);
- }
- bool hasResource = DM.GetCBuffers().size() ||
- DM.GetUAVs().size() || DM.GetSRVs().size() || DM.GetSamplers().size();
- if (hasResource) {
- if (!DM.GetShaderModel()->IsLib()) {
- AllocateDxilResources(DM);
- PatchCreateHandle(DM);
- }
- }
- return true;
- }
- DxilResourceBase &GetFirstRewrite() const {
- DXASSERT_NOMSG(!m_rewrites.empty());
- return *m_rewrites.begin()->second.Resource;
- }
- private:
- void ApplyRewriteMap(DxilModule &DM);
- // Add lowbound to create handle range index.
- void PatchCreateHandle(DxilModule &DM);
- };
- void DxilCondenseResources::ApplyRewriteMap(DxilModule &DM) {
- for (Function &F : DM.GetModule()->functions()) {
- if (F.isDeclaration()) {
- continue;
- }
- for (inst_iterator iter = inst_begin(F), E = inst_end(F); iter != E; ++iter) {
- llvm::Instruction &I = *iter;
- DxilInst_CreateHandle CH(&I);
- if (!CH)
- continue;
- ResourceID RId;
- RId.Class = (DXIL::ResourceClass)CH.get_resourceClass_val();
- RId.ID = (unsigned)llvm::dyn_cast<llvm::ConstantInt>(CH.get_rangeId())
- ->getZExtValue();
- RemapEntryCollection::iterator it = m_rewrites.find(RId);
- if (it == m_rewrites.end()) {
- continue;
- }
- CallInst *CI = cast<CallInst>(&I);
- Value *newRangeID = DM.GetOP()->GetU32Const(it->second.Index);
- CI->setArgOperand(DXIL::OperandIndex::kCreateHandleResIDOpIdx,
- newRangeID);
- }
- }
- ApplyRewriteMapOnResTable(m_rewrites, DM);
- }
- namespace {
- void PatchLowerBoundOfCreateHandle(CallInst *handle, DxilModule &DM) {
- DxilInst_CreateHandle createHandle(handle);
- DXASSERT_NOMSG(createHandle);
- DXIL::ResourceClass ResClass =
- static_cast<DXIL::ResourceClass>(createHandle.get_resourceClass_val());
- // Dynamic rangeId is not supported - skip and let validation report the
- // error.
- if (!isa<ConstantInt>(createHandle.get_rangeId()))
- return;
- unsigned rangeId =
- cast<ConstantInt>(createHandle.get_rangeId())->getLimitedValue();
- DxilResourceBase *res = nullptr;
- switch (ResClass) {
- case DXIL::ResourceClass::SRV:
- res = &DM.GetSRV(rangeId);
- break;
- case DXIL::ResourceClass::UAV:
- res = &DM.GetUAV(rangeId);
- break;
- case DXIL::ResourceClass::CBuffer:
- res = &DM.GetCBuffer(rangeId);
- break;
- case DXIL::ResourceClass::Sampler:
- res = &DM.GetSampler(rangeId);
- break;
- default:
- DXASSERT(0, "invalid res class");
- return;
- }
- IRBuilder<> Builder(handle);
- unsigned lowBound = res->GetLowerBound();
- if (lowBound) {
- Value *Index = createHandle.get_index();
- if (ConstantInt *cIndex = dyn_cast<ConstantInt>(Index)) {
- unsigned newIdx = lowBound + cIndex->getLimitedValue();
- handle->setArgOperand(DXIL::OperandIndex::kCreateHandleResIndexOpIdx,
- Builder.getInt32(newIdx));
- } else {
- Value *newIdx = Builder.CreateAdd(Index, Builder.getInt32(lowBound));
- handle->setArgOperand(DXIL::OperandIndex::kCreateHandleResIndexOpIdx,
- newIdx);
- }
- }
- }
- static void PatchTBufferCreateHandle(CallInst *handle, DxilModule &DM, std::unordered_set<unsigned> &tbufferIDs) {
- DxilInst_CreateHandle createHandle(handle);
- DXASSERT_NOMSG(createHandle);
- DXIL::ResourceClass ResClass = static_cast<DXIL::ResourceClass>(createHandle.get_resourceClass_val());
- if (ResClass != DXIL::ResourceClass::CBuffer)
- return;
- Value *resID = createHandle.get_rangeId();
- DXASSERT(isa<ConstantInt>(resID), "cannot handle dynamic resID for cbuffer CreateHandle");
- if (!isa<ConstantInt>(resID))
- return;
- unsigned rangeId = cast<ConstantInt>(resID)->getLimitedValue();
- DxilResourceBase *res = &DM.GetCBuffer(rangeId);
- // For TBuffer, we need to switch resource type from CBuffer to SRV
- if (res->GetKind() == DXIL::ResourceKind::TBuffer) {
- // Track cbuffers IDs that are actually tbuffers
- tbufferIDs.insert(rangeId);
- hlsl::OP *hlslOP = DM.GetOP();
- llvm::LLVMContext &Ctx = DM.GetCtx();
- // Temporarily add SRV size to rangeID to guarantee unique new SRV ID
- Value *newRangeID = hlslOP->GetU32Const(rangeId + DM.GetSRVs().size());
- handle->setArgOperand(DXIL::OperandIndex::kCreateHandleResIDOpIdx,
- newRangeID);
- // switch create handle to SRV
- handle->setArgOperand(DXIL::OperandIndex::kCreateHandleResClassOpIdx,
- hlslOP->GetU8Const(
- static_cast<std::underlying_type<DxilResourceBase::Class>::type>(
- DXIL::ResourceClass::SRV)));
- Type *doubleTy = Type::getDoubleTy(Ctx);
- Type *i64Ty = Type::getInt64Ty(Ctx);
- // Replace corresponding cbuffer loads with typed buffer loads
- for (auto U = handle->user_begin(); U != handle->user_end(); ) {
- CallInst *I = cast<CallInst>(*(U++));
- DXASSERT(I && OP::IsDxilOpFuncCallInst(I), "otherwise unexpected user of CreateHandle value");
- DXIL::OpCode opcode = OP::GetDxilOpFuncCallInst(I);
- if (opcode == DXIL::OpCode::CBufferLoadLegacy) {
- DxilInst_CBufferLoadLegacy cbLoad(I);
- // Replace with appropriate buffer load instruction
- IRBuilder<> Builder(I);
- opcode = OP::OpCode::BufferLoad;
- Type *Ty = Type::getInt32Ty(Ctx);
- Function *BufLoad = hlslOP->GetOpFunc(opcode, Ty);
- Constant *opArg = hlslOP->GetU32Const((unsigned)opcode);
- Value *undefI = UndefValue::get(Type::getInt32Ty(Ctx));
- Value *offset = cbLoad.get_regIndex();
- CallInst* load = Builder.CreateCall(BufLoad, {opArg, handle, offset, undefI});
- // Find extractelement uses of cbuffer load and replace + generate bitcast as necessary
- for (auto LU = I->user_begin(); LU != I->user_end(); ) {
- ExtractValueInst *evInst = dyn_cast<ExtractValueInst>(*(LU++));
- DXASSERT(evInst && evInst->getNumIndices() == 1, "user of cbuffer load result should be extractvalue");
- uint64_t idx = evInst->getIndices()[0];
- Type *EltTy = evInst->getType();
- IRBuilder<> EEBuilder(evInst);
- Value *result = nullptr;
- if (EltTy != Ty) {
- // extract two values and DXIL::OpCode::MakeDouble or construct i64
- if ((EltTy == doubleTy) || (EltTy == i64Ty)) {
- DXASSERT(idx < 2, "64-bit component index out of range");
- // This assumes big endian order in tbuffer elements (is this correct?)
- Value *low = EEBuilder.CreateExtractValue(load, idx * 2);
- Value *high = EEBuilder.CreateExtractValue(load, idx * 2 + 1);
- if (EltTy == doubleTy) {
- opcode = OP::OpCode::MakeDouble;
- Function *MakeDouble = hlslOP->GetOpFunc(opcode, doubleTy);
- Constant *opArg = hlslOP->GetU32Const((unsigned)opcode);
- result = EEBuilder.CreateCall(MakeDouble, {opArg, low, high});
- } else {
- high = EEBuilder.CreateZExt(high, i64Ty);
- low = EEBuilder.CreateZExt(low, i64Ty);
- high = EEBuilder.CreateShl(high, hlslOP->GetU64Const(32));
- result = EEBuilder.CreateOr(high, low);
- }
- } else {
- result = EEBuilder.CreateExtractValue(load, idx);
- result = EEBuilder.CreateBitCast(result, EltTy);
- }
- } else {
- result = EEBuilder.CreateExtractValue(load, idx);
- }
- evInst->replaceAllUsesWith(result);
- evInst->eraseFromParent();
- }
- } else if (opcode == DXIL::OpCode::CBufferLoad) {
- // TODO: Handle this, or prevent this for tbuffer
- DXASSERT(false, "otherwise CBufferLoad used for tbuffer rather than CBufferLoadLegacy");
- } else {
- DXASSERT(false, "otherwise unexpected user of CreateHandle value");
- }
- I->eraseFromParent();
- }
- }
- }
- }
- void DxilCondenseResources::PatchCreateHandle(DxilModule &DM) {
- Function *createHandle = DM.GetOP()->GetOpFunc(DXIL::OpCode::CreateHandle,
- Type::getVoidTy(DM.GetCtx()));
- for (User *U : createHandle->users()) {
- PatchLowerBoundOfCreateHandle(cast<CallInst>(U), DM);
- }
- }
- char DxilCondenseResources::ID = 0;
- bool llvm::AreDxilResourcesDense(llvm::Module *M, hlsl::DxilResourceBase **ppNonDense) {
- DxilModule &DM = M->GetOrCreateDxilModule();
- RemapEntryCollection rewrites;
- if (BuildRewriteMap(rewrites, DM)) {
- *ppNonDense = rewrites.begin()->second.Resource;
- return false;
- }
- else {
- *ppNonDense = nullptr;
- return true;
- }
- }
- ModulePass *llvm::createDxilCondenseResourcesPass() {
- return new DxilCondenseResources();
- }
- INITIALIZE_PASS(DxilCondenseResources, "hlsl-dxil-condense", "DXIL Condense Resources", false, false)
- namespace {
- class DxilLowerCreateHandleForLib : public ModulePass {
- private:
- RemapEntryCollection m_rewrites;
- DxilModule *m_DM;
- bool m_HasDbgInfo;
- bool m_bIsLib;
- bool m_bLegalizationFailed;
- public:
- static char ID; // Pass identification, replacement for typeid
- explicit DxilLowerCreateHandleForLib() : ModulePass(ID) {}
- const char *getPassName() const override {
- return "DXIL Lower createHandleForLib";
- }
- bool runOnModule(Module &M) override {
- DxilModule &DM = M.GetOrCreateDxilModule();
- m_DM = &DM;
- // Clear llvm used to remove unused resource.
- m_DM->ClearLLVMUsed();
- m_bIsLib = DM.GetShaderModel()->IsLib();
- m_bLegalizationFailed = false;
- bool bChanged = false;
- unsigned numResources = DM.GetCBuffers().size() + DM.GetUAVs().size() +
- DM.GetSRVs().size() + DM.GetSamplers().size();
- if (!numResources)
- return false;
- // Switch tbuffers to SRVs, as they have been treated as cbuffers up to this
- // point.
- if (DM.GetCBuffers().size())
- bChanged = PatchTBuffers(DM) || bChanged;
- // Remove unused resource.
- DM.RemoveUnusedResourceSymbols();
- unsigned newResources = DM.GetCBuffers().size() + DM.GetUAVs().size() +
- DM.GetSRVs().size() + DM.GetSamplers().size();
- bChanged = bChanged || (numResources != newResources);
- if (0 == newResources)
- return bChanged;
- bChanged |= AllocateDxilResources(DM);
- if (m_bIsLib && DM.GetShaderModel()->GetMinor() == ShaderModel::kOfflineMinor)
- return bChanged;
- // Make sure no select on resource.
- bChanged |= RemovePhiOnResource();
- if (m_bIsLib || m_bLegalizationFailed)
- return bChanged;
- bChanged = true;
- // Load up debug information, to cross-reference values and the instructions
- // used to load them.
- m_HasDbgInfo = getDebugMetadataVersionFromModule(M) != 0;
- GenerateDxilResourceHandles();
- if (DM.GetOP()->UseMinPrecision())
- UpdateStructTypeForLegacyLayout();
- // Change resource symbol into undef.
- UpdateResourceSymbols();
- // Remove unused createHandleForLib functions.
- dxilutil::RemoveUnusedFunctions(M, DM.GetEntryFunction(),
- DM.GetPatchConstantFunction(), m_bIsLib);
- return bChanged;
- }
- private:
- bool RemovePhiOnResource();
- void UpdateResourceSymbols();
- void TranslateDxilResourceUses(DxilResourceBase &res);
- void GenerateDxilResourceHandles();
- void UpdateStructTypeForLegacyLayout();
- // Switch CBuffer for SRV for TBuffers.
- bool PatchTBuffers(DxilModule &DM);
- void PatchTBufferUse(Value *V, DxilModule &DM);
- };
- } // namespace
- // Phi on resource.
- namespace {
- typedef std::unordered_map<Value*, Value*> ValueToValueMap;
- typedef llvm::SetVector<Value*> ValueSetVector;
- typedef llvm::SmallVector<Value*, 4> IndexVector;
- typedef std::unordered_map<Value*, IndexVector> ValueToIdxMap;
- //#define SUPPORT_SELECT_ON_ALLOCA
- // Errors:
- class ResourceUseErrors
- {
- bool m_bErrorsReported;
- public:
- ResourceUseErrors() : m_bErrorsReported(false) {}
- enum ErrorCode {
- // Collision between use of one resource GV and another.
- // All uses must be guaranteed to resolve to only one GV.
- // Additionally, when writing resource to alloca, all uses
- // of that alloca are considered resolving to a single GV.
- GVConflicts,
- // static global resources are disallowed for libraries at this time.
- // for non-library targets, they should have been eliminated already.
- StaticGVUsed,
- // user function calls with resource params or return type are
- // are currently disallowed for libraries.
- UserCallsWithResources,
- // When searching up from store pointer looking for alloca,
- // we encountered an unexpted value type
- UnexpectedValuesFromStorePointer,
- // When remapping values to be replaced, we add them to RemappedValues
- // so we don't use dead values stored in other sets/maps. Circular
- // remaps that should not happen are aadded to RemappingCyclesDetected.
- RemappingCyclesDetected,
- // Without SUPPORT_SELECT_ON_ALLOCA, phi/select on alloca based
- // pointer is disallowed, since this scenario is still untested.
- // This error also covers any other unknown alloca pointer uses.
- // Supported:
- // alloca (-> gep)? -> load -> ...
- // alloca (-> gep)? -> store.
- // Unsupported without SUPPORT_SELECT_ON_ALLOCA:
- // alloca (-> gep)? -> phi/select -> ...
- AllocaUserDisallowed,
- #ifdef SUPPORT_SELECT_ON_ALLOCA
- // Conflict in select/phi between GV pointer and alloca pointer. This
- // algorithm can't handle this case.
- AllocaSelectConflict,
- #endif
- ErrorCodeCount
- };
- const StringRef ErrorText[ErrorCodeCount] = {
- "local resource not guaranteed to map to unique global resource.",
- "static global resource use is disallowed for library functions.",
- "exported library functions cannot have resource parameters or return value.",
- "internal error: unexpected instruction type when looking for alloca from store.",
- "internal error: cycles detected in value remapping.",
- "phi/select disallowed on pointers to local resources."
- #ifdef SUPPORT_SELECT_ON_ALLOCA
- ,"unable to resolve merge of global and local resource pointers."
- #endif
- };
- ValueSetVector ErrorSets[ErrorCodeCount];
- // Ulitimately, the goal of ErrorUsers is to mark all create handles
- // so we don't try to report errors on them again later.
- std::unordered_set<Value*> ErrorUsers; // users of error values
- bool AddErrorUsers(Value* V) {
- auto it = ErrorUsers.insert(V);
- if (!it.second)
- return false; // already there
- if (isa<GEPOperator>(V) ||
- isa<LoadInst>(V) ||
- isa<PHINode>(V) ||
- isa<SelectInst>(V) ||
- isa<AllocaInst>(V)) {
- for (auto U : V->users()) {
- AddErrorUsers(U);
- }
- } else if(isa<StoreInst>(V)) {
- AddErrorUsers(cast<StoreInst>(V)->getPointerOperand());
- }
- // create handle will be marked, but users not followed
- return true;
- }
- void ReportError(ErrorCode ec, Value* V) {
- DXASSERT_NOMSG(ec < ErrorCodeCount);
- if (!ErrorSets[ec].insert(V))
- return; // Error already reported
- AddErrorUsers(V);
- m_bErrorsReported = true;
- if (Instruction *I = dyn_cast<Instruction>(V)) {
- dxilutil::EmitErrorOnInstruction(I, ErrorText[ec]);
- } else {
- StringRef Name = V->getName();
- std::string escName;
- if (isa<Function>(V)) {
- llvm::raw_string_ostream os(escName);
- dxilutil::PrintEscapedString(Name, os);
- os.flush();
- Name = escName;
- }
- Twine msg = Twine(ErrorText[ec]) + " Value: " + Name;
- V->getContext().emitError(msg);
- }
- }
- bool ErrorsReported() {
- return m_bErrorsReported;
- }
- };
- unsigned CountArrayDimensions(Type* Ty,
- // Optionally collect dimensions
- SmallVector<unsigned, 4> *dims = nullptr) {
- if (Ty->isPointerTy())
- Ty = Ty->getPointerElementType();
- unsigned dim = 0;
- if (dims)
- dims->clear();
- while (Ty->isArrayTy()) {
- if (dims)
- dims->push_back(Ty->getArrayNumElements());
- dim++;
- Ty = Ty->getArrayElementType();
- }
- return dim;
- }
- // Helper class for legalizing resource use
- // Convert select/phi on resources to select/phi on index to GEP on GV.
- // Convert resource alloca to index alloca.
- // Assumes createHandleForLib has no select/phi
- class LegalizeResourceUseHelper {
- // Change:
- // gep1 = GEP gRes, i1
- // res1 = load gep1
- // gep2 = GEP gRes, i2
- // gep3 = GEP gRes, i3
- // gep4 = phi gep2, gep3 <-- handle select/phi on GEP
- // res4 = load gep4
- // res5 = phi res1, res4
- // res6 = load GEP gRes, 23 <-- handle constant GepExpression
- // res = select cnd2, res5, res6
- // handle = createHandleForLib(res)
- // To:
- // i4 = phi i2, i3
- // i5 = phi i1, i4
- // i6 = select cnd, i5, 23
- // gep = GEP gRes, i6
- // res = load gep
- // handle = createHandleForLib(res)
- // Also handles alloca
- // resArray = alloca [2 x Resource]
- // gep1 = GEP gRes, i1
- // res1 = load gep1
- // gep2 = GEP gRes, i2
- // gep3 = GEP gRes, i3
- // phi4 = phi gep2, gep3
- // res4 = load phi4
- // gep5 = GEP resArray, 0
- // gep6 = GEP resArray, 1
- // store gep5, res1
- // store gep6, res4
- // gep7 = GEP resArray, i7 <-- dynamically index array
- // res = load gep7
- // handle = createHandleForLib(res)
- // Desired result:
- // idxArray = alloca [2 x i32]
- // phi4 = phi i2, i3
- // gep5 = GEP idxArray, 0
- // gep6 = GEP idxArray, 1
- // store gep5, i1
- // store gep6, phi4
- // gep7 = GEP idxArray, i7
- // gep8 = GEP gRes, gep7
- // res = load gep8
- // handle = createHandleForLib(res)
- // Also handles multi-dim resource index and multi-dim resource array allocas
- // Basic algorithm:
- // - recursively mark each GV user with GV (ValueToResourceGV)
- // - verify only one GV used for any given value
- // - handle allocas by searching up from store for alloca
- // - then recursively mark alloca users
- // - ResToIdxReplacement keeps track of vector of indices that
- // will be used to replace a given resource value or pointer
- // - Next, create selects/phis for indices corresponding to
- // selects/phis on resource pointers or values.
- // - leave incoming index values undef for now
- // - Create index allocas to replace resource allocas
- // - Create GEPs on index allocas to replace GEPs on resource allocas
- // - Create index loads on index allocas to replace loads on resource alloca GEP
- // - Fill in replacements for GEPs on resource GVs
- // - copy replacement index vectors to corresponding loads
- // - Create index stores to replace resource stores to alloca/GEPs
- // - Update selects/phis incoming index values
- // - SimplifyMerges: replace index phis/selects on same value with that value
- // - RemappedValues[phi/select] set to replacement value
- // - use LookupValue from now on when reading from ResToIdxReplacement
- // - Update handles by replacing load/GEP chains that go through select/phi
- // with direct GV GEP + load, with select/phi on GEP indices instead.
- public:
- ResourceUseErrors m_Errors;
- ValueToValueMap ValueToResourceGV;
- ValueToIdxMap ResToIdxReplacement;
- // Value sets we can use to iterate
- ValueSetVector Selects, GEPs, Stores, Handles;
- ValueSetVector Allocas, AllocaGEPs, AllocaLoads;
- #ifdef SUPPORT_SELECT_ON_ALLOCA
- ValueSetVector AllocaSelects;
- #endif
- std::unordered_set<Value *> NonUniformSet;
- // New index selects created by pass, so we can try simplifying later
- ValueSetVector NewSelects;
- // Values that have been replaced with other values need remapping
- ValueToValueMap RemappedValues;
- // Things to clean up if no users:
- std::unordered_set<Instruction*> CleanupInsts;
- GlobalVariable *LookupResourceGV(Value *V) {
- auto itGV = ValueToResourceGV.find(V);
- if (itGV == ValueToResourceGV.end())
- return nullptr;
- return cast<GlobalVariable>(itGV->second);
- }
- // Follow RemappedValues, return input if not remapped
- Value *LookupValue(Value *V) {
- auto it = RemappedValues.find(V);
- SmallPtrSet<Value*, 4> visited;
- while (it != RemappedValues.end()) {
- // Cycles should not happen, but are bad if they do.
- if (visited.count(it->second)) {
- DXASSERT(false, "otherwise, circular remapping");
- m_Errors.ReportError(ResourceUseErrors::RemappingCyclesDetected, V);
- break;
- }
- V = it->second;
- it = RemappedValues.find(V);
- if (it != RemappedValues.end())
- visited.insert(V);
- }
- return V;
- }
- bool AreLoadUsersTrivial(LoadInst *LI) {
- for (auto U : LI->users()) {
- if (CallInst *CI = dyn_cast<CallInst>(U)) {
- Function *F = CI->getCalledFunction();
- DxilModule &DM = F->getParent()->GetDxilModule();
- hlsl::OP *hlslOP = DM.GetOP();
- if (hlslOP->IsDxilOpFunc(F)) {
- hlsl::OP::OpCodeClass opClass;
- if (hlslOP->GetOpCodeClass(F, opClass) &&
- opClass == DXIL::OpCodeClass::CreateHandleForLib) {
- continue;
- }
- }
- }
- return false;
- }
- return true;
- }
- // This is used to quickly skip the common case where no work is needed
- bool AreGEPUsersTrivial(GEPOperator *GEP) {
- if (GlobalVariable *GV = LookupResourceGV(GEP)) {
- if (GEP->getPointerOperand() != LookupResourceGV(GEP))
- return false;
- }
- for (auto U : GEP->users()) {
- if (LoadInst *LI = dyn_cast<LoadInst>(U)) {
- if (AreLoadUsersTrivial(LI))
- continue;
- }
- return false;
- }
- return true;
- }
- // AssignResourceGVFromStore is used on pointer being stored to.
- // Follow GEP/Phi/Select up to Alloca, then CollectResourceGVUsers on Alloca
- void AssignResourceGVFromStore(GlobalVariable *GV, Value *V,
- SmallPtrSet<Value*, 4> &visited,
- bool bNonUniform) {
- // Prevent cycles as we search up
- if (visited.count(V) != 0)
- return;
- // Verify and skip if already processed
- auto it = ValueToResourceGV.find(V);
- if (it != ValueToResourceGV.end()) {
- if (it->second != GV) {
- m_Errors.ReportError(ResourceUseErrors::GVConflicts, V);
- }
- return;
- }
- if (AllocaInst *AI = dyn_cast<AllocaInst>(V)) {
- CollectResourceGVUsers(GV, AI, /*bAlloca*/true, bNonUniform);
- return;
- } else if (GEPOperator *GEP = dyn_cast<GEPOperator>(V)) {
- // follow the pointer up
- AssignResourceGVFromStore(GV, GEP->getPointerOperand(), visited, bNonUniform);
- return;
- } else if (PHINode *Phi = dyn_cast<PHINode>(V)) {
- #ifdef SUPPORT_SELECT_ON_ALLOCA
- // follow all incoming values
- for (auto it : Phi->operand_values())
- AssignResourceGVFromStore(GV, it, visited, bNonUniform);
- #else
- m_Errors.ReportError(ResourceUseErrors::AllocaUserDisallowed, V);
- #endif
- return;
- } else if (SelectInst *Sel = dyn_cast<SelectInst>(V)) {
- #ifdef SUPPORT_SELECT_ON_ALLOCA
- // follow all incoming values
- AssignResourceGVFromStore(GV, Sel->getTrueValue(), visited, bNonUniform);
- AssignResourceGVFromStore(GV, Sel->getFalseValue(), visited, bNonUniform);
- #else
- m_Errors.ReportError(ResourceUseErrors::AllocaUserDisallowed, V);
- #endif
- return;
- } else if (isa<GlobalVariable>(V) &&
- cast<GlobalVariable>(V)->getLinkage() ==
- GlobalVariable::LinkageTypes::InternalLinkage) {
- // this is writing to global static, which is disallowed at this point.
- m_Errors.ReportError(ResourceUseErrors::StaticGVUsed, V);
- return;
- } else {
- // Most likely storing to output parameter
- m_Errors.ReportError(ResourceUseErrors::UserCallsWithResources, V);
- return;
- }
- return;
- }
- // Recursively mark values with GV, following users.
- // Starting value V should be GV itself.
- // Returns true if value/uses reference no other GV in map.
- void CollectResourceGVUsers(GlobalVariable *GV, Value *V, bool bAlloca = false, bool bNonUniform = false) {
- // Recursively tag value V and its users as using GV.
- auto it = ValueToResourceGV.find(V);
- if (it != ValueToResourceGV.end()) {
- if (it->second != GV) {
- m_Errors.ReportError(ResourceUseErrors::GVConflicts, V);
- #ifdef SUPPORT_SELECT_ON_ALLOCA
- } else {
- // if select/phi, make sure bAlloca is consistent
- if (isa<PHINode>(V) || isa<SelectInst>(V))
- if ((bAlloca && AllocaSelects.count(V) == 0) ||
- (!bAlloca && Selects.count(V) == 0))
- m_Errors.ReportError(ResourceUseErrors::AllocaSelectConflict, V);
- #endif
- }
- return;
- }
- ValueToResourceGV[V] = GV;
- if (GV == V) {
- // Just add and recurse users
- // make sure bAlloca is clear for users
- bAlloca = false;
- } else if (GEPOperator *GEP = dyn_cast<GEPOperator>(V)) {
- if (bAlloca)
- AllocaGEPs.insert(GEP);
- else if (!AreGEPUsersTrivial(GEP))
- GEPs.insert(GEP);
- else
- return; // Optimization: skip trivial GV->GEP->load->createHandle
- if (GetElementPtrInst *GEPInst = dyn_cast<GetElementPtrInst>(GEP)) {
- if (DxilMDHelper::IsMarkedNonUniform(GEPInst))
- bNonUniform = true;
- }
- } else if (LoadInst *LI = dyn_cast<LoadInst>(V)) {
- if (bAlloca)
- AllocaLoads.insert(LI);
- // clear bAlloca for users
- bAlloca = false;
- if (bNonUniform)
- NonUniformSet.insert(LI);
- } else if (StoreInst *SI = dyn_cast<StoreInst>(V)) {
- Stores.insert(SI);
- if (!bAlloca) {
- // Find and mark allocas this store could be storing to
- SmallPtrSet<Value*, 4> visited;
- AssignResourceGVFromStore(GV, SI->getPointerOperand(), visited, bNonUniform);
- }
- return;
- } else if (PHINode *Phi = dyn_cast<PHINode>(V)) {
- if (bAlloca) {
- #ifdef SUPPORT_SELECT_ON_ALLOCA
- AllocaSelects.insert(Phi);
- #else
- m_Errors.ReportError(ResourceUseErrors::AllocaUserDisallowed, V);
- #endif
- } else {
- Selects.insert(Phi);
- }
- } else if (SelectInst *Sel = dyn_cast<SelectInst>(V)) {
- if (bAlloca) {
- #ifdef SUPPORT_SELECT_ON_ALLOCA
- AllocaSelects.insert(Sel);
- #else
- m_Errors.ReportError(ResourceUseErrors::AllocaUserDisallowed, V);
- #endif
- } else {
- Selects.insert(Sel);
- }
- } else if (AllocaInst *AI = dyn_cast<AllocaInst>(V)) {
- Allocas.insert(AI);
- // set bAlloca for users
- bAlloca = true;
- } else if (Constant *C = dyn_cast<Constant>(V)) {
- // skip @llvm.used entry
- return;
- } else if (bAlloca) {
- m_Errors.ReportError(ResourceUseErrors::AllocaUserDisallowed, V);
- } else {
- // Must be createHandleForLib or user function call.
- CallInst *CI = cast<CallInst>(V);
- Function *F = CI->getCalledFunction();
- DxilModule &DM = GV->getParent()->GetDxilModule();
- hlsl::OP *hlslOP = DM.GetOP();
- if (hlslOP->IsDxilOpFunc(F)) {
- hlsl::OP::OpCodeClass opClass;
- if (hlslOP->GetOpCodeClass(F, opClass) &&
- opClass == DXIL::OpCodeClass::CreateHandleForLib) {
- Handles.insert(CI);
- if (bNonUniform)
- NonUniformSet.insert(CI);
- return;
- }
- }
- // This could be user call with resource param, which is disallowed for lib_6_3
- m_Errors.ReportError(ResourceUseErrors::UserCallsWithResources, V);
- return;
- }
- // Recurse users
- for (auto U : V->users())
- CollectResourceGVUsers(GV, U, bAlloca, bNonUniform);
- return;
- }
- // Remove conflicting values from sets before
- // transforming the remainder.
- void RemoveConflictingValue(Value* V) {
- bool bRemoved = false;
- if (isa<GEPOperator>(V)) {
- bRemoved = GEPs.remove(V) || AllocaGEPs.remove(V);
- } else if (isa<LoadInst>(V)) {
- bRemoved = AllocaLoads.remove(V);
- } else if (isa<StoreInst>(V)) {
- bRemoved = Stores.remove(V);
- } else if (isa<PHINode>(V) || isa<SelectInst>(V)) {
- bRemoved = Selects.remove(V);
- #ifdef SUPPORT_SELECT_ON_ALLOCA
- bRemoved |= AllocaSelects.remove(V);
- #endif
- } else if (isa<AllocaInst>(V)) {
- bRemoved = Allocas.remove(V);
- } else if (isa<CallInst>(V)) {
- bRemoved = Handles.remove(V);
- return; // don't recurse
- }
- if (bRemoved) {
- // Recurse users
- for (auto U : V->users())
- RemoveConflictingValue(U);
- }
- }
- void RemoveConflicts() {
- for (auto V : m_Errors.ErrorSets[ResourceUseErrors::GVConflicts]) {
- RemoveConflictingValue(V);
- ValueToResourceGV.erase(V);
- }
- }
- void CreateSelects() {
- if (Selects.empty()
- #ifdef SUPPORT_SELECT_ON_ALLOCA
- && AllocaSelects.empty()
- #endif
- )
- return;
- LLVMContext &Ctx =
- #ifdef SUPPORT_SELECT_ON_ALLOCA
- Selects.empty() ? AllocaSelects[0]->getContext() :
- #endif
- Selects[0]->getContext();
- Type *i32Ty = IntegerType::getInt32Ty(Ctx);
- #ifdef SUPPORT_SELECT_ON_ALLOCA
- for (auto &SelectSet : {Selects, AllocaSelects}) {
- bool bAlloca = !(&SelectSet == &Selects);
- #else
- for (auto &SelectSet : { Selects }) {
- #endif
- for (auto pValue : SelectSet) {
- Type *SelectTy = i32Ty;
- #ifdef SUPPORT_SELECT_ON_ALLOCA
- // For alloca case, type needs to match dimensionality of incoming value
- if (bAlloca) {
- // TODO: Not sure if this case will actually work
- // (or whether it can even be generated from HLSL)
- Type *Ty = pValue->getType();
- SmallVector<unsigned, 4> dims;
- unsigned dim = CountArrayDimensions(Ty, &dims);
- for (unsigned i = 0; i < dim; i++)
- SelectTy = ArrayType::get(SelectTy, (uint64_t)dims[dim - i - 1]);
- if (Ty->isPointerTy())
- SelectTy = PointerType::get(SelectTy, 0);
- }
- #endif
- Value *UndefValue = UndefValue::get(SelectTy);
- if (PHINode *Phi = dyn_cast<PHINode>(pValue)) {
- GlobalVariable *GV = LookupResourceGV(Phi);
- if (!GV)
- continue; // skip value removed due to conflict
- IRBuilder<> PhiBuilder(Phi);
- unsigned gvDim = CountArrayDimensions(GV->getType());
- IndexVector &idxVector = ResToIdxReplacement[Phi];
- idxVector.resize(gvDim, nullptr);
- unsigned numIncoming = Phi->getNumIncomingValues();
- for (unsigned i = 0; i < gvDim; i++) {
- PHINode *newPhi = PhiBuilder.CreatePHI(SelectTy, numIncoming);
- NewSelects.insert(newPhi);
- idxVector[i] = newPhi;
- for (unsigned j = 0; j < numIncoming; j++) {
- // Set incoming values to undef until next pass
- newPhi->addIncoming(UndefValue, Phi->getIncomingBlock(j));
- }
- }
- } else if (SelectInst *Sel = dyn_cast<SelectInst>(pValue)) {
- GlobalVariable *GV = LookupResourceGV(Sel);
- if (!GV)
- continue; // skip value removed due to conflict
- IRBuilder<> Builder(Sel);
- unsigned gvDim = CountArrayDimensions(GV->getType());
- IndexVector &idxVector = ResToIdxReplacement[Sel];
- idxVector.resize(gvDim, nullptr);
- for (unsigned i = 0; i < gvDim; i++) {
- Value *newSel = Builder.CreateSelect(Sel->getCondition(), UndefValue, UndefValue);
- NewSelects.insert(newSel);
- idxVector[i] = newSel;
- }
- } else {
- DXASSERT(false, "otherwise, non-select/phi in Selects set");
- }
- }
- }
- }
- // Create index allocas to replace resource allocas
- void CreateIndexAllocas() {
- if (Allocas.empty())
- return;
- Type *i32Ty = IntegerType::getInt32Ty(Allocas[0]->getContext());
- for (auto pValue : Allocas) {
- AllocaInst *pAlloca = cast<AllocaInst>(pValue);
- GlobalVariable *GV = LookupResourceGV(pAlloca);
- if (!GV)
- continue; // skip value removed due to conflict
- IRBuilder<> AllocaBuilder(pAlloca);
- unsigned gvDim = CountArrayDimensions(GV->getType());
- SmallVector<unsigned, 4> dimVector;
- unsigned allocaTyDim = CountArrayDimensions(pAlloca->getType(), &dimVector);
- Type *pIndexType = i32Ty;
- for (unsigned i = 0; i < allocaTyDim; i++) {
- pIndexType = ArrayType::get(pIndexType, dimVector[allocaTyDim - i - 1]);
- }
- Value *arraySize = pAlloca->getArraySize();
- IndexVector &idxVector = ResToIdxReplacement[pAlloca];
- idxVector.resize(gvDim, nullptr);
- for (unsigned i = 0; i < gvDim; i++) {
- AllocaInst *pAlloca = AllocaBuilder.CreateAlloca(pIndexType, arraySize);
- pAlloca->setAlignment(4);
- idxVector[i] = pAlloca;
- }
- }
- }
- // Add corresponding GEPs for index allocas
- IndexVector &ReplaceAllocaGEP(GetElementPtrInst *GEP) {
- IndexVector &idxVector = ResToIdxReplacement[GEP];
- if (!idxVector.empty())
- return idxVector;
- Value *Ptr = GEP->getPointerOperand();
- // Recurse for partial GEPs
- IndexVector &ptrIndices = isa<GetElementPtrInst>(Ptr) ?
- ReplaceAllocaGEP(cast<GetElementPtrInst>(Ptr)) : ResToIdxReplacement[Ptr];
- IRBuilder<> Builder(GEP);
- SmallVector<Value*, 4> gepIndices;
- for (auto it = GEP->idx_begin(), idxEnd = GEP->idx_end(); it != idxEnd; it++)
- gepIndices.push_back(*it);
- idxVector.resize(ptrIndices.size(), nullptr);
- for (unsigned i = 0; i < ptrIndices.size(); i++) {
- idxVector[i] = Builder.CreateInBoundsGEP(ptrIndices[i], gepIndices);
- }
- return idxVector;
- }
- void ReplaceAllocaGEPs() {
- for (auto V : AllocaGEPs) {
- ReplaceAllocaGEP(cast<GetElementPtrInst>(V));
- }
- }
- void ReplaceAllocaLoads() {
- for (auto V : AllocaLoads) {
- LoadInst *LI = cast<LoadInst>(V);
- Value *Ptr = LI->getPointerOperand();
- IRBuilder<> Builder(LI);
- IndexVector &idxVector = ResToIdxReplacement[V];
- IndexVector &ptrIndices = ResToIdxReplacement[Ptr];
- idxVector.resize(ptrIndices.size(), nullptr);
- for (unsigned i = 0; i < ptrIndices.size(); i++) {
- idxVector[i] = Builder.CreateLoad(ptrIndices[i]);
- }
- }
- }
- // Add GEP to ResToIdxReplacement with indices from incoming + GEP
- IndexVector &ReplaceGVGEPs(GEPOperator *GEP) {
- IndexVector &idxVector = ResToIdxReplacement[GEP];
- // Skip if already done
- // (we recurse into partial GEP and iterate all GEPs)
- if (!idxVector.empty())
- return idxVector;
- Type *i32Ty = IntegerType::getInt32Ty(GEP->getContext());
- Constant *Zero = Constant::getIntegerValue(i32Ty, APInt(32, 0));
- Value *Ptr = GEP->getPointerOperand();
- unsigned idx = 0;
- if (GlobalVariable *GV = dyn_cast<GlobalVariable>(Ptr)) {
- unsigned gvDim = CountArrayDimensions(GV->getType());
- idxVector.resize(gvDim, Zero);
- } else if (isa<GEPOperator>(Ptr) || isa<PHINode>(Ptr) || isa<SelectInst>(Ptr)) {
- // Recurse for partial GEPs
- IndexVector &ptrIndices = isa<GEPOperator>(Ptr) ?
- ReplaceGVGEPs(cast<GEPOperator>(Ptr)) : ResToIdxReplacement[Ptr];
- unsigned ptrDim = CountArrayDimensions(Ptr->getType());
- unsigned gvDim = ptrIndices.size();
- DXASSERT(ptrDim <= gvDim, "otherwise incoming pointer has more dimensions than associated GV");
- unsigned gepStart = gvDim - ptrDim;
- // Copy indices and add ours
- idxVector.resize(ptrIndices.size(), Zero);
- for (; idx < gepStart; idx++)
- idxVector[idx] = ptrIndices[idx];
- }
- if (GEP->hasIndices()) {
- auto itIdx = GEP->idx_begin();
- ++itIdx; // Always skip leading zero (we don't support GV+n pointer arith)
- while (itIdx != GEP->idx_end())
- idxVector[idx++] = *itIdx++;
- }
- return idxVector;
- }
- // Add GEPs to ResToIdxReplacement and update loads
- void ReplaceGVGEPs() {
- if (GEPs.empty())
- return;
- for (auto V : GEPs) {
- GEPOperator *GEP = cast<GEPOperator>(V);
- IndexVector &gepVector = ReplaceGVGEPs(GEP);
- for (auto U : GEP->users()) {
- if (LoadInst *LI = dyn_cast<LoadInst>(U)) {
- // Just copy incoming indices
- ResToIdxReplacement[LI] = gepVector;
- }
- }
- }
- }
- // Create new index stores for incoming indices
- void ReplaceStores() {
- // generate stores of incoming indices to corresponding index pointers
- if (Stores.empty())
- return;
- for (auto V : Stores) {
- StoreInst *SI = cast<StoreInst>(V);
- IRBuilder<> Builder(SI);
- IndexVector &idxVector = ResToIdxReplacement[SI];
- Value *Ptr = SI->getPointerOperand();
- Value *Val = SI->getValueOperand();
- IndexVector &ptrIndices = ResToIdxReplacement[Ptr];
- IndexVector &valIndices = ResToIdxReplacement[Val];
- DXASSERT_NOMSG(ptrIndices.size() == valIndices.size());
- idxVector.resize(ptrIndices.size(), nullptr);
- for (unsigned i = 0; i < idxVector.size(); i++) {
- idxVector[i] = Builder.CreateStore(valIndices[i], ptrIndices[i]);
- }
- }
- }
- // For each Phi/Select: update matching incoming values for new phis
- void UpdateSelects() {
- for (auto V : Selects) {
- // update incoming index values corresponding to incoming resource values
- IndexVector &idxVector = ResToIdxReplacement[V];
- Instruction *I = cast<Instruction>(V);
- unsigned numOperands = I->getNumOperands();
- unsigned startOp = isa<PHINode>(V) ? 0 : 1;
- for (unsigned iOp = startOp; iOp < numOperands; iOp++) {
- IndexVector &incomingIndices = ResToIdxReplacement[I->getOperand(iOp)];
- DXASSERT_NOMSG(idxVector.size() == incomingIndices.size());
- for (unsigned i = 0; i < idxVector.size(); i++) {
- // must be instruction (phi/select)
- Instruction *indexI = cast<Instruction>(idxVector[i]);
- indexI->setOperand(iOp, incomingIndices[i]);
- }
- // Now clear incoming operand (adding to cleanup) to break cycles
- if (Instruction *OpI = dyn_cast<Instruction>(I->getOperand(iOp)))
- CleanupInsts.insert(OpI);
- I->setOperand(iOp, UndefValue::get(I->getType()));
- }
- }
- }
- // ReplaceHandles
- // - iterate handles
- // - insert GEP using new indices associated with resource value
- // - load resource from new GEP
- // - replace resource use in createHandleForLib with new load
- // Assumes: no users of handle are phi/select or store
- void ReplaceHandles() {
- if (Handles.empty())
- return;
- Type *i32Ty = IntegerType::getInt32Ty(Handles[0]->getContext());
- Constant *Zero = Constant::getIntegerValue(i32Ty, APInt(32, 0));
- for (auto V : Handles) {
- CallInst *CI = cast<CallInst>(V);
- DxilInst_CreateHandleForLib createHandle(CI);
- Value *res = createHandle.get_Resource();
- // Skip extra work if nothing between load and create handle
- if (LoadInst *LI = dyn_cast<LoadInst>(res)) {
- Value *Ptr = LI->getPointerOperand();
- if (GEPOperator *GEP = dyn_cast<GEPOperator>(Ptr))
- Ptr = GEP->getPointerOperand();
- if (isa<GlobalVariable>(Ptr))
- continue;
- }
- GlobalVariable *GV = LookupResourceGV(res);
- if (!GV)
- continue; // skip value removed due to conflict
- IRBuilder<> Builder(CI);
- IndexVector &idxVector = ResToIdxReplacement[res];
- DXASSERT(idxVector.size() == CountArrayDimensions(GV->getType()), "replacements empty or invalid");
- SmallVector<Value*, 4> gepIndices;
- gepIndices.push_back(Zero);
- for (auto idxVal : idxVector)
- gepIndices.push_back(LookupValue(idxVal));
- Value *GEP = Builder.CreateInBoundsGEP(GV, gepIndices);
- // Mark new GEP instruction non-uniform if necessary
- if (NonUniformSet.count(res) != 0 || NonUniformSet.count(CI) != 0)
- if (GetElementPtrInst *GEPInst = dyn_cast<GetElementPtrInst>(GEP))
- DxilMDHelper::MarkNonUniform(GEPInst);
- LoadInst *LI = Builder.CreateLoad(GEP);
- createHandle.set_Resource(LI);
- if (Instruction *resI = dyn_cast<Instruction>(res))
- CleanupInsts.insert(resI);
- }
- }
- // Delete unused CleanupInsts, restarting when changed
- // Return true if something was deleted
- bool CleanupUnusedValues() {
- // - delete unused CleanupInsts, restarting when changed
- bool bAnyChanges = false;
- bool bChanged = false;
- do {
- bChanged = false;
- for (auto it = CleanupInsts.begin(); it != CleanupInsts.end();) {
- Instruction *I = *(it++);
- if (I->user_empty()) {
- // Add instructions operands CleanupInsts
- for (unsigned iOp = 0; iOp < I->getNumOperands(); iOp++) {
- if (Instruction *opI = dyn_cast<Instruction>(I->getOperand(iOp)))
- CleanupInsts.insert(opI);
- }
- I->eraseFromParent();
- CleanupInsts.erase(I);
- bChanged = true;
- }
- }
- if (bChanged)
- bAnyChanges = true;
- } while (bChanged);
- return bAnyChanges;
- }
- void SimplifyMerges() {
- // Loop if changed
- bool bChanged = false;
- do {
- bChanged = false;
- for (auto V : NewSelects) {
- if (LookupValue(V) != V)
- continue;
- Instruction *I = cast<Instruction>(V);
- unsigned startOp = isa<PHINode>(I) ? 0 : 1;
- Value *newV = dxilutil::MergeSelectOnSameValue(
- cast<Instruction>(V), startOp, I->getNumOperands());
- if (newV) {
- RemappedValues[V] = newV;
- bChanged = true;
- }
- }
- } while (bChanged);
- }
- void CleanupDeadInsts() {
- // Assuming everything was successful:
- // delete stores to allocas to remove cycles
- for (auto V : Stores) {
- StoreInst *SI = cast<StoreInst>(V);
- if (Instruction *I = dyn_cast<Instruction>(SI->getValueOperand()))
- CleanupInsts.insert(I);
- if (Instruction *I = dyn_cast<Instruction>(SI->getPointerOperand()))
- CleanupInsts.insert(I);
- SI->eraseFromParent();
- }
- CleanupUnusedValues();
- }
- void VerifyComplete(DxilModule &DM) {
- // Check that all handles now resolve to a global variable, otherwise,
- // they are likely loading from resource function parameter, which
- // is disallowed.
- hlsl::OP *hlslOP = DM.GetOP();
- for (Function &F : DM.GetModule()->functions()) {
- if (hlslOP->IsDxilOpFunc(&F)) {
- hlsl::OP::OpCodeClass opClass;
- if (hlslOP->GetOpCodeClass(&F, opClass) &&
- opClass == DXIL::OpCodeClass::CreateHandleForLib) {
- for (auto U : F.users()) {
- CallInst *CI = cast<CallInst>(U);
- if (m_Errors.ErrorUsers.count(CI))
- continue; // Error already reported
- DxilInst_CreateHandleForLib createHandle(CI);
- Value *res = createHandle.get_Resource();
- LoadInst *LI = dyn_cast<LoadInst>(res);
- if (LI) {
- Value *Ptr = LI->getPointerOperand();
- if (GEPOperator *GEP = dyn_cast<GEPOperator>(Ptr))
- Ptr = GEP->getPointerOperand();
- if (isa<GlobalVariable>(Ptr))
- continue;
- }
- // handle wasn't processed
- // Right now, the most likely cause is user call with resources, but
- // this should be updated if there are other reasons for this to happen.
- m_Errors.ReportError(ResourceUseErrors::UserCallsWithResources, U);
- }
- }
- }
- }
- }
- // Fix resource global variable properties to external constant
- bool SetExternalConstant(GlobalVariable *GV) {
- if (GV->hasInitializer() || !GV->isConstant() ||
- GV->getLinkage() != GlobalVariable::LinkageTypes::ExternalLinkage) {
- GV->setInitializer(nullptr);
- GV->setConstant(true);
- GV->setLinkage(GlobalVariable::LinkageTypes::ExternalLinkage);
- return true;
- }
- return false;
- }
- bool CollectResources(DxilModule &DM) {
- bool bChanged = false;
- for (const auto &res : DM.GetCBuffers()) {
- if (GlobalVariable *GV = dyn_cast<GlobalVariable>(res->GetGlobalSymbol())) {
- bChanged |= SetExternalConstant(GV);
- CollectResourceGVUsers(GV, GV);
- }
- }
- for (const auto &res : DM.GetSRVs()) {
- if (GlobalVariable *GV = dyn_cast<GlobalVariable>(res->GetGlobalSymbol())) {
- bChanged |= SetExternalConstant(GV);
- CollectResourceGVUsers(GV, GV);
- }
- }
- for (const auto &res : DM.GetUAVs()) {
- if (GlobalVariable *GV = dyn_cast<GlobalVariable>(res->GetGlobalSymbol())) {
- bChanged |= SetExternalConstant(GV);
- CollectResourceGVUsers(GV, GV);
- }
- }
- for (const auto &res : DM.GetSamplers()) {
- if (GlobalVariable *GV = dyn_cast<GlobalVariable>(res->GetGlobalSymbol())) {
- bChanged |= SetExternalConstant(GV);
- CollectResourceGVUsers(GV, GV);
- }
- }
- return bChanged;
- }
- void DoTransform() {
- RemoveConflicts();
- CreateSelects();
- CreateIndexAllocas();
- ReplaceAllocaGEPs();
- ReplaceAllocaLoads();
- ReplaceGVGEPs();
- ReplaceStores();
- UpdateSelects();
- SimplifyMerges();
- ReplaceHandles();
- if (!m_Errors.ErrorsReported())
- CleanupDeadInsts();
- }
- bool ErrorsReported() {
- return m_Errors.ErrorsReported();
- }
- bool runOnModule(llvm::Module &M) {
- DxilModule &DM = M.GetOrCreateDxilModule();
- bool bChanged = CollectResources(DM);
- // If no selects or allocas are involved, there isn't anything to do
- if (Selects.empty() && Allocas.empty())
- return bChanged;
- DoTransform();
- VerifyComplete(DM);
- return true;
- }
- };
- class DxilLegalizeResources : public ModulePass {
- public:
- static char ID; // Pass identification, replacement for typeid
- explicit DxilLegalizeResources()
- : ModulePass(ID) {}
- const char *getPassName() const override {
- return "DXIL Legalize Resource Use";
- }
- bool runOnModule(Module &M) override {
- LegalizeResourceUseHelper helper;
- return helper.runOnModule(M);
- }
- private:
- };
- } // namespace
- char DxilLegalizeResources::ID = 0;
- ModulePass *llvm::createDxilLegalizeResources() {
- return new DxilLegalizeResources();
- }
- INITIALIZE_PASS(DxilLegalizeResources,
- "hlsl-dxil-legalize-resources",
- "DXIL legalize resource use", false, false)
- bool DxilLowerCreateHandleForLib::RemovePhiOnResource() {
- LegalizeResourceUseHelper helper;
- bool bChanged = helper.runOnModule(*m_DM->GetModule());
- if (helper.ErrorsReported())
- m_bLegalizationFailed = true;
- return bChanged;
- }
- // LegacyLayout.
- 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 UpdateStructTypeForLegacyLayoutOnDM(DxilModule &DM) {
- DxilTypeSystem &TypeSys = DM.GetTypeSystem();
- Module &M = *DM.GetModule();
- for (auto &CBuf : DM.GetCBuffers()) {
- UpdateStructTypeForLegacyLayout(*CBuf.get(), TypeSys, M);
- }
- for (auto &UAV : DM.GetUAVs()) {
- if (UAV->GetKind() == DxilResourceBase::Kind::StructuredBuffer)
- UpdateStructTypeForLegacyLayout(*UAV.get(), TypeSys, M);
- }
- for (auto &SRV : DM.GetSRVs()) {
- if (SRV->GetKind() == DxilResourceBase::Kind::StructuredBuffer)
- UpdateStructTypeForLegacyLayout(*SRV.get(), TypeSys, M);
- }
- }
- } // namespace
- void DxilLowerCreateHandleForLib::UpdateStructTypeForLegacyLayout() {
- UpdateStructTypeForLegacyLayoutOnDM(*m_DM);
- }
- // Change ResourceSymbol to undef if don't need.
- void DxilLowerCreateHandleForLib::UpdateResourceSymbols() {
- std::vector<GlobalVariable *> &LLVMUsed = m_DM->GetLLVMUsed();
- auto UpdateResourceSymbol = [&LLVMUsed, this](DxilResourceBase *res) {
- GlobalVariable *GV = cast<GlobalVariable>(res->GetGlobalSymbol());
- GV->removeDeadConstantUsers();
- DXASSERT(GV->user_empty(), "else resource not lowered");
- Type *Ty = GV->getType();
- res->SetGlobalSymbol(UndefValue::get(Ty));
- if (m_HasDbgInfo)
- LLVMUsed.emplace_back(GV);
- res->SetGlobalSymbol(UndefValue::get(Ty));
- };
- for (auto &&C : m_DM->GetCBuffers()) {
- UpdateResourceSymbol(C.get());
- }
- for (auto &&Srv : m_DM->GetSRVs()) {
- UpdateResourceSymbol(Srv.get());
- }
- for (auto &&Uav : m_DM->GetUAVs()) {
- UpdateResourceSymbol(Uav.get());
- }
- for (auto &&S : m_DM->GetSamplers()) {
- UpdateResourceSymbol(S.get());
- }
- }
- // Lower createHandleForLib
- namespace {
- void ReplaceResourceUserWithHandle(
- LoadInst *Res, Value *handle) {
- for (auto resUser = Res->user_begin(); resUser != Res->user_end();) {
- Value *V = *(resUser++);
- CallInst *CI = dyn_cast<CallInst>(V);
- DxilInst_CreateHandleForLib createHandle(CI);
- DXASSERT(createHandle, "must be createHandle");
- CI->replaceAllUsesWith(handle);
- CI->eraseFromParent();
- }
- Res->eraseFromParent();
- }
- DIGlobalVariable *FindGlobalVariableDebugInfo(GlobalVariable *GV,
- DebugInfoFinder &DbgInfoFinder) {
- struct GlobalFinder {
- GlobalVariable *GV;
- bool operator()(llvm::DIGlobalVariable *const arg) const {
- return arg->getVariable() == GV;
- }
- };
- GlobalFinder F = {GV};
- DebugInfoFinder::global_variable_iterator Found =
- std::find_if(DbgInfoFinder.global_variables().begin(),
- DbgInfoFinder.global_variables().end(), F);
- if (Found != DbgInfoFinder.global_variables().end()) {
- return *Found;
- }
- return nullptr;
- }
- } // namespace
- void DxilLowerCreateHandleForLib::TranslateDxilResourceUses(
- DxilResourceBase &res) {
- OP *hlslOP = m_DM->GetOP();
- Function *createHandle = hlslOP->GetOpFunc(
- OP::OpCode::CreateHandle, llvm::Type::getVoidTy(m_DM->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(res.GetLowerBound());
- Value *isUniformRes = hlslOP->GetI1Const(0);
- Value *GV = res.GetGlobalSymbol();
- Module *pM = m_DM->GetModule();
- // TODO: add debug info to create handle.
- DIVariable *DIV = nullptr;
- DILocation *DL = nullptr;
- if (m_HasDbgInfo) {
- DebugInfoFinder &Finder = m_DM->GetOrCreateDebugInfoFinder();
- DIV = 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(dxilutil::FirstNonAllocaInsertionPt(F));
- 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)) {
- 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 = llvm::make_unique<IRBuilder<> >(GEPInst);
- } else {
- Builder = llvm::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;
- createHandleArgs[DXIL::OperandIndex::kCreateHandleIsUniformOpIdx] =
- isUniformRes;
- Value *handle = nullptr;
- if (GetElementPtrInst *GEPInst = dyn_cast<GetElementPtrInst>(GEP)) {
- IRBuilder<> Builder = IRBuilder<>(GEPInst);
- if (DxilMDHelper::IsMarkedNonUniform(GEPInst)) {
- // Mark nonUniform.
- createHandleArgs[DXIL::OperandIndex::kCreateHandleIsUniformOpIdx] =
- hlslOP->GetI1Const(1);
- // Clear nonUniform on GEP.
- GEPInst->setMetadata(DxilMDHelper::kDxilNonUniformAttributeMDName, nullptr);
- }
- createHandleArgs[DXIL::OperandIndex::kCreateHandleResIndexOpIdx] =
- Builder.CreateAdd(idx, resLowerBound);
- 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 (handle) {
- ReplaceResourceUserWithHandle(ldInst, handle);
- } else {
- IRBuilder<> Builder = IRBuilder<>(ldInst);
- createHandleArgs[DXIL::OperandIndex::kCreateHandleResIndexOpIdx] =
- Builder.CreateAdd(idx, resLowerBound);
- Value *localHandle =
- Builder.CreateCall(createHandle, createHandleArgs, handleName);
- ReplaceResourceUserWithHandle(ldInst, localHandle);
- }
- }
- if (Instruction *I = dyn_cast<Instruction>(GEP)) {
- I->eraseFromParent();
- }
- }
- }
- // Erase unused handle.
- for (auto It : handleMapOnFunction) {
- Instruction *I = It.second;
- if (I->user_empty())
- I->eraseFromParent();
- }
- }
- void DxilLowerCreateHandleForLib::GenerateDxilResourceHandles() {
- for (size_t i = 0; i < m_DM->GetCBuffers().size(); i++) {
- DxilCBuffer &C = m_DM->GetCBuffer(i);
- TranslateDxilResourceUses(C);
- }
- // Create sampler handle first, may be used by SRV operations.
- for (size_t i = 0; i < m_DM->GetSamplers().size(); i++) {
- DxilSampler &S = m_DM->GetSampler(i);
- TranslateDxilResourceUses(S);
- }
- for (size_t i = 0; i < m_DM->GetSRVs().size(); i++) {
- DxilResource &SRV = m_DM->GetSRV(i);
- TranslateDxilResourceUses(SRV);
- }
- for (size_t i = 0; i < m_DM->GetUAVs().size(); i++) {
- DxilResource &UAV = m_DM->GetUAV(i);
- TranslateDxilResourceUses(UAV);
- }
- }
- // TBuffer.
- namespace {
- void InitTBuffer(const DxilCBuffer *pSource, DxilResource *pDest) {
- pDest->SetKind(pSource->GetKind());
- pDest->SetCompType(DXIL::ComponentType::U32);
- pDest->SetSampleCount(0);
- pDest->SetElementStride(0);
- pDest->SetGloballyCoherent(false);
- pDest->SetHasCounter(false);
- pDest->SetRW(false);
- pDest->SetROV(false);
- 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 PatchTBufferLoad(CallInst *handle, DxilModule &DM) {
- hlsl::OP *hlslOP = DM.GetOP();
- llvm::LLVMContext &Ctx = DM.GetCtx();
- Type *doubleTy = Type::getDoubleTy(Ctx);
- Type *i64Ty = Type::getInt64Ty(Ctx);
- // Replace corresponding cbuffer loads with typed buffer loads
- for (auto U = handle->user_begin(); U != handle->user_end();) {
- CallInst *I = cast<CallInst>(*(U++));
- DXASSERT(I && OP::IsDxilOpFuncCallInst(I),
- "otherwise unexpected user of CreateHandle value");
- DXIL::OpCode opcode = OP::GetDxilOpFuncCallInst(I);
- if (opcode == DXIL::OpCode::CBufferLoadLegacy) {
- DxilInst_CBufferLoadLegacy cbLoad(I);
- // Replace with appropriate buffer load instruction
- IRBuilder<> Builder(I);
- opcode = OP::OpCode::BufferLoad;
- Type *Ty = Type::getInt32Ty(Ctx);
- Function *BufLoad = hlslOP->GetOpFunc(opcode, Ty);
- Constant *opArg = hlslOP->GetU32Const((unsigned)opcode);
- Value *undefI = UndefValue::get(Type::getInt32Ty(Ctx));
- Value *offset = cbLoad.get_regIndex();
- CallInst *load =
- Builder.CreateCall(BufLoad, {opArg, handle, offset, undefI});
- // Find extractelement uses of cbuffer load and replace + generate bitcast
- // as necessary
- for (auto LU = I->user_begin(); LU != I->user_end();) {
- ExtractValueInst *evInst = dyn_cast<ExtractValueInst>(*(LU++));
- DXASSERT(evInst && evInst->getNumIndices() == 1,
- "user of cbuffer load result should be extractvalue");
- uint64_t idx = evInst->getIndices()[0];
- Type *EltTy = evInst->getType();
- IRBuilder<> EEBuilder(evInst);
- Value *result = nullptr;
- if (EltTy != Ty) {
- // extract two values and DXIL::OpCode::MakeDouble or construct i64
- if ((EltTy == doubleTy) || (EltTy == i64Ty)) {
- DXASSERT(idx < 2, "64-bit component index out of range");
- // This assumes big endian order in tbuffer elements (is this
- // correct?)
- Value *low = EEBuilder.CreateExtractValue(load, idx * 2);
- Value *high = EEBuilder.CreateExtractValue(load, idx * 2 + 1);
- if (EltTy == doubleTy) {
- opcode = OP::OpCode::MakeDouble;
- Function *MakeDouble = hlslOP->GetOpFunc(opcode, doubleTy);
- Constant *opArg = hlslOP->GetU32Const((unsigned)opcode);
- result = EEBuilder.CreateCall(MakeDouble, {opArg, low, high});
- } else {
- high = EEBuilder.CreateZExt(high, i64Ty);
- low = EEBuilder.CreateZExt(low, i64Ty);
- high = EEBuilder.CreateShl(high, hlslOP->GetU64Const(32));
- result = EEBuilder.CreateOr(high, low);
- }
- } else {
- result = EEBuilder.CreateExtractValue(load, idx);
- result = EEBuilder.CreateBitCast(result, EltTy);
- }
- } else {
- result = EEBuilder.CreateExtractValue(load, idx);
- }
- evInst->replaceAllUsesWith(result);
- evInst->eraseFromParent();
- }
- } else if (opcode == DXIL::OpCode::CBufferLoad) {
- // TODO: Handle this, or prevent this for tbuffer
- DXASSERT(false, "otherwise CBufferLoad used for tbuffer rather than "
- "CBufferLoadLegacy");
- } else {
- DXASSERT(false, "otherwise unexpected user of CreateHandle value");
- }
- I->eraseFromParent();
- }
- }
- } // namespace
- void DxilLowerCreateHandleForLib::PatchTBufferUse(Value *V, DxilModule &DM) {
- for (User *U : V->users()) {
- if (CallInst *CI = dyn_cast<CallInst>(U)) {
- // Patch dxil call.
- if (hlsl::OP::IsDxilOpFuncCallInst(CI))
- PatchTBufferLoad(CI, DM);
- } else {
- PatchTBufferUse(U, DM);
- }
- }
- }
- bool DxilLowerCreateHandleForLib::PatchTBuffers(DxilModule &DM) {
- bool bChanged = false;
- // move tbuffer resources to SRVs
- unsigned offset = DM.GetSRVs().size();
- Module &M = *DM.GetModule();
- for (auto it = DM.GetCBuffers().begin(); it != DM.GetCBuffers().end(); it++) {
- DxilCBuffer *CB = it->get();
- if (CB->GetKind() == DXIL::ResourceKind::TBuffer) {
- auto srv = make_unique<DxilResource>();
- InitTBuffer(CB, srv.get());
- srv->SetID(offset++);
- DM.AddSRV(std::move(srv));
- GlobalVariable *GV = cast<GlobalVariable>(CB->GetGlobalSymbol());
- PatchTBufferUse(GV, DM);
- // Set global symbol for cbuffer to an unused value so it can be removed
- // in RemoveUnusedResourceSymbols.
- Type *Ty = GV->getType()->getElementType();
- GlobalVariable *NewGV = new GlobalVariable(
- M, Ty, GV->isConstant(), GV->getLinkage(), /*Initializer*/ nullptr,
- GV->getName(),
- /*InsertBefore*/ nullptr, GV->getThreadLocalMode(),
- GV->getType()->getAddressSpace(), GV->isExternallyInitialized());
- CB->SetGlobalSymbol(NewGV);
- bChanged = true;
- }
- }
- return bChanged;
- }
- char DxilLowerCreateHandleForLib::ID = 0;
- ModulePass *llvm::createDxilLowerCreateHandleForLibPass() {
- return new DxilLowerCreateHandleForLib();
- }
- INITIALIZE_PASS(DxilLowerCreateHandleForLib, "hlsl-dxil-lower-handle-for-lib", "DXIL Lower createHandleForLib", false, false)
- class DxilAllocateResourcesForLib : public ModulePass {
- private:
- RemapEntryCollection m_rewrites;
- public:
- static char ID; // Pass identification, replacement for typeid
- explicit DxilAllocateResourcesForLib() : ModulePass(ID), m_AutoBindingSpace(UINT_MAX) {}
- void applyOptions(PassOptions O) override {
- GetPassOptionUInt32(O, "auto-binding-space", &m_AutoBindingSpace, UINT_MAX);
- }
- const char *getPassName() const override { return "DXIL Condense Resources"; }
- bool runOnModule(Module &M) override {
- DxilModule &DM = M.GetOrCreateDxilModule();
- // Must specify a default space, and must apply to library.
- // Use DxilCondenseResources instead for shaders.
- if ((m_AutoBindingSpace == UINT_MAX) || !DM.GetShaderModel()->IsLib())
- return false;
- bool hasResource = DM.GetCBuffers().size() ||
- DM.GetUAVs().size() || DM.GetSRVs().size() || DM.GetSamplers().size();
- if (hasResource) {
- DM.SetAutoBindingSpace(m_AutoBindingSpace);
- AllocateDxilResources(DM);
- }
- return true;
- }
- private:
- uint32_t m_AutoBindingSpace;
- };
- char DxilAllocateResourcesForLib::ID = 0;
- ModulePass *llvm::createDxilAllocateResourcesForLibPass() {
- return new DxilAllocateResourcesForLib();
- }
- INITIALIZE_PASS(DxilAllocateResourcesForLib, "hlsl-dxil-allocate-resources-for-lib", "DXIL Allocate Resources For Library", false, false)
|