| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424 |
- ///////////////////////////////////////////////////////////////////////////////
- // //
- // 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/DXIL/DxilOperations.h"
- #include "dxc/DXIL/DxilSignatureElement.h"
- #include "dxc/DXIL/DxilModule.h"
- #include "dxc/Support/Global.h"
- #include "dxc/DXIL/DxilTypeSystem.h"
- #include "dxc/DXIL/DxilInstructions.h"
- #include "dxc/HLSL/DxilSpanAllocator.h"
- #include "dxc/HLSL/HLMatrixType.h"
- #include "dxc/DXIL/DxilUtil.h"
- #include "dxc/HLSL/HLMatrixType.h"
- #include "dxc/HLSL/HLModule.h"
- #include "llvm/Analysis/DxilValueCache.h"
- #include "dxc/DXIL/DxilMetadataHelper.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
- class DxilResourceRegisterAllocator {
- private:
- SpacesAllocator<unsigned, hlsl::DxilCBuffer> m_reservedCBufferRegisters;
- SpacesAllocator<unsigned, hlsl::DxilSampler> m_reservedSamplerRegisters;
- SpacesAllocator<unsigned, hlsl::DxilResource> m_reservedUAVRegisters;
- SpacesAllocator<unsigned, hlsl::DxilResource> m_reservedSRVRegisters;
- template<typename T>
- static void GatherReservedRegisters(
- const std::vector<std::unique_ptr<T>> &ResourceList,
- SpacesAllocator<unsigned, T> &SAlloc) {
- for (auto &res : ResourceList) {
- if (res->IsAllocated()) {
- typename SpacesAllocator<unsigned, T>::Allocator &Alloc = SAlloc.Get(res->GetSpaceID());
- Alloc.ForceInsertAndClobber(res.get(), res->GetLowerBound(), res->GetUpperBound());
- if (res->IsUnbounded())
- Alloc.SetUnbounded(res.get());
- }
- }
- }
- template <typename T>
- static bool
- AllocateRegisters(const std::vector<std::unique_ptr<T>> &resourceList,
- SpacesAllocator<unsigned, T> &ReservedRegisters,
- unsigned AutoBindingSpace) {
- bool bChanged = false;
- SpacesAllocator<unsigned, T> SAlloc;
- // Reserve explicitly allocated resources
- for (auto &res : resourceList) {
- const unsigned space = res->GetSpaceID();
- typename SpacesAllocator<unsigned, T>::Allocator &alloc = SAlloc.Get(space);
- typename SpacesAllocator<unsigned, T>::Allocator &reservedAlloc = ReservedRegisters.Get(space);
- if (res->IsAllocated()) {
- const unsigned reg = res->GetLowerBound();
- const T *conflict = nullptr;
- if (res->IsUnbounded()) {
- const T *unbounded = alloc.GetUnbounded();
- if (unbounded) {
- dxilutil::EmitErrorOnGlobalVariable(dyn_cast<GlobalVariable>(res->GetGlobalSymbol()),
- 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());
- reservedAlloc.SetUnbounded(res.get());
- }
- }
- }
- else {
- conflict = alloc.Insert(res.get(), reg, res->GetUpperBound());
- }
- if (conflict) {
- dxilutil::EmitErrorOnGlobalVariable(dyn_cast<GlobalVariable>(res->GetGlobalSymbol()),
- ((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));
- }
- else {
- // Also add this to the reserved (unallocatable) range, if it wasn't already there.
- reservedAlloc.ForceInsertAndClobber(res.get(), res->GetLowerBound(), res->GetUpperBound());
- }
- }
- }
- // Allocate unallocated resources
- for (auto &res : resourceList) {
- if (res->IsAllocated())
- continue;
- unsigned space = res->GetSpaceID();
- if (space == UINT_MAX) space = AutoBindingSpace;
- typename SpacesAllocator<unsigned, T>::Allocator& alloc = SAlloc.Get(space);
- typename SpacesAllocator<unsigned, T>::Allocator& reservedAlloc = ReservedRegisters.Get(space);
- unsigned reg = 0;
- unsigned end = 0;
- bool allocateSpaceFound = false;
- if (res->IsUnbounded()) {
- if (alloc.GetUnbounded() != nullptr) {
- const T *unbounded = alloc.GetUnbounded();
- dxilutil::EmitErrorOnGlobalVariable(dyn_cast<GlobalVariable>(res->GetGlobalSymbol()),
- Twine("more than one unbounded resource (") +
- unbounded->GetGlobalName() + Twine(" and ") +
- res->GetGlobalName() + Twine(") in space ") +
- Twine(space));
- continue;
- }
- if (reservedAlloc.FindForUnbounded(reg)) {
- end = UINT_MAX;
- allocateSpaceFound = true;
- }
- }
- else if (reservedAlloc.Find(res->GetRangeSize(), reg)) {
- end = reg + res->GetRangeSize() - 1;
- allocateSpaceFound = true;
- }
- if (allocateSpaceFound) {
- bool success = reservedAlloc.Insert(res.get(), reg, end) == nullptr;
- DXASSERT_NOMSG(success);
- success = alloc.Insert(res.get(), reg, end) == nullptr;
- DXASSERT_NOMSG(success);
- if (res->IsUnbounded()) {
- alloc.SetUnbounded(res.get());
- reservedAlloc.SetUnbounded(res.get());
- }
- res->SetLowerBound(reg);
- res->SetSpaceID(space);
- bChanged = true;
- } else {
- dxilutil::EmitErrorOnGlobalVariable(dyn_cast<GlobalVariable>(res->GetGlobalSymbol()),
- ((res->IsUnbounded()) ? Twine("unbounded ") : Twine("")) +
- Twine("resource ") + res->GetGlobalName() +
- Twine(" could not be allocated"));
- }
- }
- return bChanged;
- }
- public:
- void GatherReservedRegisters(DxilModule &DM) {
- // For backcompat with FXC, shader models 5.0 and below will not auto-allocate
- // resources at a register explicitely assigned to even an unused resource.
- if (DM.GetLegacyResourceReservation()) {
- GatherReservedRegisters(DM.GetCBuffers(), m_reservedCBufferRegisters);
- GatherReservedRegisters(DM.GetSamplers(), m_reservedSamplerRegisters);
- GatherReservedRegisters(DM.GetUAVs(), m_reservedUAVRegisters);
- GatherReservedRegisters(DM.GetSRVs(), m_reservedSRVRegisters);
- }
- }
- bool AllocateRegisters(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 |= AllocateRegisters(DM.GetCBuffers(), m_reservedCBufferRegisters, AutoBindingSpace);
- bChanged |= AllocateRegisters(DM.GetSamplers(), m_reservedSamplerRegisters, AutoBindingSpace);
- bChanged |= AllocateRegisters(DM.GetUAVs(), m_reservedUAVRegisters, AutoBindingSpace);
- bChanged |= AllocateRegisters(DM.GetSRVs(), m_reservedSRVRegisters, AutoBindingSpace);
- return bChanged;
- }
- };
- 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;
- // Gather reserved resource registers while we still have
- // unused resources that might have explicit register assignments.
- DxilResourceRegisterAllocator ResourceRegisterAllocator;
- ResourceRegisterAllocator.GatherReservedRegisters(DM);
- // Remove unused resources.
- 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()) {
- ResourceRegisterAllocator.AllocateRegisters(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);
- }
- }
- }
- }
- 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)
- static
- bool LegalizeResourcesPHIs(Module &M, DxilValueCache *DVC) {
- // Simple pass to collect resource PHI's
- SmallVector<PHINode *, 8> PHIs;
- for (Function &F : M) {
- for (BasicBlock &BB : F) {
- for (Instruction &I : BB) {
- if (PHINode *PN = dyn_cast<PHINode>(&I)) {
- if (hlsl::dxilutil::IsHLSLResourceType(PN->getType())) {
- PHIs.push_back(PN);
- }
- }
- else {
- break;
- }
- }
- }
- }
- bool Changed = false;
- SmallVector<Instruction *, 8> DCEWorklist;
- // Try to simplify those PHI's with DVC and collect them in DCEWorklist
- for (unsigned Attempt = 0, MaxAttempt = PHIs.size(); Attempt < MaxAttempt; Attempt++) {
- bool LocalChanged = false;
- for (unsigned i = 0; i < PHIs.size(); i++) {
- PHINode *PN = PHIs[i];
- if (Value *V = DVC->GetValue(PN)) {
- PN->replaceAllUsesWith(V);
- LocalChanged = true;
- DCEWorklist.push_back(PN);
- PHIs.erase(PHIs.begin() + i);
- }
- else {
- i++;
- }
- }
- Changed |= LocalChanged;
- if (!LocalChanged)
- break;
- }
- // Collect Resource GV loads
- for (GlobalVariable &GV : M.globals()) {
- Type *Ty = GV.getType()->getPointerElementType();
- while (Ty->isArrayTy())
- Ty = Ty->getArrayElementType();
- if (!hlsl::dxilutil::IsHLSLResourceType(Ty))
- continue;
- SmallVector<User *, 4> WorkList(GV.user_begin(), GV.user_end());
- while (WorkList.size()) {
- User *U = WorkList.pop_back_val();
- if (LoadInst *Load = dyn_cast<LoadInst>(U)) {
- DCEWorklist.push_back(Load);
- }
- else if (GEPOperator *GEP = dyn_cast<GEPOperator>(U)) {
- for (User *GepU : GEP->users())
- WorkList.push_back(GepU);
- }
- }
- }
- // Simple DCE
- while (DCEWorklist.size()) {
- Instruction *I = DCEWorklist.back();
- DCEWorklist.pop_back();
- if (llvm::isInstructionTriviallyDead(I)) {
- for (Use &Op : I->operands())
- if (Instruction *OpI = dyn_cast<Instruction>(Op.get()))
- DCEWorklist.push_back(OpI);
- I->eraseFromParent();
- // Remove the instruction from the worklist if it still exists in it.
- DCEWorklist.erase(std::remove(DCEWorklist.begin(), DCEWorklist.end(), I),
- DCEWorklist.end());
- Changed = true;
- }
- }
- return Changed;
- }
- 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) {}
- void getAnalysisUsage(AnalysisUsage &AU) const override {
- AU.addRequired<DxilValueCache>();
- }
- 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;
- FailOnPoisonResources();
- 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;
- // Gather reserved resource registers while we still have
- // unused resources that might have explicit register assignments.
- DxilResourceRegisterAllocator ResourceRegisterAllocator;
- ResourceRegisterAllocator.GatherReservedRegisters(DM);
- // Remove unused resources.
- DM.RemoveResourcesWithUnusedSymbols();
- unsigned newResources = DM.GetCBuffers().size() + DM.GetUAVs().size() +
- DM.GetSRVs().size() + DM.GetSamplers().size();
- bChanged = bChanged || (numResources != newResources);
- if (0 == newResources)
- return bChanged;
- {
- DxilValueCache *DVC = &getAnalysis<DxilValueCache>();
- bool bLocalChanged = LegalizeResourcesPHIs(M, DVC);
- if (bLocalChanged) {
- // Remove unused resources.
- DM.RemoveResourcesWithUnusedSymbols();
- }
- bChanged |= bLocalChanged;
- }
- bChanged |= ResourceRegisterAllocator.AllocateRegisters(DM);
- // Fill in top-level CBuffer variable usage bit
- UpdateCBufferUsage();
- 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();
- // TODO: Update types earlier for libraries and replace users, to
- // avoid having to preserve HL struct annotation.
- // Note 1: Needs to happen after legalize
- // Note 2: Cannot do this easily/trivially if any functions have
- // resource arguments (in offline linking target).
- 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:
- void FailOnPoisonResources();
- 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);
- void UpdateCBufferUsage();
- };
- } // 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;
- }
- V->getContext().emitError(Twine(ErrorText[ec]) + " Value: " + Name);
- }
- }
- 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;
- Type *i32Ty = IntegerType::getInt32Ty(Stores[0]->getContext());
- 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];
- // If Val is not found, it is treated as an undef value that will translate
- // to an undef index, which may still be valid if it's never used.
- Value *UndefIndex = valIndices.size() > 0 ? nullptr : UndefValue::get(i32Ty);
- DXASSERT_NOMSG(valIndices.size() == 0 || ptrIndices.size() == valIndices.size());
- idxVector.resize(ptrIndices.size(), nullptr);
- for (unsigned i = 0; i < idxVector.size(); i++) {
- idxVector[i] = Builder.CreateStore(
- UndefIndex ? UndefIndex : valIndices[i],
- ptrIndices[i]);
- }
- }
- }
- // For each Phi/Select: update matching incoming values for new phis
- void UpdateSelects() {
- if (Selects.empty())
- return;
- Type *i32Ty = IntegerType::getInt32Ty(Selects[0]->getContext());
- 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++) {
- Value *Val = I->getOperand(iOp);
- IndexVector &incomingIndices = ResToIdxReplacement[Val];
- // If Val is not found, it is treated as an undef value that will translate
- // to an undef index, which may still be valid if it's never used.
- Value *UndefIndex = incomingIndices.size() > 0 ? nullptr : UndefValue::get(i32Ty);
- DXASSERT_NOMSG(incomingIndices.size() == 0 || 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,
- UndefIndex ? UndefIndex : 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,
- DxilTypeSystem &TypeSys, Module &M);
- Type *UpdateFieldTypeForLegacyLayout(Type *Ty,
- 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, annotation, TypeSys, M);
- if (EltTy == UpdatedTy)
- return Ty;
- else
- return ArrayType::get(UpdatedTy, Ty->getArrayNumElements());
- } else if (hlsl::HLMatrixType::isa(Ty)) {
- DXASSERT(annotation.HasMatrixAnnotation(), "must a matrix");
- HLMatrixType MatTy = HLMatrixType::cast(Ty);
- unsigned rows = MatTy.getNumRows();
- unsigned cols = MatTy.getNumColumns();
- Type *EltTy = MatTy.getElementTypeForReg();
- // 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;
- }
- EltTy =
- UpdateFieldTypeForLegacyLayout(EltTy, annotation, TypeSys, M);
- Type *rowTy = VectorType::get(EltTy, cols);
- // Matrix should be aligned like array if rows > 1,
- // otherwise, it's just like a vector.
- if (rows > 1)
- return ArrayType::get(rowTy, rows);
- else
- return rowTy;
- } else if (StructType *ST = dyn_cast<StructType>(Ty)) {
- return UpdateStructTypeForLegacyLayout(ST, TypeSys, M);
- } else if (Ty->isVectorTy()) {
- Type *EltTy = Ty->getVectorElementType();
- Type *UpdatedTy =
- UpdateFieldTypeForLegacyLayout(EltTy, 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,
- DxilTypeSystem &TypeSys,
- Module &M) {
- bool bUpdated = false;
- unsigned fieldsCount = ST->getNumElements();
- std::vector<Type *> fieldTypes(fieldsCount);
- DxilStructAnnotation *SA = TypeSys.GetStructAnnotation(ST);
- // After reflection is stripped from library, this will be null if no update is required.
- if (!SA) {
- return ST;
- }
- if (SA->IsEmptyStruct()) {
- return ST;
- }
- for (unsigned i = 0; i < fieldsCount; i++) {
- Type *EltTy = ST->getElementType(i);
- Type *UpdatedTy = UpdateFieldTypeForLegacyLayout(
- EltTy, 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;
- // Make sure we set the struct type back to the new one, since the
- // clone would have clobbered it with the old one.
- NewSA->SetStructType(NewST);
- return NewST;
- }
- }
- void UpdateStructTypeForLegacyLayout(DxilResourceBase &Res,
- DxilTypeSystem &TypeSys, Module &M) {
- Constant *Symbol = Res.GetGlobalSymbol();
- Type *ElemTy = Symbol->getType()->getPointerElementType();
- // Support Array of ConstantBuffer/StructuredBuffer.
- llvm::SmallVector<unsigned, 4> arrayDims;
- ElemTy = dxilutil::StripArrayTypes(ElemTy, &arrayDims);
- StructType *ST = cast<StructType>(ElemTy);
- if (ST->isOpaque()) {
- DXASSERT(Res.GetClass() == DxilResourceBase::Class::CBuffer,
- "Only cbuffer can have opaque struct.");
- return;
- }
- Type *UpdatedST =
- UpdateStructTypeForLegacyLayout(ST, TypeSys, M);
- if (ST != UpdatedST) {
- // Support Array of ConstantBuffer/StructuredBuffer.
- UpdatedST = dxilutil::WrapInArrayTypes(UpdatedST, arrayDims);
- GlobalVariable *NewGV = cast<GlobalVariable>(
- M.getOrInsertGlobal(Symbol->getName().str() + "_legacy", UpdatedST));
- Res.SetGlobalSymbol(NewGV);
- // Delete old GV.
- for (auto UserIt = Symbol->user_begin(); UserIt != Symbol->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()));
- }
- }
- Symbol->removeDeadConstantUsers();
- if (GlobalVariable *GV = dyn_cast<GlobalVariable>(Symbol))
- 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 (DXIL::IsStructuredBuffer(UAV->GetKind()))
- UpdateStructTypeForLegacyLayout(*UAV.get(), TypeSys, M);
- }
- for (auto &SRV : DM.GetSRVs()) {
- if (SRV->GetKind() == DxilResourceBase::Kind::StructuredBuffer)
- UpdateStructTypeForLegacyLayout(*SRV.get(), TypeSys, M);
- }
- }
- } // namespace
- void DxilLowerCreateHandleForLib::FailOnPoisonResources() {
- // A previous pass replaced all undef resources with constant zero resources.
- // If those made it here, the program is malformed.
- for (Function &Func : this->m_DM->GetModule()->functions()) {
- hlsl::OP::OpCodeClass OpcodeClass;
- if (m_DM->GetOP()->GetOpCodeClass(&Func, OpcodeClass)
- && OpcodeClass == OP::OpCodeClass::CreateHandleForLib) {
- Type *ResTy = Func.getFunctionType()->getParamType(
- DXIL::OperandIndex::kCreateHandleForLibResOpIdx);
- Constant *PoisonRes = ConstantAggregateZero::get(ResTy);
- for (User *PoisonUser : PoisonRes->users())
- if (Instruction *PoisonUserInst = dyn_cast<Instruction>(PoisonUser))
- dxilutil::EmitResMappingError(PoisonUserInst);
- }
- }
- }
- void DxilLowerCreateHandleForLib::UpdateStructTypeForLegacyLayout() {
- UpdateStructTypeForLegacyLayoutOnDM(*m_DM);
- }
- // Change ResourceSymbol to undef if don't need.
- void DxilLowerCreateHandleForLib::UpdateResourceSymbols() {
- auto UpdateResourceSymbol = [](DxilResourceBase *res) {
- if (GlobalVariable *GV = dyn_cast<GlobalVariable>(res->GetGlobalSymbol())) {
- GV->removeDeadConstantUsers();
- DXASSERT(GV->user_empty(), "else resource not lowered");
- res->SetGlobalSymbol(UndefValue::get(GV->getType()));
- }
- };
- 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();
- }
- } // 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();
- DXASSERT(isa<GlobalValue>(GV), "DxilLowerCreateHandleForLib cannot deal with unused resources.");
- 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 = dxilutil::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 if (opcode == DXIL::OpCode::AnnotateHandle) {
- DxilInst_AnnotateHandle annotateHandle(I);
- PatchTBufferLoad(cast<CallInst>(annotateHandle.get_res()), DM);
- } 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 = dyn_cast<GlobalVariable>(CB->GetGlobalSymbol());
- if (GV == nullptr)
- continue;
- 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;
- }
- typedef DenseMap<Value*, unsigned> OffsetForValueMap;
- // Find the imm offset part from a value.
- // It must exist unless offset is 0.
- static unsigned GetCBOffset(Value *V, OffsetForValueMap &visited) {
- auto it = visited.find(V);
- if (it != visited.end())
- return it->second;
- visited[V] = 0;
- unsigned result = 0;
- if (ConstantInt *Imm = dyn_cast<ConstantInt>(V)) {
- result = Imm->getLimitedValue();
- } else if (BinaryOperator *BO = dyn_cast<BinaryOperator>(V)) {
- switch (BO->getOpcode()) {
- case Instruction::Add: {
- unsigned left = GetCBOffset(BO->getOperand(0), visited);
- unsigned right = GetCBOffset(BO->getOperand(1), visited);
- result = left + right;
- } break;
- case Instruction::Or: {
- unsigned left = GetCBOffset(BO->getOperand(0), visited);
- unsigned right = GetCBOffset(BO->getOperand(1), visited);
- result = left | right;
- } break;
- default:
- break;
- }
- } else if (SelectInst *SI = dyn_cast<SelectInst>(V)) {
- result = std::min(GetCBOffset(SI->getOperand(1), visited),
- GetCBOffset(SI->getOperand(2), visited));
- } else if (PHINode *PN = dyn_cast<PHINode>(V)) {
- result = UINT_MAX;
- for (unsigned i = 0, ops = PN->getNumIncomingValues(); i < ops; ++i) {
- result = std::min(result, GetCBOffset(PN->getIncomingValue(i), visited));
- }
- }
- visited[V] = result;
- return result;
- }
- typedef std::map<unsigned, DxilFieldAnnotation*> FieldAnnotationByOffsetMap;
- static void MarkCBUse(unsigned offset, FieldAnnotationByOffsetMap &fieldMap) {
- auto it = fieldMap.upper_bound(offset);
- it--;
- if (it != fieldMap.end())
- it->second->SetCBVarUsed(true);
- }
- static unsigned GetOffsetForCBExtractValue(ExtractValueInst *EV, bool bMinPrecision) {
- DXASSERT(EV->getNumIndices() == 1, "otherwise, unexpected indices/type for extractvalue");
- unsigned typeSize = 4;
- unsigned bits = EV->getType()->getScalarSizeInBits();
- if (bits == 64)
- typeSize = 8;
- else if (bits == 16 && !bMinPrecision)
- typeSize = 2;
- return (EV->getIndices().front() * typeSize);
- }
- static void CollectInPhiChain(PHINode *cbUser, unsigned offset,
- std::unordered_set<Value *> &userSet,
- FieldAnnotationByOffsetMap &fieldMap,
- bool bMinPrecision) {
- if (userSet.count(cbUser) > 0)
- return;
- userSet.insert(cbUser);
- for (User *cbU : cbUser->users()) {
- if (ExtractValueInst *EV = dyn_cast<ExtractValueInst>(cbU)) {
- MarkCBUse(offset + GetOffsetForCBExtractValue(EV, bMinPrecision), fieldMap);
- } else {
- PHINode *phi = cast<PHINode>(cbU);
- CollectInPhiChain(phi, offset, userSet, fieldMap, bMinPrecision);
- }
- }
- }
- static void CollectCBufferMemberUsage(Value *V,
- FieldAnnotationByOffsetMap &legacyFieldMap,
- FieldAnnotationByOffsetMap &newFieldMap,
- hlsl::OP *hlslOP, bool bMinPrecision,
- OffsetForValueMap &visited) {
- for (auto U : V->users()) {
- if (Constant *C = dyn_cast<Constant>(U)) {
- CollectCBufferMemberUsage(C, legacyFieldMap, newFieldMap, hlslOP, bMinPrecision, visited);
- } else if (LoadInst *LI = dyn_cast<LoadInst>(U)) {
- CollectCBufferMemberUsage(U, legacyFieldMap, newFieldMap, hlslOP, bMinPrecision, visited);
- } else if (CallInst *CI = dyn_cast<CallInst>(U)) {
- if (hlslOP->IsDxilOpFuncCallInst(CI)) {
- hlsl::OP::OpCode op = hlslOP->GetDxilOpFuncCallInst(CI);
- if (op == DXIL::OpCode::CreateHandleForLib) {
- CollectCBufferMemberUsage(U, legacyFieldMap, newFieldMap, hlslOP, bMinPrecision, visited);
- } else if (op == DXIL::OpCode::AnnotateHandle) {
- CollectCBufferMemberUsage(U, legacyFieldMap, newFieldMap, hlslOP,
- bMinPrecision, visited);
- } else if (op == DXIL::OpCode::CBufferLoadLegacy) {
- DxilInst_CBufferLoadLegacy cbload(CI);
- Value *resIndex = cbload.get_regIndex();
- unsigned offset = GetCBOffset(resIndex, visited);
- offset <<= 4; // translate 16-byte vector index to byte offset
- for (User *cbU : U->users()) {
- if (ExtractValueInst *EV = dyn_cast<ExtractValueInst>(cbU)) {
- MarkCBUse(offset + GetOffsetForCBExtractValue(EV, bMinPrecision), legacyFieldMap);
- } else {
- PHINode *phi = cast<PHINode>(cbU);
- std::unordered_set<Value *> userSet;
- CollectInPhiChain(phi, offset, userSet, legacyFieldMap, bMinPrecision);
- }
- }
- } else if (op == DXIL::OpCode::CBufferLoad) {
- DxilInst_CBufferLoad cbload(CI);
- Value *byteOffset = cbload.get_byteOffset();
- unsigned offset = GetCBOffset(byteOffset, visited);
- MarkCBUse(offset, newFieldMap);
- }
- }
- }
- }
- }
- void DxilLowerCreateHandleForLib::UpdateCBufferUsage() {
- DxilTypeSystem &TypeSys = m_DM->GetTypeSystem();
- hlsl::OP *hlslOP = m_DM->GetOP();
- const DataLayout &DL = m_DM->GetModule()->getDataLayout();
- const auto &CBuffers = m_DM->GetCBuffers();
- OffsetForValueMap visited;
- for (auto it = CBuffers.begin(); it != CBuffers.end(); it++) {
- DxilCBuffer *CB = it->get();
- GlobalVariable *GV = dyn_cast<GlobalVariable>(CB->GetGlobalSymbol());
- if (GV == nullptr)
- continue;
- Type *ElemTy = GV->getType()->getPointerElementType();
- ElemTy = dxilutil::StripArrayTypes(ElemTy, nullptr);
- StructType *ST = cast<StructType>(ElemTy);
- DxilStructAnnotation *SA = TypeSys.GetStructAnnotation(ST);
- if (SA == nullptr)
- continue;
- // If elements < 2, it's used if it exists.
- // Only old-style cbuffer { ... } will have more than one member, and
- // old-style cbuffers are the only ones that report usage per member.
- if (ST->getStructNumElements() < 2) {
- continue;
- }
- // Create offset maps for legacy layout and new compact layout, while resetting usage flags
- const StructLayout *SL = DL.getStructLayout(ST);
- FieldAnnotationByOffsetMap legacyFieldMap, newFieldMap;
- for (unsigned i = 0; i < SA->GetNumFields(); ++i) {
- DxilFieldAnnotation &FA = SA->GetFieldAnnotation(i);
- FA.SetCBVarUsed(false);
- legacyFieldMap[FA.GetCBufferOffset()] = &FA;
- newFieldMap[(unsigned)SL->getElementOffset(i)] = &FA;
- }
- CollectCBufferMemberUsage(GV, legacyFieldMap, newFieldMap, hlslOP, m_DM->GetUseMinPrecision(), visited);
- }
- }
- char DxilLowerCreateHandleForLib::ID = 0;
- ModulePass *llvm::createDxilLowerCreateHandleForLibPass() {
- return new DxilLowerCreateHandleForLib();
- }
- INITIALIZE_PASS_BEGIN(DxilLowerCreateHandleForLib, "hlsl-dxil-lower-handle-for-lib", "DXIL Lower createHandleForLib", false, false)
- INITIALIZE_PASS_DEPENDENCY(DxilValueCache)
- INITIALIZE_PASS_END(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);
- DxilResourceRegisterAllocator ResourceRegisterAllocator;
- ResourceRegisterAllocator.AllocateRegisters(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)
|