DxilCondenseResources.cpp 74 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // //
  3. // DxilCondenseResources.cpp //
  4. // Copyright (C) Microsoft Corporation. All rights reserved. //
  5. // This file is distributed under the University of Illinois Open Source //
  6. // License. See LICENSE.TXT for details. //
  7. // //
  8. // Provides a pass to make resource IDs zero-based and dense. //
  9. // //
  10. ///////////////////////////////////////////////////////////////////////////////
  11. #include "dxc/HLSL/DxilGenerationPass.h"
  12. #include "dxc/HLSL/DxilOperations.h"
  13. #include "dxc/HLSL/DxilSignatureElement.h"
  14. #include "dxc/HLSL/DxilModule.h"
  15. #include "dxc/Support/Global.h"
  16. #include "dxc/HLSL/DxilTypeSystem.h"
  17. #include "dxc/HLSL/DxilInstructions.h"
  18. #include "dxc/HLSL/DxilSpanAllocator.h"
  19. #include "dxc/HLSL/HLMatrixLowerHelper.h"
  20. #include "dxc/HLSL/DxilUtil.h"
  21. #include "dxc/HLSL/HLModule.h"
  22. #include "llvm/IR/Instructions.h"
  23. #include "llvm/IR/IntrinsicInst.h"
  24. #include "llvm/IR/InstIterator.h"
  25. #include "llvm/IR/Module.h"
  26. #include "llvm/IR/PassManager.h"
  27. #include "llvm/IR/DebugInfo.h"
  28. #include "llvm/ADT/BitVector.h"
  29. #include "llvm/ADT/SetVector.h"
  30. #include "llvm/Pass.h"
  31. #include "llvm/Transforms/Utils/Local.h"
  32. #include <memory>
  33. #include <unordered_set>
  34. using namespace llvm;
  35. using namespace hlsl;
  36. // Resource rangeID remap.
  37. namespace {
  38. struct ResourceID {
  39. DXIL::ResourceClass Class; // Resource class.
  40. unsigned ID; // Resource ID, as specified on entry.
  41. bool operator<(const ResourceID &other) const {
  42. if (Class < other.Class)
  43. return true;
  44. if (Class > other.Class)
  45. return false;
  46. if (ID < other.ID)
  47. return true;
  48. return false;
  49. }
  50. };
  51. struct RemapEntry {
  52. ResourceID ResID; // Resource identity, as specified on entry.
  53. DxilResourceBase *Resource; // In-memory resource representation.
  54. unsigned Index; // Index in resource vector - new ID for the resource.
  55. };
  56. typedef std::map<ResourceID, RemapEntry> RemapEntryCollection;
  57. template <typename TResource>
  58. void BuildRewrites(const std::vector<std::unique_ptr<TResource>> &Rs,
  59. RemapEntryCollection &C) {
  60. const unsigned s = (unsigned)Rs.size();
  61. for (unsigned i = 0; i < s; ++i) {
  62. const std::unique_ptr<TResource> &R = Rs[i];
  63. if (R->GetID() != i) {
  64. ResourceID RId = {R->GetClass(), R->GetID()};
  65. RemapEntry RE = {RId, R.get(), i};
  66. C[RId] = RE;
  67. }
  68. }
  69. }
  70. // Build m_rewrites, returns 'true' if any rewrites are needed.
  71. bool BuildRewriteMap(RemapEntryCollection &rewrites, DxilModule &DM) {
  72. BuildRewrites(DM.GetCBuffers(), rewrites);
  73. BuildRewrites(DM.GetSRVs(), rewrites);
  74. BuildRewrites(DM.GetUAVs(), rewrites);
  75. BuildRewrites(DM.GetSamplers(), rewrites);
  76. return !rewrites.empty();
  77. }
  78. void ApplyRewriteMapOnResTable(RemapEntryCollection &rewrites, DxilModule &DM) {
  79. for (auto &entry : rewrites) {
  80. entry.second.Resource->SetID(entry.second.Index);
  81. }
  82. }
  83. } // namespace
  84. // Resource lowerBound allocation.
  85. namespace {
  86. template <typename T>
  87. static bool
  88. AllocateDxilResource(const std::vector<std::unique_ptr<T>> &resourceList,
  89. LLVMContext &Ctx, unsigned AutoBindingSpace=0) {
  90. bool bChanged = false;
  91. SpacesAllocator<unsigned, T> SAlloc;
  92. for (auto &res : resourceList) {
  93. const unsigned space = res->GetSpaceID();
  94. typename SpacesAllocator<unsigned, T>::Allocator &alloc = SAlloc.Get(space);
  95. if (res->IsAllocated()) {
  96. const unsigned reg = res->GetLowerBound();
  97. const T *conflict = nullptr;
  98. if (res->IsUnbounded()) {
  99. const T *unbounded = alloc.GetUnbounded();
  100. if (unbounded) {
  101. Ctx.emitError(Twine("more than one unbounded resource (") +
  102. unbounded->GetGlobalName() + (" and ") +
  103. res->GetGlobalName() + (") in space ") + Twine(space));
  104. } else {
  105. conflict = alloc.Insert(res.get(), reg, res->GetUpperBound());
  106. if (!conflict)
  107. alloc.SetUnbounded(res.get());
  108. }
  109. } else {
  110. conflict = alloc.Insert(res.get(), reg, res->GetUpperBound());
  111. }
  112. if (conflict) {
  113. Ctx.emitError(((res->IsUnbounded()) ? Twine("unbounded ") : Twine("")) +
  114. Twine("resource ") + res->GetGlobalName() +
  115. Twine(" at register ") + Twine(reg) +
  116. Twine(" overlaps with resource ") +
  117. conflict->GetGlobalName() + Twine(" at register ") +
  118. Twine(conflict->GetLowerBound()) + Twine(", space ") +
  119. Twine(space));
  120. }
  121. }
  122. }
  123. // Allocate.
  124. const unsigned space = AutoBindingSpace;
  125. typename SpacesAllocator<unsigned, T>::Allocator &alloc0 = SAlloc.Get(space);
  126. for (auto &res : resourceList) {
  127. if (!res->IsAllocated()) {
  128. DXASSERT(res->GetSpaceID() == 0,
  129. "otherwise non-zero space has no user register assignment");
  130. unsigned reg = 0;
  131. bool success = false;
  132. if (res->IsUnbounded()) {
  133. const T *unbounded = alloc0.GetUnbounded();
  134. if (unbounded) {
  135. Ctx.emitError(Twine("more than one unbounded resource (") +
  136. unbounded->GetGlobalName() + Twine(" and ") +
  137. res->GetGlobalName() + Twine(") in space ") +
  138. Twine(space));
  139. } else {
  140. success = alloc0.AllocateUnbounded(res.get(), reg);
  141. if (success)
  142. alloc0.SetUnbounded(res.get());
  143. }
  144. } else {
  145. success = alloc0.Allocate(res.get(), res->GetRangeSize(), reg);
  146. }
  147. if (success) {
  148. res->SetLowerBound(reg);
  149. res->SetSpaceID(space);
  150. bChanged = true;
  151. } else {
  152. Ctx.emitError(((res->IsUnbounded()) ? Twine("unbounded ") : Twine("")) +
  153. Twine("resource ") + res->GetGlobalName() +
  154. Twine(" could not be allocated"));
  155. }
  156. }
  157. }
  158. return bChanged;
  159. }
  160. bool AllocateDxilResources(DxilModule &DM) {
  161. uint32_t AutoBindingSpace = DM.GetAutoBindingSpace();
  162. if (AutoBindingSpace == UINT_MAX) {
  163. // For libraries, we don't allocate unless AutoBindingSpace is set.
  164. if (DM.GetShaderModel()->IsLib())
  165. return false;
  166. // For shaders, we allocate in space 0 by default.
  167. AutoBindingSpace = 0;
  168. }
  169. bool bChanged = false;
  170. bChanged |= AllocateDxilResource(DM.GetCBuffers(), DM.GetCtx(), AutoBindingSpace);
  171. bChanged |= AllocateDxilResource(DM.GetSamplers(), DM.GetCtx(), AutoBindingSpace);
  172. bChanged |= AllocateDxilResource(DM.GetUAVs(), DM.GetCtx(), AutoBindingSpace);
  173. bChanged |= AllocateDxilResource(DM.GetSRVs(), DM.GetCtx(), AutoBindingSpace);
  174. return bChanged;
  175. }
  176. } // namespace
  177. class DxilCondenseResources : public ModulePass {
  178. private:
  179. RemapEntryCollection m_rewrites;
  180. public:
  181. static char ID; // Pass identification, replacement for typeid
  182. explicit DxilCondenseResources() : ModulePass(ID) {}
  183. const char *getPassName() const override { return "DXIL Condense Resources"; }
  184. bool runOnModule(Module &M) override {
  185. DxilModule &DM = M.GetOrCreateDxilModule();
  186. // Skip lib.
  187. if (DM.GetShaderModel()->IsLib())
  188. return false;
  189. // Remove unused resource.
  190. DM.RemoveUnusedResources();
  191. // Make sure all resource types are dense; build a map of rewrites.
  192. if (BuildRewriteMap(m_rewrites, DM)) {
  193. // Rewrite all instructions that refer to resources in the map.
  194. ApplyRewriteMap(DM);
  195. }
  196. bool hasResource = DM.GetCBuffers().size() ||
  197. DM.GetUAVs().size() || DM.GetSRVs().size() || DM.GetSamplers().size();
  198. if (hasResource) {
  199. if (!DM.GetShaderModel()->IsLib()) {
  200. AllocateDxilResources(DM);
  201. PatchCreateHandle(DM);
  202. }
  203. }
  204. return true;
  205. }
  206. DxilResourceBase &GetFirstRewrite() const {
  207. DXASSERT_NOMSG(!m_rewrites.empty());
  208. return *m_rewrites.begin()->second.Resource;
  209. }
  210. private:
  211. void ApplyRewriteMap(DxilModule &DM);
  212. // Add lowbound to create handle range index.
  213. void PatchCreateHandle(DxilModule &DM);
  214. };
  215. void DxilCondenseResources::ApplyRewriteMap(DxilModule &DM) {
  216. for (Function &F : DM.GetModule()->functions()) {
  217. if (F.isDeclaration()) {
  218. continue;
  219. }
  220. for (inst_iterator iter = inst_begin(F), E = inst_end(F); iter != E; ++iter) {
  221. llvm::Instruction &I = *iter;
  222. DxilInst_CreateHandle CH(&I);
  223. if (!CH)
  224. continue;
  225. ResourceID RId;
  226. RId.Class = (DXIL::ResourceClass)CH.get_resourceClass_val();
  227. RId.ID = (unsigned)llvm::dyn_cast<llvm::ConstantInt>(CH.get_rangeId())
  228. ->getZExtValue();
  229. RemapEntryCollection::iterator it = m_rewrites.find(RId);
  230. if (it == m_rewrites.end()) {
  231. continue;
  232. }
  233. CallInst *CI = cast<CallInst>(&I);
  234. Value *newRangeID = DM.GetOP()->GetU32Const(it->second.Index);
  235. CI->setArgOperand(DXIL::OperandIndex::kCreateHandleResIDOpIdx,
  236. newRangeID);
  237. }
  238. }
  239. ApplyRewriteMapOnResTable(m_rewrites, DM);
  240. }
  241. namespace {
  242. void PatchLowerBoundOfCreateHandle(CallInst *handle, DxilModule &DM) {
  243. DxilInst_CreateHandle createHandle(handle);
  244. DXASSERT_NOMSG(createHandle);
  245. DXIL::ResourceClass ResClass =
  246. static_cast<DXIL::ResourceClass>(createHandle.get_resourceClass_val());
  247. // Dynamic rangeId is not supported - skip and let validation report the
  248. // error.
  249. if (!isa<ConstantInt>(createHandle.get_rangeId()))
  250. return;
  251. unsigned rangeId =
  252. cast<ConstantInt>(createHandle.get_rangeId())->getLimitedValue();
  253. DxilResourceBase *res = nullptr;
  254. switch (ResClass) {
  255. case DXIL::ResourceClass::SRV:
  256. res = &DM.GetSRV(rangeId);
  257. break;
  258. case DXIL::ResourceClass::UAV:
  259. res = &DM.GetUAV(rangeId);
  260. break;
  261. case DXIL::ResourceClass::CBuffer:
  262. res = &DM.GetCBuffer(rangeId);
  263. break;
  264. case DXIL::ResourceClass::Sampler:
  265. res = &DM.GetSampler(rangeId);
  266. break;
  267. default:
  268. DXASSERT(0, "invalid res class");
  269. return;
  270. }
  271. IRBuilder<> Builder(handle);
  272. unsigned lowBound = res->GetLowerBound();
  273. if (lowBound) {
  274. Value *Index = createHandle.get_index();
  275. if (ConstantInt *cIndex = dyn_cast<ConstantInt>(Index)) {
  276. unsigned newIdx = lowBound + cIndex->getLimitedValue();
  277. handle->setArgOperand(DXIL::OperandIndex::kCreateHandleResIndexOpIdx,
  278. Builder.getInt32(newIdx));
  279. } else {
  280. Value *newIdx = Builder.CreateAdd(Index, Builder.getInt32(lowBound));
  281. handle->setArgOperand(DXIL::OperandIndex::kCreateHandleResIndexOpIdx,
  282. newIdx);
  283. }
  284. }
  285. }
  286. static void PatchTBufferCreateHandle(CallInst *handle, DxilModule &DM, std::unordered_set<unsigned> &tbufferIDs) {
  287. DxilInst_CreateHandle createHandle(handle);
  288. DXASSERT_NOMSG(createHandle);
  289. DXIL::ResourceClass ResClass = static_cast<DXIL::ResourceClass>(createHandle.get_resourceClass_val());
  290. if (ResClass != DXIL::ResourceClass::CBuffer)
  291. return;
  292. Value *resID = createHandle.get_rangeId();
  293. DXASSERT(isa<ConstantInt>(resID), "cannot handle dynamic resID for cbuffer CreateHandle");
  294. if (!isa<ConstantInt>(resID))
  295. return;
  296. unsigned rangeId = cast<ConstantInt>(resID)->getLimitedValue();
  297. DxilResourceBase *res = &DM.GetCBuffer(rangeId);
  298. // For TBuffer, we need to switch resource type from CBuffer to SRV
  299. if (res->GetKind() == DXIL::ResourceKind::TBuffer) {
  300. // Track cbuffers IDs that are actually tbuffers
  301. tbufferIDs.insert(rangeId);
  302. hlsl::OP *hlslOP = DM.GetOP();
  303. llvm::LLVMContext &Ctx = DM.GetCtx();
  304. // Temporarily add SRV size to rangeID to guarantee unique new SRV ID
  305. Value *newRangeID = hlslOP->GetU32Const(rangeId + DM.GetSRVs().size());
  306. handle->setArgOperand(DXIL::OperandIndex::kCreateHandleResIDOpIdx,
  307. newRangeID);
  308. // switch create handle to SRV
  309. handle->setArgOperand(DXIL::OperandIndex::kCreateHandleResClassOpIdx,
  310. hlslOP->GetU8Const(
  311. static_cast<std::underlying_type<DxilResourceBase::Class>::type>(
  312. DXIL::ResourceClass::SRV)));
  313. Type *doubleTy = Type::getDoubleTy(Ctx);
  314. Type *i64Ty = Type::getInt64Ty(Ctx);
  315. // Replace corresponding cbuffer loads with typed buffer loads
  316. for (auto U = handle->user_begin(); U != handle->user_end(); ) {
  317. CallInst *I = cast<CallInst>(*(U++));
  318. DXASSERT(I && OP::IsDxilOpFuncCallInst(I), "otherwise unexpected user of CreateHandle value");
  319. DXIL::OpCode opcode = OP::GetDxilOpFuncCallInst(I);
  320. if (opcode == DXIL::OpCode::CBufferLoadLegacy) {
  321. DxilInst_CBufferLoadLegacy cbLoad(I);
  322. // Replace with appropriate buffer load instruction
  323. IRBuilder<> Builder(I);
  324. opcode = OP::OpCode::BufferLoad;
  325. Type *Ty = Type::getInt32Ty(Ctx);
  326. Function *BufLoad = hlslOP->GetOpFunc(opcode, Ty);
  327. Constant *opArg = hlslOP->GetU32Const((unsigned)opcode);
  328. Value *undefI = UndefValue::get(Type::getInt32Ty(Ctx));
  329. Value *offset = cbLoad.get_regIndex();
  330. CallInst* load = Builder.CreateCall(BufLoad, {opArg, handle, offset, undefI});
  331. // Find extractelement uses of cbuffer load and replace + generate bitcast as necessary
  332. for (auto LU = I->user_begin(); LU != I->user_end(); ) {
  333. ExtractValueInst *evInst = dyn_cast<ExtractValueInst>(*(LU++));
  334. DXASSERT(evInst && evInst->getNumIndices() == 1, "user of cbuffer load result should be extractvalue");
  335. uint64_t idx = evInst->getIndices()[0];
  336. Type *EltTy = evInst->getType();
  337. IRBuilder<> EEBuilder(evInst);
  338. Value *result = nullptr;
  339. if (EltTy != Ty) {
  340. // extract two values and DXIL::OpCode::MakeDouble or construct i64
  341. if ((EltTy == doubleTy) || (EltTy == i64Ty)) {
  342. DXASSERT(idx < 2, "64-bit component index out of range");
  343. // This assumes big endian order in tbuffer elements (is this correct?)
  344. Value *low = EEBuilder.CreateExtractValue(load, idx * 2);
  345. Value *high = EEBuilder.CreateExtractValue(load, idx * 2 + 1);
  346. if (EltTy == doubleTy) {
  347. opcode = OP::OpCode::MakeDouble;
  348. Function *MakeDouble = hlslOP->GetOpFunc(opcode, doubleTy);
  349. Constant *opArg = hlslOP->GetU32Const((unsigned)opcode);
  350. result = EEBuilder.CreateCall(MakeDouble, {opArg, low, high});
  351. } else {
  352. high = EEBuilder.CreateZExt(high, i64Ty);
  353. low = EEBuilder.CreateZExt(low, i64Ty);
  354. high = EEBuilder.CreateShl(high, hlslOP->GetU64Const(32));
  355. result = EEBuilder.CreateOr(high, low);
  356. }
  357. } else {
  358. result = EEBuilder.CreateExtractValue(load, idx);
  359. result = EEBuilder.CreateBitCast(result, EltTy);
  360. }
  361. } else {
  362. result = EEBuilder.CreateExtractValue(load, idx);
  363. }
  364. evInst->replaceAllUsesWith(result);
  365. evInst->eraseFromParent();
  366. }
  367. } else if (opcode == DXIL::OpCode::CBufferLoad) {
  368. // TODO: Handle this, or prevent this for tbuffer
  369. DXASSERT(false, "otherwise CBufferLoad used for tbuffer rather than CBufferLoadLegacy");
  370. } else {
  371. DXASSERT(false, "otherwise unexpected user of CreateHandle value");
  372. }
  373. I->eraseFromParent();
  374. }
  375. }
  376. }
  377. }
  378. void DxilCondenseResources::PatchCreateHandle(DxilModule &DM) {
  379. Function *createHandle = DM.GetOP()->GetOpFunc(DXIL::OpCode::CreateHandle,
  380. Type::getVoidTy(DM.GetCtx()));
  381. for (User *U : createHandle->users()) {
  382. PatchLowerBoundOfCreateHandle(cast<CallInst>(U), DM);
  383. }
  384. }
  385. char DxilCondenseResources::ID = 0;
  386. bool llvm::AreDxilResourcesDense(llvm::Module *M, hlsl::DxilResourceBase **ppNonDense) {
  387. DxilModule &DM = M->GetOrCreateDxilModule();
  388. RemapEntryCollection rewrites;
  389. if (BuildRewriteMap(rewrites, DM)) {
  390. *ppNonDense = rewrites.begin()->second.Resource;
  391. return false;
  392. }
  393. else {
  394. *ppNonDense = nullptr;
  395. return true;
  396. }
  397. }
  398. ModulePass *llvm::createDxilCondenseResourcesPass() {
  399. return new DxilCondenseResources();
  400. }
  401. INITIALIZE_PASS(DxilCondenseResources, "hlsl-dxil-condense", "DXIL Condense Resources", false, false)
  402. namespace {
  403. class DxilLowerCreateHandleForLib : public ModulePass {
  404. private:
  405. RemapEntryCollection m_rewrites;
  406. DxilModule *m_DM;
  407. bool m_HasDbgInfo;
  408. bool m_bIsLib;
  409. bool m_bLegalizationFailed;
  410. public:
  411. static char ID; // Pass identification, replacement for typeid
  412. explicit DxilLowerCreateHandleForLib() : ModulePass(ID) {}
  413. const char *getPassName() const override {
  414. return "DXIL Lower createHandleForLib";
  415. }
  416. bool runOnModule(Module &M) override {
  417. DxilModule &DM = M.GetOrCreateDxilModule();
  418. m_DM = &DM;
  419. // Clear llvm used to remove unused resource.
  420. m_DM->ClearLLVMUsed();
  421. m_bIsLib = DM.GetShaderModel()->IsLib();
  422. m_bLegalizationFailed = false;
  423. bool bChanged = false;
  424. unsigned numResources = DM.GetCBuffers().size() + DM.GetUAVs().size() +
  425. DM.GetSRVs().size() + DM.GetSamplers().size();
  426. if (!numResources)
  427. return false;
  428. // Switch tbuffers to SRVs, as they have been treated as cbuffers up to this
  429. // point.
  430. if (DM.GetCBuffers().size())
  431. bChanged = PatchTBuffers(DM) || bChanged;
  432. // Remove unused resource.
  433. DM.RemoveUnusedResourceSymbols();
  434. unsigned newResources = DM.GetCBuffers().size() + DM.GetUAVs().size() +
  435. DM.GetSRVs().size() + DM.GetSamplers().size();
  436. bChanged = bChanged || (numResources != newResources);
  437. if (0 == newResources)
  438. return bChanged;
  439. bChanged |= AllocateDxilResources(DM);
  440. if (m_bIsLib && DM.GetShaderModel()->GetMinor() == ShaderModel::kOfflineMinor)
  441. return bChanged;
  442. // Make sure no select on resource.
  443. bChanged |= RemovePhiOnResource();
  444. if (m_bIsLib || m_bLegalizationFailed)
  445. return bChanged;
  446. bChanged = true;
  447. // Load up debug information, to cross-reference values and the instructions
  448. // used to load them.
  449. m_HasDbgInfo = getDebugMetadataVersionFromModule(M) != 0;
  450. GenerateDxilResourceHandles();
  451. if (DM.GetOP()->UseMinPrecision())
  452. UpdateStructTypeForLegacyLayout();
  453. // Change resource symbol into undef.
  454. UpdateResourceSymbols();
  455. // Remove unused createHandleForLib functions.
  456. dxilutil::RemoveUnusedFunctions(M, DM.GetEntryFunction(),
  457. DM.GetPatchConstantFunction(), m_bIsLib);
  458. return bChanged;
  459. }
  460. private:
  461. bool RemovePhiOnResource();
  462. void UpdateResourceSymbols();
  463. void TranslateDxilResourceUses(DxilResourceBase &res);
  464. void GenerateDxilResourceHandles();
  465. void UpdateStructTypeForLegacyLayout();
  466. // Switch CBuffer for SRV for TBuffers.
  467. bool PatchTBuffers(DxilModule &DM);
  468. void PatchTBufferUse(Value *V, DxilModule &DM);
  469. };
  470. } // namespace
  471. // Phi on resource.
  472. namespace {
  473. typedef std::unordered_map<Value*, Value*> ValueToValueMap;
  474. typedef llvm::SetVector<Value*> ValueSetVector;
  475. typedef llvm::SmallVector<Value*, 4> IndexVector;
  476. typedef std::unordered_map<Value*, IndexVector> ValueToIdxMap;
  477. //#define SUPPORT_SELECT_ON_ALLOCA
  478. // Errors:
  479. class ResourceUseErrors
  480. {
  481. bool m_bErrorsReported;
  482. public:
  483. ResourceUseErrors() : m_bErrorsReported(false) {}
  484. enum ErrorCode {
  485. // Collision between use of one resource GV and another.
  486. // All uses must be guaranteed to resolve to only one GV.
  487. // Additionally, when writing resource to alloca, all uses
  488. // of that alloca are considered resolving to a single GV.
  489. GVConflicts,
  490. // static global resources are disallowed for libraries at this time.
  491. // for non-library targets, they should have been eliminated already.
  492. StaticGVUsed,
  493. // user function calls with resource params or return type are
  494. // are currently disallowed for libraries.
  495. UserCallsWithResources,
  496. // When searching up from store pointer looking for alloca,
  497. // we encountered an unexpted value type
  498. UnexpectedValuesFromStorePointer,
  499. // When remapping values to be replaced, we add them to RemappedValues
  500. // so we don't use dead values stored in other sets/maps. Circular
  501. // remaps that should not happen are aadded to RemappingCyclesDetected.
  502. RemappingCyclesDetected,
  503. // Without SUPPORT_SELECT_ON_ALLOCA, phi/select on alloca based
  504. // pointer is disallowed, since this scenario is still untested.
  505. // This error also covers any other unknown alloca pointer uses.
  506. // Supported:
  507. // alloca (-> gep)? -> load -> ...
  508. // alloca (-> gep)? -> store.
  509. // Unsupported without SUPPORT_SELECT_ON_ALLOCA:
  510. // alloca (-> gep)? -> phi/select -> ...
  511. AllocaUserDisallowed,
  512. #ifdef SUPPORT_SELECT_ON_ALLOCA
  513. // Conflict in select/phi between GV pointer and alloca pointer. This
  514. // algorithm can't handle this case.
  515. AllocaSelectConflict,
  516. #endif
  517. ErrorCodeCount
  518. };
  519. const StringRef ErrorText[ErrorCodeCount] = {
  520. "local resource not guaranteed to map to unique global resource.",
  521. "static global resource use is disallowed for library functions.",
  522. "exported library functions cannot have resource parameters or return value.",
  523. "internal error: unexpected instruction type when looking for alloca from store.",
  524. "internal error: cycles detected in value remapping.",
  525. "phi/select disallowed on pointers to local resources."
  526. #ifdef SUPPORT_SELECT_ON_ALLOCA
  527. ,"unable to resolve merge of global and local resource pointers."
  528. #endif
  529. };
  530. ValueSetVector ErrorSets[ErrorCodeCount];
  531. // Ulitimately, the goal of ErrorUsers is to mark all create handles
  532. // so we don't try to report errors on them again later.
  533. std::unordered_set<Value*> ErrorUsers; // users of error values
  534. bool AddErrorUsers(Value* V) {
  535. auto it = ErrorUsers.insert(V);
  536. if (!it.second)
  537. return false; // already there
  538. if (isa<GEPOperator>(V) ||
  539. isa<LoadInst>(V) ||
  540. isa<PHINode>(V) ||
  541. isa<SelectInst>(V) ||
  542. isa<AllocaInst>(V)) {
  543. for (auto U : V->users()) {
  544. AddErrorUsers(U);
  545. }
  546. } else if(isa<StoreInst>(V)) {
  547. AddErrorUsers(cast<StoreInst>(V)->getPointerOperand());
  548. }
  549. // create handle will be marked, but users not followed
  550. return true;
  551. }
  552. void ReportError(ErrorCode ec, Value* V) {
  553. DXASSERT_NOMSG(ec < ErrorCodeCount);
  554. if (!ErrorSets[ec].insert(V))
  555. return; // Error already reported
  556. AddErrorUsers(V);
  557. m_bErrorsReported = true;
  558. if (Instruction *I = dyn_cast<Instruction>(V)) {
  559. dxilutil::EmitErrorOnInstruction(I, ErrorText[ec]);
  560. } else {
  561. StringRef Name = V->getName();
  562. std::string escName;
  563. if (isa<Function>(V)) {
  564. llvm::raw_string_ostream os(escName);
  565. dxilutil::PrintEscapedString(Name, os);
  566. os.flush();
  567. Name = escName;
  568. }
  569. Twine msg = Twine(ErrorText[ec]) + " Value: " + Name;
  570. V->getContext().emitError(msg);
  571. }
  572. }
  573. bool ErrorsReported() {
  574. return m_bErrorsReported;
  575. }
  576. };
  577. unsigned CountArrayDimensions(Type* Ty,
  578. // Optionally collect dimensions
  579. SmallVector<unsigned, 4> *dims = nullptr) {
  580. if (Ty->isPointerTy())
  581. Ty = Ty->getPointerElementType();
  582. unsigned dim = 0;
  583. if (dims)
  584. dims->clear();
  585. while (Ty->isArrayTy()) {
  586. if (dims)
  587. dims->push_back(Ty->getArrayNumElements());
  588. dim++;
  589. Ty = Ty->getArrayElementType();
  590. }
  591. return dim;
  592. }
  593. // Helper class for legalizing resource use
  594. // Convert select/phi on resources to select/phi on index to GEP on GV.
  595. // Convert resource alloca to index alloca.
  596. // Assumes createHandleForLib has no select/phi
  597. class LegalizeResourceUseHelper {
  598. // Change:
  599. // gep1 = GEP gRes, i1
  600. // res1 = load gep1
  601. // gep2 = GEP gRes, i2
  602. // gep3 = GEP gRes, i3
  603. // gep4 = phi gep2, gep3 <-- handle select/phi on GEP
  604. // res4 = load gep4
  605. // res5 = phi res1, res4
  606. // res6 = load GEP gRes, 23 <-- handle constant GepExpression
  607. // res = select cnd2, res5, res6
  608. // handle = createHandleForLib(res)
  609. // To:
  610. // i4 = phi i2, i3
  611. // i5 = phi i1, i4
  612. // i6 = select cnd, i5, 23
  613. // gep = GEP gRes, i6
  614. // res = load gep
  615. // handle = createHandleForLib(res)
  616. // Also handles alloca
  617. // resArray = alloca [2 x Resource]
  618. // gep1 = GEP gRes, i1
  619. // res1 = load gep1
  620. // gep2 = GEP gRes, i2
  621. // gep3 = GEP gRes, i3
  622. // phi4 = phi gep2, gep3
  623. // res4 = load phi4
  624. // gep5 = GEP resArray, 0
  625. // gep6 = GEP resArray, 1
  626. // store gep5, res1
  627. // store gep6, res4
  628. // gep7 = GEP resArray, i7 <-- dynamically index array
  629. // res = load gep7
  630. // handle = createHandleForLib(res)
  631. // Desired result:
  632. // idxArray = alloca [2 x i32]
  633. // phi4 = phi i2, i3
  634. // gep5 = GEP idxArray, 0
  635. // gep6 = GEP idxArray, 1
  636. // store gep5, i1
  637. // store gep6, phi4
  638. // gep7 = GEP idxArray, i7
  639. // gep8 = GEP gRes, gep7
  640. // res = load gep8
  641. // handle = createHandleForLib(res)
  642. // Also handles multi-dim resource index and multi-dim resource array allocas
  643. // Basic algorithm:
  644. // - recursively mark each GV user with GV (ValueToResourceGV)
  645. // - verify only one GV used for any given value
  646. // - handle allocas by searching up from store for alloca
  647. // - then recursively mark alloca users
  648. // - ResToIdxReplacement keeps track of vector of indices that
  649. // will be used to replace a given resource value or pointer
  650. // - Next, create selects/phis for indices corresponding to
  651. // selects/phis on resource pointers or values.
  652. // - leave incoming index values undef for now
  653. // - Create index allocas to replace resource allocas
  654. // - Create GEPs on index allocas to replace GEPs on resource allocas
  655. // - Create index loads on index allocas to replace loads on resource alloca GEP
  656. // - Fill in replacements for GEPs on resource GVs
  657. // - copy replacement index vectors to corresponding loads
  658. // - Create index stores to replace resource stores to alloca/GEPs
  659. // - Update selects/phis incoming index values
  660. // - SimplifyMerges: replace index phis/selects on same value with that value
  661. // - RemappedValues[phi/select] set to replacement value
  662. // - use LookupValue from now on when reading from ResToIdxReplacement
  663. // - Update handles by replacing load/GEP chains that go through select/phi
  664. // with direct GV GEP + load, with select/phi on GEP indices instead.
  665. public:
  666. ResourceUseErrors m_Errors;
  667. ValueToValueMap ValueToResourceGV;
  668. ValueToIdxMap ResToIdxReplacement;
  669. // Value sets we can use to iterate
  670. ValueSetVector Selects, GEPs, Stores, Handles;
  671. ValueSetVector Allocas, AllocaGEPs, AllocaLoads;
  672. #ifdef SUPPORT_SELECT_ON_ALLOCA
  673. ValueSetVector AllocaSelects;
  674. #endif
  675. std::unordered_set<Value *> NonUniformSet;
  676. // New index selects created by pass, so we can try simplifying later
  677. ValueSetVector NewSelects;
  678. // Values that have been replaced with other values need remapping
  679. ValueToValueMap RemappedValues;
  680. // Things to clean up if no users:
  681. std::unordered_set<Instruction*> CleanupInsts;
  682. GlobalVariable *LookupResourceGV(Value *V) {
  683. auto itGV = ValueToResourceGV.find(V);
  684. if (itGV == ValueToResourceGV.end())
  685. return nullptr;
  686. return cast<GlobalVariable>(itGV->second);
  687. }
  688. // Follow RemappedValues, return input if not remapped
  689. Value *LookupValue(Value *V) {
  690. auto it = RemappedValues.find(V);
  691. SmallPtrSet<Value*, 4> visited;
  692. while (it != RemappedValues.end()) {
  693. // Cycles should not happen, but are bad if they do.
  694. if (visited.count(it->second)) {
  695. DXASSERT(false, "otherwise, circular remapping");
  696. m_Errors.ReportError(ResourceUseErrors::RemappingCyclesDetected, V);
  697. break;
  698. }
  699. V = it->second;
  700. it = RemappedValues.find(V);
  701. if (it != RemappedValues.end())
  702. visited.insert(V);
  703. }
  704. return V;
  705. }
  706. bool AreLoadUsersTrivial(LoadInst *LI) {
  707. for (auto U : LI->users()) {
  708. if (CallInst *CI = dyn_cast<CallInst>(U)) {
  709. Function *F = CI->getCalledFunction();
  710. DxilModule &DM = F->getParent()->GetDxilModule();
  711. hlsl::OP *hlslOP = DM.GetOP();
  712. if (hlslOP->IsDxilOpFunc(F)) {
  713. hlsl::OP::OpCodeClass opClass;
  714. if (hlslOP->GetOpCodeClass(F, opClass) &&
  715. opClass == DXIL::OpCodeClass::CreateHandleForLib) {
  716. continue;
  717. }
  718. }
  719. }
  720. return false;
  721. }
  722. return true;
  723. }
  724. // This is used to quickly skip the common case where no work is needed
  725. bool AreGEPUsersTrivial(GEPOperator *GEP) {
  726. if (GlobalVariable *GV = LookupResourceGV(GEP)) {
  727. if (GEP->getPointerOperand() != LookupResourceGV(GEP))
  728. return false;
  729. }
  730. for (auto U : GEP->users()) {
  731. if (LoadInst *LI = dyn_cast<LoadInst>(U)) {
  732. if (AreLoadUsersTrivial(LI))
  733. continue;
  734. }
  735. return false;
  736. }
  737. return true;
  738. }
  739. // AssignResourceGVFromStore is used on pointer being stored to.
  740. // Follow GEP/Phi/Select up to Alloca, then CollectResourceGVUsers on Alloca
  741. void AssignResourceGVFromStore(GlobalVariable *GV, Value *V,
  742. SmallPtrSet<Value*, 4> &visited,
  743. bool bNonUniform) {
  744. // Prevent cycles as we search up
  745. if (visited.count(V) != 0)
  746. return;
  747. // Verify and skip if already processed
  748. auto it = ValueToResourceGV.find(V);
  749. if (it != ValueToResourceGV.end()) {
  750. if (it->second != GV) {
  751. m_Errors.ReportError(ResourceUseErrors::GVConflicts, V);
  752. }
  753. return;
  754. }
  755. if (AllocaInst *AI = dyn_cast<AllocaInst>(V)) {
  756. CollectResourceGVUsers(GV, AI, /*bAlloca*/true, bNonUniform);
  757. return;
  758. } else if (GEPOperator *GEP = dyn_cast<GEPOperator>(V)) {
  759. // follow the pointer up
  760. AssignResourceGVFromStore(GV, GEP->getPointerOperand(), visited, bNonUniform);
  761. return;
  762. } else if (PHINode *Phi = dyn_cast<PHINode>(V)) {
  763. #ifdef SUPPORT_SELECT_ON_ALLOCA
  764. // follow all incoming values
  765. for (auto it : Phi->operand_values())
  766. AssignResourceGVFromStore(GV, it, visited, bNonUniform);
  767. #else
  768. m_Errors.ReportError(ResourceUseErrors::AllocaUserDisallowed, V);
  769. #endif
  770. return;
  771. } else if (SelectInst *Sel = dyn_cast<SelectInst>(V)) {
  772. #ifdef SUPPORT_SELECT_ON_ALLOCA
  773. // follow all incoming values
  774. AssignResourceGVFromStore(GV, Sel->getTrueValue(), visited, bNonUniform);
  775. AssignResourceGVFromStore(GV, Sel->getFalseValue(), visited, bNonUniform);
  776. #else
  777. m_Errors.ReportError(ResourceUseErrors::AllocaUserDisallowed, V);
  778. #endif
  779. return;
  780. } else if (isa<GlobalVariable>(V) &&
  781. cast<GlobalVariable>(V)->getLinkage() ==
  782. GlobalVariable::LinkageTypes::InternalLinkage) {
  783. // this is writing to global static, which is disallowed at this point.
  784. m_Errors.ReportError(ResourceUseErrors::StaticGVUsed, V);
  785. return;
  786. } else {
  787. // Most likely storing to output parameter
  788. m_Errors.ReportError(ResourceUseErrors::UserCallsWithResources, V);
  789. return;
  790. }
  791. return;
  792. }
  793. // Recursively mark values with GV, following users.
  794. // Starting value V should be GV itself.
  795. // Returns true if value/uses reference no other GV in map.
  796. void CollectResourceGVUsers(GlobalVariable *GV, Value *V, bool bAlloca = false, bool bNonUniform = false) {
  797. // Recursively tag value V and its users as using GV.
  798. auto it = ValueToResourceGV.find(V);
  799. if (it != ValueToResourceGV.end()) {
  800. if (it->second != GV) {
  801. m_Errors.ReportError(ResourceUseErrors::GVConflicts, V);
  802. #ifdef SUPPORT_SELECT_ON_ALLOCA
  803. } else {
  804. // if select/phi, make sure bAlloca is consistent
  805. if (isa<PHINode>(V) || isa<SelectInst>(V))
  806. if ((bAlloca && AllocaSelects.count(V) == 0) ||
  807. (!bAlloca && Selects.count(V) == 0))
  808. m_Errors.ReportError(ResourceUseErrors::AllocaSelectConflict, V);
  809. #endif
  810. }
  811. return;
  812. }
  813. ValueToResourceGV[V] = GV;
  814. if (GV == V) {
  815. // Just add and recurse users
  816. // make sure bAlloca is clear for users
  817. bAlloca = false;
  818. } else if (GEPOperator *GEP = dyn_cast<GEPOperator>(V)) {
  819. if (bAlloca)
  820. AllocaGEPs.insert(GEP);
  821. else if (!AreGEPUsersTrivial(GEP))
  822. GEPs.insert(GEP);
  823. else
  824. return; // Optimization: skip trivial GV->GEP->load->createHandle
  825. if (GetElementPtrInst *GEPInst = dyn_cast<GetElementPtrInst>(GEP)) {
  826. if (DxilMDHelper::IsMarkedNonUniform(GEPInst))
  827. bNonUniform = true;
  828. }
  829. } else if (LoadInst *LI = dyn_cast<LoadInst>(V)) {
  830. if (bAlloca)
  831. AllocaLoads.insert(LI);
  832. // clear bAlloca for users
  833. bAlloca = false;
  834. if (bNonUniform)
  835. NonUniformSet.insert(LI);
  836. } else if (StoreInst *SI = dyn_cast<StoreInst>(V)) {
  837. Stores.insert(SI);
  838. if (!bAlloca) {
  839. // Find and mark allocas this store could be storing to
  840. SmallPtrSet<Value*, 4> visited;
  841. AssignResourceGVFromStore(GV, SI->getPointerOperand(), visited, bNonUniform);
  842. }
  843. return;
  844. } else if (PHINode *Phi = dyn_cast<PHINode>(V)) {
  845. if (bAlloca) {
  846. #ifdef SUPPORT_SELECT_ON_ALLOCA
  847. AllocaSelects.insert(Phi);
  848. #else
  849. m_Errors.ReportError(ResourceUseErrors::AllocaUserDisallowed, V);
  850. #endif
  851. } else {
  852. Selects.insert(Phi);
  853. }
  854. } else if (SelectInst *Sel = dyn_cast<SelectInst>(V)) {
  855. if (bAlloca) {
  856. #ifdef SUPPORT_SELECT_ON_ALLOCA
  857. AllocaSelects.insert(Sel);
  858. #else
  859. m_Errors.ReportError(ResourceUseErrors::AllocaUserDisallowed, V);
  860. #endif
  861. } else {
  862. Selects.insert(Sel);
  863. }
  864. } else if (AllocaInst *AI = dyn_cast<AllocaInst>(V)) {
  865. Allocas.insert(AI);
  866. // set bAlloca for users
  867. bAlloca = true;
  868. } else if (Constant *C = dyn_cast<Constant>(V)) {
  869. // skip @llvm.used entry
  870. return;
  871. } else if (bAlloca) {
  872. m_Errors.ReportError(ResourceUseErrors::AllocaUserDisallowed, V);
  873. } else {
  874. // Must be createHandleForLib or user function call.
  875. CallInst *CI = cast<CallInst>(V);
  876. Function *F = CI->getCalledFunction();
  877. DxilModule &DM = GV->getParent()->GetDxilModule();
  878. hlsl::OP *hlslOP = DM.GetOP();
  879. if (hlslOP->IsDxilOpFunc(F)) {
  880. hlsl::OP::OpCodeClass opClass;
  881. if (hlslOP->GetOpCodeClass(F, opClass) &&
  882. opClass == DXIL::OpCodeClass::CreateHandleForLib) {
  883. Handles.insert(CI);
  884. if (bNonUniform)
  885. NonUniformSet.insert(CI);
  886. return;
  887. }
  888. }
  889. // This could be user call with resource param, which is disallowed for lib_6_3
  890. m_Errors.ReportError(ResourceUseErrors::UserCallsWithResources, V);
  891. return;
  892. }
  893. // Recurse users
  894. for (auto U : V->users())
  895. CollectResourceGVUsers(GV, U, bAlloca, bNonUniform);
  896. return;
  897. }
  898. // Remove conflicting values from sets before
  899. // transforming the remainder.
  900. void RemoveConflictingValue(Value* V) {
  901. bool bRemoved = false;
  902. if (isa<GEPOperator>(V)) {
  903. bRemoved = GEPs.remove(V) || AllocaGEPs.remove(V);
  904. } else if (isa<LoadInst>(V)) {
  905. bRemoved = AllocaLoads.remove(V);
  906. } else if (isa<StoreInst>(V)) {
  907. bRemoved = Stores.remove(V);
  908. } else if (isa<PHINode>(V) || isa<SelectInst>(V)) {
  909. bRemoved = Selects.remove(V);
  910. #ifdef SUPPORT_SELECT_ON_ALLOCA
  911. bRemoved |= AllocaSelects.remove(V);
  912. #endif
  913. } else if (isa<AllocaInst>(V)) {
  914. bRemoved = Allocas.remove(V);
  915. } else if (isa<CallInst>(V)) {
  916. bRemoved = Handles.remove(V);
  917. return; // don't recurse
  918. }
  919. if (bRemoved) {
  920. // Recurse users
  921. for (auto U : V->users())
  922. RemoveConflictingValue(U);
  923. }
  924. }
  925. void RemoveConflicts() {
  926. for (auto V : m_Errors.ErrorSets[ResourceUseErrors::GVConflicts]) {
  927. RemoveConflictingValue(V);
  928. ValueToResourceGV.erase(V);
  929. }
  930. }
  931. void CreateSelects() {
  932. if (Selects.empty()
  933. #ifdef SUPPORT_SELECT_ON_ALLOCA
  934. && AllocaSelects.empty()
  935. #endif
  936. )
  937. return;
  938. LLVMContext &Ctx =
  939. #ifdef SUPPORT_SELECT_ON_ALLOCA
  940. Selects.empty() ? AllocaSelects[0]->getContext() :
  941. #endif
  942. Selects[0]->getContext();
  943. Type *i32Ty = IntegerType::getInt32Ty(Ctx);
  944. #ifdef SUPPORT_SELECT_ON_ALLOCA
  945. for (auto &SelectSet : {Selects, AllocaSelects}) {
  946. bool bAlloca = !(&SelectSet == &Selects);
  947. #else
  948. for (auto &SelectSet : { Selects }) {
  949. #endif
  950. for (auto pValue : SelectSet) {
  951. Type *SelectTy = i32Ty;
  952. #ifdef SUPPORT_SELECT_ON_ALLOCA
  953. // For alloca case, type needs to match dimensionality of incoming value
  954. if (bAlloca) {
  955. // TODO: Not sure if this case will actually work
  956. // (or whether it can even be generated from HLSL)
  957. Type *Ty = pValue->getType();
  958. SmallVector<unsigned, 4> dims;
  959. unsigned dim = CountArrayDimensions(Ty, &dims);
  960. for (unsigned i = 0; i < dim; i++)
  961. SelectTy = ArrayType::get(SelectTy, (uint64_t)dims[dim - i - 1]);
  962. if (Ty->isPointerTy())
  963. SelectTy = PointerType::get(SelectTy, 0);
  964. }
  965. #endif
  966. Value *UndefValue = UndefValue::get(SelectTy);
  967. if (PHINode *Phi = dyn_cast<PHINode>(pValue)) {
  968. GlobalVariable *GV = LookupResourceGV(Phi);
  969. if (!GV)
  970. continue; // skip value removed due to conflict
  971. IRBuilder<> PhiBuilder(Phi);
  972. unsigned gvDim = CountArrayDimensions(GV->getType());
  973. IndexVector &idxVector = ResToIdxReplacement[Phi];
  974. idxVector.resize(gvDim, nullptr);
  975. unsigned numIncoming = Phi->getNumIncomingValues();
  976. for (unsigned i = 0; i < gvDim; i++) {
  977. PHINode *newPhi = PhiBuilder.CreatePHI(SelectTy, numIncoming);
  978. NewSelects.insert(newPhi);
  979. idxVector[i] = newPhi;
  980. for (unsigned j = 0; j < numIncoming; j++) {
  981. // Set incoming values to undef until next pass
  982. newPhi->addIncoming(UndefValue, Phi->getIncomingBlock(j));
  983. }
  984. }
  985. } else if (SelectInst *Sel = dyn_cast<SelectInst>(pValue)) {
  986. GlobalVariable *GV = LookupResourceGV(Sel);
  987. if (!GV)
  988. continue; // skip value removed due to conflict
  989. IRBuilder<> Builder(Sel);
  990. unsigned gvDim = CountArrayDimensions(GV->getType());
  991. IndexVector &idxVector = ResToIdxReplacement[Sel];
  992. idxVector.resize(gvDim, nullptr);
  993. for (unsigned i = 0; i < gvDim; i++) {
  994. Value *newSel = Builder.CreateSelect(Sel->getCondition(), UndefValue, UndefValue);
  995. NewSelects.insert(newSel);
  996. idxVector[i] = newSel;
  997. }
  998. } else {
  999. DXASSERT(false, "otherwise, non-select/phi in Selects set");
  1000. }
  1001. }
  1002. }
  1003. }
  1004. // Create index allocas to replace resource allocas
  1005. void CreateIndexAllocas() {
  1006. if (Allocas.empty())
  1007. return;
  1008. Type *i32Ty = IntegerType::getInt32Ty(Allocas[0]->getContext());
  1009. for (auto pValue : Allocas) {
  1010. AllocaInst *pAlloca = cast<AllocaInst>(pValue);
  1011. GlobalVariable *GV = LookupResourceGV(pAlloca);
  1012. if (!GV)
  1013. continue; // skip value removed due to conflict
  1014. IRBuilder<> AllocaBuilder(pAlloca);
  1015. unsigned gvDim = CountArrayDimensions(GV->getType());
  1016. SmallVector<unsigned, 4> dimVector;
  1017. unsigned allocaTyDim = CountArrayDimensions(pAlloca->getType(), &dimVector);
  1018. Type *pIndexType = i32Ty;
  1019. for (unsigned i = 0; i < allocaTyDim; i++) {
  1020. pIndexType = ArrayType::get(pIndexType, dimVector[allocaTyDim - i - 1]);
  1021. }
  1022. Value *arraySize = pAlloca->getArraySize();
  1023. IndexVector &idxVector = ResToIdxReplacement[pAlloca];
  1024. idxVector.resize(gvDim, nullptr);
  1025. for (unsigned i = 0; i < gvDim; i++) {
  1026. AllocaInst *pAlloca = AllocaBuilder.CreateAlloca(pIndexType, arraySize);
  1027. pAlloca->setAlignment(4);
  1028. idxVector[i] = pAlloca;
  1029. }
  1030. }
  1031. }
  1032. // Add corresponding GEPs for index allocas
  1033. IndexVector &ReplaceAllocaGEP(GetElementPtrInst *GEP) {
  1034. IndexVector &idxVector = ResToIdxReplacement[GEP];
  1035. if (!idxVector.empty())
  1036. return idxVector;
  1037. Value *Ptr = GEP->getPointerOperand();
  1038. // Recurse for partial GEPs
  1039. IndexVector &ptrIndices = isa<GetElementPtrInst>(Ptr) ?
  1040. ReplaceAllocaGEP(cast<GetElementPtrInst>(Ptr)) : ResToIdxReplacement[Ptr];
  1041. IRBuilder<> Builder(GEP);
  1042. SmallVector<Value*, 4> gepIndices;
  1043. for (auto it = GEP->idx_begin(), idxEnd = GEP->idx_end(); it != idxEnd; it++)
  1044. gepIndices.push_back(*it);
  1045. idxVector.resize(ptrIndices.size(), nullptr);
  1046. for (unsigned i = 0; i < ptrIndices.size(); i++) {
  1047. idxVector[i] = Builder.CreateInBoundsGEP(ptrIndices[i], gepIndices);
  1048. }
  1049. return idxVector;
  1050. }
  1051. void ReplaceAllocaGEPs() {
  1052. for (auto V : AllocaGEPs) {
  1053. ReplaceAllocaGEP(cast<GetElementPtrInst>(V));
  1054. }
  1055. }
  1056. void ReplaceAllocaLoads() {
  1057. for (auto V : AllocaLoads) {
  1058. LoadInst *LI = cast<LoadInst>(V);
  1059. Value *Ptr = LI->getPointerOperand();
  1060. IRBuilder<> Builder(LI);
  1061. IndexVector &idxVector = ResToIdxReplacement[V];
  1062. IndexVector &ptrIndices = ResToIdxReplacement[Ptr];
  1063. idxVector.resize(ptrIndices.size(), nullptr);
  1064. for (unsigned i = 0; i < ptrIndices.size(); i++) {
  1065. idxVector[i] = Builder.CreateLoad(ptrIndices[i]);
  1066. }
  1067. }
  1068. }
  1069. // Add GEP to ResToIdxReplacement with indices from incoming + GEP
  1070. IndexVector &ReplaceGVGEPs(GEPOperator *GEP) {
  1071. IndexVector &idxVector = ResToIdxReplacement[GEP];
  1072. // Skip if already done
  1073. // (we recurse into partial GEP and iterate all GEPs)
  1074. if (!idxVector.empty())
  1075. return idxVector;
  1076. Type *i32Ty = IntegerType::getInt32Ty(GEP->getContext());
  1077. Constant *Zero = Constant::getIntegerValue(i32Ty, APInt(32, 0));
  1078. Value *Ptr = GEP->getPointerOperand();
  1079. unsigned idx = 0;
  1080. if (GlobalVariable *GV = dyn_cast<GlobalVariable>(Ptr)) {
  1081. unsigned gvDim = CountArrayDimensions(GV->getType());
  1082. idxVector.resize(gvDim, Zero);
  1083. } else if (isa<GEPOperator>(Ptr) || isa<PHINode>(Ptr) || isa<SelectInst>(Ptr)) {
  1084. // Recurse for partial GEPs
  1085. IndexVector &ptrIndices = isa<GEPOperator>(Ptr) ?
  1086. ReplaceGVGEPs(cast<GEPOperator>(Ptr)) : ResToIdxReplacement[Ptr];
  1087. unsigned ptrDim = CountArrayDimensions(Ptr->getType());
  1088. unsigned gvDim = ptrIndices.size();
  1089. DXASSERT(ptrDim <= gvDim, "otherwise incoming pointer has more dimensions than associated GV");
  1090. unsigned gepStart = gvDim - ptrDim;
  1091. // Copy indices and add ours
  1092. idxVector.resize(ptrIndices.size(), Zero);
  1093. for (; idx < gepStart; idx++)
  1094. idxVector[idx] = ptrIndices[idx];
  1095. }
  1096. if (GEP->hasIndices()) {
  1097. auto itIdx = GEP->idx_begin();
  1098. ++itIdx; // Always skip leading zero (we don't support GV+n pointer arith)
  1099. while (itIdx != GEP->idx_end())
  1100. idxVector[idx++] = *itIdx++;
  1101. }
  1102. return idxVector;
  1103. }
  1104. // Add GEPs to ResToIdxReplacement and update loads
  1105. void ReplaceGVGEPs() {
  1106. if (GEPs.empty())
  1107. return;
  1108. for (auto V : GEPs) {
  1109. GEPOperator *GEP = cast<GEPOperator>(V);
  1110. IndexVector &gepVector = ReplaceGVGEPs(GEP);
  1111. for (auto U : GEP->users()) {
  1112. if (LoadInst *LI = dyn_cast<LoadInst>(U)) {
  1113. // Just copy incoming indices
  1114. ResToIdxReplacement[LI] = gepVector;
  1115. }
  1116. }
  1117. }
  1118. }
  1119. // Create new index stores for incoming indices
  1120. void ReplaceStores() {
  1121. // generate stores of incoming indices to corresponding index pointers
  1122. if (Stores.empty())
  1123. return;
  1124. for (auto V : Stores) {
  1125. StoreInst *SI = cast<StoreInst>(V);
  1126. IRBuilder<> Builder(SI);
  1127. IndexVector &idxVector = ResToIdxReplacement[SI];
  1128. Value *Ptr = SI->getPointerOperand();
  1129. Value *Val = SI->getValueOperand();
  1130. IndexVector &ptrIndices = ResToIdxReplacement[Ptr];
  1131. IndexVector &valIndices = ResToIdxReplacement[Val];
  1132. DXASSERT_NOMSG(ptrIndices.size() == valIndices.size());
  1133. idxVector.resize(ptrIndices.size(), nullptr);
  1134. for (unsigned i = 0; i < idxVector.size(); i++) {
  1135. idxVector[i] = Builder.CreateStore(valIndices[i], ptrIndices[i]);
  1136. }
  1137. }
  1138. }
  1139. // For each Phi/Select: update matching incoming values for new phis
  1140. void UpdateSelects() {
  1141. for (auto V : Selects) {
  1142. // update incoming index values corresponding to incoming resource values
  1143. IndexVector &idxVector = ResToIdxReplacement[V];
  1144. Instruction *I = cast<Instruction>(V);
  1145. unsigned numOperands = I->getNumOperands();
  1146. unsigned startOp = isa<PHINode>(V) ? 0 : 1;
  1147. for (unsigned iOp = startOp; iOp < numOperands; iOp++) {
  1148. IndexVector &incomingIndices = ResToIdxReplacement[I->getOperand(iOp)];
  1149. DXASSERT_NOMSG(idxVector.size() == incomingIndices.size());
  1150. for (unsigned i = 0; i < idxVector.size(); i++) {
  1151. // must be instruction (phi/select)
  1152. Instruction *indexI = cast<Instruction>(idxVector[i]);
  1153. indexI->setOperand(iOp, incomingIndices[i]);
  1154. }
  1155. // Now clear incoming operand (adding to cleanup) to break cycles
  1156. if (Instruction *OpI = dyn_cast<Instruction>(I->getOperand(iOp)))
  1157. CleanupInsts.insert(OpI);
  1158. I->setOperand(iOp, UndefValue::get(I->getType()));
  1159. }
  1160. }
  1161. }
  1162. // ReplaceHandles
  1163. // - iterate handles
  1164. // - insert GEP using new indices associated with resource value
  1165. // - load resource from new GEP
  1166. // - replace resource use in createHandleForLib with new load
  1167. // Assumes: no users of handle are phi/select or store
  1168. void ReplaceHandles() {
  1169. if (Handles.empty())
  1170. return;
  1171. Type *i32Ty = IntegerType::getInt32Ty(Handles[0]->getContext());
  1172. Constant *Zero = Constant::getIntegerValue(i32Ty, APInt(32, 0));
  1173. for (auto V : Handles) {
  1174. CallInst *CI = cast<CallInst>(V);
  1175. DxilInst_CreateHandleForLib createHandle(CI);
  1176. Value *res = createHandle.get_Resource();
  1177. // Skip extra work if nothing between load and create handle
  1178. if (LoadInst *LI = dyn_cast<LoadInst>(res)) {
  1179. Value *Ptr = LI->getPointerOperand();
  1180. if (GEPOperator *GEP = dyn_cast<GEPOperator>(Ptr))
  1181. Ptr = GEP->getPointerOperand();
  1182. if (isa<GlobalVariable>(Ptr))
  1183. continue;
  1184. }
  1185. GlobalVariable *GV = LookupResourceGV(res);
  1186. if (!GV)
  1187. continue; // skip value removed due to conflict
  1188. IRBuilder<> Builder(CI);
  1189. IndexVector &idxVector = ResToIdxReplacement[res];
  1190. DXASSERT(idxVector.size() == CountArrayDimensions(GV->getType()), "replacements empty or invalid");
  1191. SmallVector<Value*, 4> gepIndices;
  1192. gepIndices.push_back(Zero);
  1193. for (auto idxVal : idxVector)
  1194. gepIndices.push_back(LookupValue(idxVal));
  1195. Value *GEP = Builder.CreateInBoundsGEP(GV, gepIndices);
  1196. // Mark new GEP instruction non-uniform if necessary
  1197. if (NonUniformSet.count(res) != 0 || NonUniformSet.count(CI) != 0)
  1198. if (GetElementPtrInst *GEPInst = dyn_cast<GetElementPtrInst>(GEP))
  1199. DxilMDHelper::MarkNonUniform(GEPInst);
  1200. LoadInst *LI = Builder.CreateLoad(GEP);
  1201. createHandle.set_Resource(LI);
  1202. if (Instruction *resI = dyn_cast<Instruction>(res))
  1203. CleanupInsts.insert(resI);
  1204. }
  1205. }
  1206. // Delete unused CleanupInsts, restarting when changed
  1207. // Return true if something was deleted
  1208. bool CleanupUnusedValues() {
  1209. // - delete unused CleanupInsts, restarting when changed
  1210. bool bAnyChanges = false;
  1211. bool bChanged = false;
  1212. do {
  1213. bChanged = false;
  1214. for (auto it = CleanupInsts.begin(); it != CleanupInsts.end();) {
  1215. Instruction *I = *(it++);
  1216. if (I->user_empty()) {
  1217. // Add instructions operands CleanupInsts
  1218. for (unsigned iOp = 0; iOp < I->getNumOperands(); iOp++) {
  1219. if (Instruction *opI = dyn_cast<Instruction>(I->getOperand(iOp)))
  1220. CleanupInsts.insert(opI);
  1221. }
  1222. I->eraseFromParent();
  1223. CleanupInsts.erase(I);
  1224. bChanged = true;
  1225. }
  1226. }
  1227. if (bChanged)
  1228. bAnyChanges = true;
  1229. } while (bChanged);
  1230. return bAnyChanges;
  1231. }
  1232. void SimplifyMerges() {
  1233. // Loop if changed
  1234. bool bChanged = false;
  1235. do {
  1236. bChanged = false;
  1237. for (auto V : NewSelects) {
  1238. if (LookupValue(V) != V)
  1239. continue;
  1240. Instruction *I = cast<Instruction>(V);
  1241. unsigned startOp = isa<PHINode>(I) ? 0 : 1;
  1242. Value *newV = dxilutil::MergeSelectOnSameValue(
  1243. cast<Instruction>(V), startOp, I->getNumOperands());
  1244. if (newV) {
  1245. RemappedValues[V] = newV;
  1246. bChanged = true;
  1247. }
  1248. }
  1249. } while (bChanged);
  1250. }
  1251. void CleanupDeadInsts() {
  1252. // Assuming everything was successful:
  1253. // delete stores to allocas to remove cycles
  1254. for (auto V : Stores) {
  1255. StoreInst *SI = cast<StoreInst>(V);
  1256. if (Instruction *I = dyn_cast<Instruction>(SI->getValueOperand()))
  1257. CleanupInsts.insert(I);
  1258. if (Instruction *I = dyn_cast<Instruction>(SI->getPointerOperand()))
  1259. CleanupInsts.insert(I);
  1260. SI->eraseFromParent();
  1261. }
  1262. CleanupUnusedValues();
  1263. }
  1264. void VerifyComplete(DxilModule &DM) {
  1265. // Check that all handles now resolve to a global variable, otherwise,
  1266. // they are likely loading from resource function parameter, which
  1267. // is disallowed.
  1268. hlsl::OP *hlslOP = DM.GetOP();
  1269. for (Function &F : DM.GetModule()->functions()) {
  1270. if (hlslOP->IsDxilOpFunc(&F)) {
  1271. hlsl::OP::OpCodeClass opClass;
  1272. if (hlslOP->GetOpCodeClass(&F, opClass) &&
  1273. opClass == DXIL::OpCodeClass::CreateHandleForLib) {
  1274. for (auto U : F.users()) {
  1275. CallInst *CI = cast<CallInst>(U);
  1276. if (m_Errors.ErrorUsers.count(CI))
  1277. continue; // Error already reported
  1278. DxilInst_CreateHandleForLib createHandle(CI);
  1279. Value *res = createHandle.get_Resource();
  1280. LoadInst *LI = dyn_cast<LoadInst>(res);
  1281. if (LI) {
  1282. Value *Ptr = LI->getPointerOperand();
  1283. if (GEPOperator *GEP = dyn_cast<GEPOperator>(Ptr))
  1284. Ptr = GEP->getPointerOperand();
  1285. if (isa<GlobalVariable>(Ptr))
  1286. continue;
  1287. }
  1288. // handle wasn't processed
  1289. // Right now, the most likely cause is user call with resources, but
  1290. // this should be updated if there are other reasons for this to happen.
  1291. m_Errors.ReportError(ResourceUseErrors::UserCallsWithResources, U);
  1292. }
  1293. }
  1294. }
  1295. }
  1296. }
  1297. // Fix resource global variable properties to external constant
  1298. bool SetExternalConstant(GlobalVariable *GV) {
  1299. if (GV->hasInitializer() || !GV->isConstant() ||
  1300. GV->getLinkage() != GlobalVariable::LinkageTypes::ExternalLinkage) {
  1301. GV->setInitializer(nullptr);
  1302. GV->setConstant(true);
  1303. GV->setLinkage(GlobalVariable::LinkageTypes::ExternalLinkage);
  1304. return true;
  1305. }
  1306. return false;
  1307. }
  1308. bool CollectResources(DxilModule &DM) {
  1309. bool bChanged = false;
  1310. for (const auto &res : DM.GetCBuffers()) {
  1311. if (GlobalVariable *GV = dyn_cast<GlobalVariable>(res->GetGlobalSymbol())) {
  1312. bChanged |= SetExternalConstant(GV);
  1313. CollectResourceGVUsers(GV, GV);
  1314. }
  1315. }
  1316. for (const auto &res : DM.GetSRVs()) {
  1317. if (GlobalVariable *GV = dyn_cast<GlobalVariable>(res->GetGlobalSymbol())) {
  1318. bChanged |= SetExternalConstant(GV);
  1319. CollectResourceGVUsers(GV, GV);
  1320. }
  1321. }
  1322. for (const auto &res : DM.GetUAVs()) {
  1323. if (GlobalVariable *GV = dyn_cast<GlobalVariable>(res->GetGlobalSymbol())) {
  1324. bChanged |= SetExternalConstant(GV);
  1325. CollectResourceGVUsers(GV, GV);
  1326. }
  1327. }
  1328. for (const auto &res : DM.GetSamplers()) {
  1329. if (GlobalVariable *GV = dyn_cast<GlobalVariable>(res->GetGlobalSymbol())) {
  1330. bChanged |= SetExternalConstant(GV);
  1331. CollectResourceGVUsers(GV, GV);
  1332. }
  1333. }
  1334. return bChanged;
  1335. }
  1336. void DoTransform() {
  1337. RemoveConflicts();
  1338. CreateSelects();
  1339. CreateIndexAllocas();
  1340. ReplaceAllocaGEPs();
  1341. ReplaceAllocaLoads();
  1342. ReplaceGVGEPs();
  1343. ReplaceStores();
  1344. UpdateSelects();
  1345. SimplifyMerges();
  1346. ReplaceHandles();
  1347. if (!m_Errors.ErrorsReported())
  1348. CleanupDeadInsts();
  1349. }
  1350. bool ErrorsReported() {
  1351. return m_Errors.ErrorsReported();
  1352. }
  1353. bool runOnModule(llvm::Module &M) {
  1354. DxilModule &DM = M.GetOrCreateDxilModule();
  1355. bool bChanged = CollectResources(DM);
  1356. // If no selects or allocas are involved, there isn't anything to do
  1357. if (Selects.empty() && Allocas.empty())
  1358. return bChanged;
  1359. DoTransform();
  1360. VerifyComplete(DM);
  1361. return true;
  1362. }
  1363. };
  1364. class DxilLegalizeResources : public ModulePass {
  1365. public:
  1366. static char ID; // Pass identification, replacement for typeid
  1367. explicit DxilLegalizeResources()
  1368. : ModulePass(ID) {}
  1369. const char *getPassName() const override {
  1370. return "DXIL Legalize Resource Use";
  1371. }
  1372. bool runOnModule(Module &M) override {
  1373. LegalizeResourceUseHelper helper;
  1374. return helper.runOnModule(M);
  1375. }
  1376. private:
  1377. };
  1378. } // namespace
  1379. char DxilLegalizeResources::ID = 0;
  1380. ModulePass *llvm::createDxilLegalizeResources() {
  1381. return new DxilLegalizeResources();
  1382. }
  1383. INITIALIZE_PASS(DxilLegalizeResources,
  1384. "hlsl-dxil-legalize-resources",
  1385. "DXIL legalize resource use", false, false)
  1386. bool DxilLowerCreateHandleForLib::RemovePhiOnResource() {
  1387. LegalizeResourceUseHelper helper;
  1388. bool bChanged = helper.runOnModule(*m_DM->GetModule());
  1389. if (helper.ErrorsReported())
  1390. m_bLegalizationFailed = true;
  1391. return bChanged;
  1392. }
  1393. // LegacyLayout.
  1394. namespace {
  1395. StructType *UpdateStructTypeForLegacyLayout(StructType *ST, bool IsCBuf,
  1396. DxilTypeSystem &TypeSys, Module &M);
  1397. Type *UpdateFieldTypeForLegacyLayout(Type *Ty, bool IsCBuf,
  1398. DxilFieldAnnotation &annotation,
  1399. DxilTypeSystem &TypeSys, Module &M) {
  1400. DXASSERT(!Ty->isPointerTy(), "struct field should not be a pointer");
  1401. if (Ty->isArrayTy()) {
  1402. Type *EltTy = Ty->getArrayElementType();
  1403. Type *UpdatedTy =
  1404. UpdateFieldTypeForLegacyLayout(EltTy, IsCBuf, annotation, TypeSys, M);
  1405. if (EltTy == UpdatedTy)
  1406. return Ty;
  1407. else
  1408. return ArrayType::get(UpdatedTy, Ty->getArrayNumElements());
  1409. } else if (HLMatrixLower::IsMatrixType(Ty)) {
  1410. DXASSERT(annotation.HasMatrixAnnotation(), "must a matrix");
  1411. unsigned rows, cols;
  1412. Type *EltTy = HLMatrixLower::GetMatrixInfo(Ty, cols, rows);
  1413. // Get cols and rows from annotation.
  1414. const DxilMatrixAnnotation &matrix = annotation.GetMatrixAnnotation();
  1415. if (matrix.Orientation == MatrixOrientation::RowMajor) {
  1416. rows = matrix.Rows;
  1417. cols = matrix.Cols;
  1418. } else {
  1419. DXASSERT(matrix.Orientation == MatrixOrientation::ColumnMajor, "");
  1420. cols = matrix.Rows;
  1421. rows = matrix.Cols;
  1422. }
  1423. // CBuffer matrix must 4 * 4 bytes align.
  1424. if (IsCBuf)
  1425. cols = 4;
  1426. EltTy =
  1427. UpdateFieldTypeForLegacyLayout(EltTy, IsCBuf, annotation, TypeSys, M);
  1428. Type *rowTy = VectorType::get(EltTy, cols);
  1429. return ArrayType::get(rowTy, rows);
  1430. } else if (StructType *ST = dyn_cast<StructType>(Ty)) {
  1431. return UpdateStructTypeForLegacyLayout(ST, IsCBuf, TypeSys, M);
  1432. } else if (Ty->isVectorTy()) {
  1433. Type *EltTy = Ty->getVectorElementType();
  1434. Type *UpdatedTy =
  1435. UpdateFieldTypeForLegacyLayout(EltTy, IsCBuf, annotation, TypeSys, M);
  1436. if (EltTy == UpdatedTy)
  1437. return Ty;
  1438. else
  1439. return VectorType::get(UpdatedTy, Ty->getVectorNumElements());
  1440. } else {
  1441. Type *i32Ty = Type::getInt32Ty(Ty->getContext());
  1442. // Basic types.
  1443. if (Ty->isHalfTy()) {
  1444. return Type::getFloatTy(Ty->getContext());
  1445. } else if (IntegerType *ITy = dyn_cast<IntegerType>(Ty)) {
  1446. if (ITy->getBitWidth() < 32)
  1447. return i32Ty;
  1448. else
  1449. return Ty;
  1450. } else
  1451. return Ty;
  1452. }
  1453. }
  1454. StructType *UpdateStructTypeForLegacyLayout(StructType *ST, bool IsCBuf,
  1455. DxilTypeSystem &TypeSys,
  1456. Module &M) {
  1457. bool bUpdated = false;
  1458. unsigned fieldsCount = ST->getNumElements();
  1459. std::vector<Type *> fieldTypes(fieldsCount);
  1460. DxilStructAnnotation *SA = TypeSys.GetStructAnnotation(ST);
  1461. DXASSERT(SA, "must have annotation for struct type");
  1462. for (unsigned i = 0; i < fieldsCount; i++) {
  1463. Type *EltTy = ST->getElementType(i);
  1464. Type *UpdatedTy = UpdateFieldTypeForLegacyLayout(
  1465. EltTy, IsCBuf, SA->GetFieldAnnotation(i), TypeSys, M);
  1466. fieldTypes[i] = UpdatedTy;
  1467. if (EltTy != UpdatedTy)
  1468. bUpdated = true;
  1469. }
  1470. if (!bUpdated) {
  1471. return ST;
  1472. } else {
  1473. std::string legacyName = "dx.alignment.legacy." + ST->getName().str();
  1474. if (StructType *legacyST = M.getTypeByName(legacyName))
  1475. return legacyST;
  1476. StructType *NewST =
  1477. StructType::create(ST->getContext(), fieldTypes, legacyName);
  1478. DxilStructAnnotation *NewSA = TypeSys.AddStructAnnotation(NewST);
  1479. // Clone annotation.
  1480. *NewSA = *SA;
  1481. return NewST;
  1482. }
  1483. }
  1484. void UpdateStructTypeForLegacyLayout(DxilResourceBase &Res,
  1485. DxilTypeSystem &TypeSys, Module &M) {
  1486. GlobalVariable *GV = cast<GlobalVariable>(Res.GetGlobalSymbol());
  1487. Type *Ty = GV->getType()->getPointerElementType();
  1488. bool IsResourceArray = Res.GetRangeSize() != 1;
  1489. if (IsResourceArray) {
  1490. // Support Array of struct buffer.
  1491. if (Ty->isArrayTy())
  1492. Ty = Ty->getArrayElementType();
  1493. }
  1494. StructType *ST = cast<StructType>(Ty);
  1495. if (ST->isOpaque()) {
  1496. DXASSERT(Res.GetClass() == DxilResourceBase::Class::CBuffer,
  1497. "Only cbuffer can have opaque struct.");
  1498. return;
  1499. }
  1500. Type *UpdatedST =
  1501. UpdateStructTypeForLegacyLayout(ST, IsResourceArray, TypeSys, M);
  1502. if (ST != UpdatedST) {
  1503. Type *Ty = GV->getType()->getPointerElementType();
  1504. if (IsResourceArray) {
  1505. // Support Array of struct buffer.
  1506. if (Ty->isArrayTy()) {
  1507. UpdatedST = ArrayType::get(UpdatedST, Ty->getArrayNumElements());
  1508. }
  1509. }
  1510. GlobalVariable *NewGV = cast<GlobalVariable>(
  1511. M.getOrInsertGlobal(GV->getName().str() + "_legacy", UpdatedST));
  1512. Res.SetGlobalSymbol(NewGV);
  1513. // Delete old GV.
  1514. for (auto UserIt = GV->user_begin(); UserIt != GV->user_end();) {
  1515. Value *User = *(UserIt++);
  1516. if (Instruction *I = dyn_cast<Instruction>(User)) {
  1517. if (!User->user_empty())
  1518. I->replaceAllUsesWith(UndefValue::get(I->getType()));
  1519. I->eraseFromParent();
  1520. } else {
  1521. ConstantExpr *CE = cast<ConstantExpr>(User);
  1522. if (!CE->user_empty())
  1523. CE->replaceAllUsesWith(UndefValue::get(CE->getType()));
  1524. }
  1525. }
  1526. GV->removeDeadConstantUsers();
  1527. GV->eraseFromParent();
  1528. }
  1529. }
  1530. void UpdateStructTypeForLegacyLayoutOnDM(DxilModule &DM) {
  1531. DxilTypeSystem &TypeSys = DM.GetTypeSystem();
  1532. Module &M = *DM.GetModule();
  1533. for (auto &CBuf : DM.GetCBuffers()) {
  1534. UpdateStructTypeForLegacyLayout(*CBuf.get(), TypeSys, M);
  1535. }
  1536. for (auto &UAV : DM.GetUAVs()) {
  1537. if (UAV->GetKind() == DxilResourceBase::Kind::StructuredBuffer)
  1538. UpdateStructTypeForLegacyLayout(*UAV.get(), TypeSys, M);
  1539. }
  1540. for (auto &SRV : DM.GetSRVs()) {
  1541. if (SRV->GetKind() == DxilResourceBase::Kind::StructuredBuffer)
  1542. UpdateStructTypeForLegacyLayout(*SRV.get(), TypeSys, M);
  1543. }
  1544. }
  1545. } // namespace
  1546. void DxilLowerCreateHandleForLib::UpdateStructTypeForLegacyLayout() {
  1547. UpdateStructTypeForLegacyLayoutOnDM(*m_DM);
  1548. }
  1549. // Change ResourceSymbol to undef if don't need.
  1550. void DxilLowerCreateHandleForLib::UpdateResourceSymbols() {
  1551. std::vector<GlobalVariable *> &LLVMUsed = m_DM->GetLLVMUsed();
  1552. auto UpdateResourceSymbol = [&LLVMUsed, this](DxilResourceBase *res) {
  1553. GlobalVariable *GV = cast<GlobalVariable>(res->GetGlobalSymbol());
  1554. GV->removeDeadConstantUsers();
  1555. DXASSERT(GV->user_empty(), "else resource not lowered");
  1556. Type *Ty = GV->getType();
  1557. res->SetGlobalSymbol(UndefValue::get(Ty));
  1558. if (m_HasDbgInfo)
  1559. LLVMUsed.emplace_back(GV);
  1560. res->SetGlobalSymbol(UndefValue::get(Ty));
  1561. };
  1562. for (auto &&C : m_DM->GetCBuffers()) {
  1563. UpdateResourceSymbol(C.get());
  1564. }
  1565. for (auto &&Srv : m_DM->GetSRVs()) {
  1566. UpdateResourceSymbol(Srv.get());
  1567. }
  1568. for (auto &&Uav : m_DM->GetUAVs()) {
  1569. UpdateResourceSymbol(Uav.get());
  1570. }
  1571. for (auto &&S : m_DM->GetSamplers()) {
  1572. UpdateResourceSymbol(S.get());
  1573. }
  1574. }
  1575. // Lower createHandleForLib
  1576. namespace {
  1577. void ReplaceResourceUserWithHandle(
  1578. LoadInst *Res, Value *handle) {
  1579. for (auto resUser = Res->user_begin(); resUser != Res->user_end();) {
  1580. Value *V = *(resUser++);
  1581. CallInst *CI = dyn_cast<CallInst>(V);
  1582. DxilInst_CreateHandleForLib createHandle(CI);
  1583. DXASSERT(createHandle, "must be createHandle");
  1584. CI->replaceAllUsesWith(handle);
  1585. CI->eraseFromParent();
  1586. }
  1587. Res->eraseFromParent();
  1588. }
  1589. DIGlobalVariable *FindGlobalVariableDebugInfo(GlobalVariable *GV,
  1590. DebugInfoFinder &DbgInfoFinder) {
  1591. struct GlobalFinder {
  1592. GlobalVariable *GV;
  1593. bool operator()(llvm::DIGlobalVariable *const arg) const {
  1594. return arg->getVariable() == GV;
  1595. }
  1596. };
  1597. GlobalFinder F = {GV};
  1598. DebugInfoFinder::global_variable_iterator Found =
  1599. std::find_if(DbgInfoFinder.global_variables().begin(),
  1600. DbgInfoFinder.global_variables().end(), F);
  1601. if (Found != DbgInfoFinder.global_variables().end()) {
  1602. return *Found;
  1603. }
  1604. return nullptr;
  1605. }
  1606. } // namespace
  1607. void DxilLowerCreateHandleForLib::TranslateDxilResourceUses(
  1608. DxilResourceBase &res) {
  1609. OP *hlslOP = m_DM->GetOP();
  1610. Function *createHandle = hlslOP->GetOpFunc(
  1611. OP::OpCode::CreateHandle, llvm::Type::getVoidTy(m_DM->GetCtx()));
  1612. Value *opArg = hlslOP->GetU32Const((unsigned)OP::OpCode::CreateHandle);
  1613. bool isViewResource = res.GetClass() == DXIL::ResourceClass::SRV ||
  1614. res.GetClass() == DXIL::ResourceClass::UAV;
  1615. bool isROV = isViewResource && static_cast<DxilResource &>(res).IsROV();
  1616. std::string handleName =
  1617. (res.GetGlobalName() + Twine("_") + Twine(res.GetResClassName())).str();
  1618. if (isViewResource)
  1619. handleName += (Twine("_") + Twine(res.GetResDimName())).str();
  1620. if (isROV)
  1621. handleName += "_ROV";
  1622. Value *resClassArg = hlslOP->GetU8Const(
  1623. static_cast<std::underlying_type<DxilResourceBase::Class>::type>(
  1624. res.GetClass()));
  1625. Value *resIDArg = hlslOP->GetU32Const(res.GetID());
  1626. // resLowerBound will be added after allocation in DxilCondenseResources.
  1627. Value *resLowerBound = hlslOP->GetU32Const(res.GetLowerBound());
  1628. Value *isUniformRes = hlslOP->GetI1Const(0);
  1629. Value *GV = res.GetGlobalSymbol();
  1630. Module *pM = m_DM->GetModule();
  1631. // TODO: add debug info to create handle.
  1632. DIVariable *DIV = nullptr;
  1633. DILocation *DL = nullptr;
  1634. if (m_HasDbgInfo) {
  1635. DebugInfoFinder &Finder = m_DM->GetOrCreateDebugInfoFinder();
  1636. DIV = FindGlobalVariableDebugInfo(cast<GlobalVariable>(GV), Finder);
  1637. if (DIV)
  1638. // TODO: how to get col?
  1639. DL =
  1640. DILocation::get(pM->getContext(), DIV->getLine(), 1, DIV->getScope());
  1641. }
  1642. bool isResArray = res.GetRangeSize() > 1;
  1643. std::unordered_map<Function *, Instruction *> handleMapOnFunction;
  1644. Value *createHandleArgs[] = {opArg, resClassArg, resIDArg, resLowerBound,
  1645. isUniformRes};
  1646. for (iplist<Function>::iterator F : pM->getFunctionList()) {
  1647. if (!F->isDeclaration()) {
  1648. if (!isResArray) {
  1649. IRBuilder<> Builder(dxilutil::FirstNonAllocaInsertionPt(F));
  1650. if (m_HasDbgInfo) {
  1651. // TODO: set debug info.
  1652. // Builder.SetCurrentDebugLocation(DL);
  1653. }
  1654. handleMapOnFunction[F] =
  1655. Builder.CreateCall(createHandle, createHandleArgs, handleName);
  1656. }
  1657. }
  1658. }
  1659. for (auto U = GV->user_begin(), E = GV->user_end(); U != E;) {
  1660. User *user = *(U++);
  1661. // Skip unused user.
  1662. if (user->user_empty())
  1663. continue;
  1664. if (LoadInst *ldInst = dyn_cast<LoadInst>(user)) {
  1665. Function *userF = ldInst->getParent()->getParent();
  1666. DXASSERT(handleMapOnFunction.count(userF), "must exist");
  1667. Value *handle = handleMapOnFunction[userF];
  1668. ReplaceResourceUserWithHandle(ldInst, handle);
  1669. } else {
  1670. DXASSERT(dyn_cast<GEPOperator>(user) != nullptr,
  1671. "else AddOpcodeParamForIntrinsic in CodeGen did not patch uses "
  1672. "to only have ld/st refer to temp object");
  1673. GEPOperator *GEP = cast<GEPOperator>(user);
  1674. Value *idx = nullptr;
  1675. if (GEP->getNumIndices() == 2) {
  1676. // one dim array of resource
  1677. idx = (GEP->idx_begin() + 1)->get();
  1678. } else {
  1679. gep_type_iterator GEPIt = gep_type_begin(GEP), E = gep_type_end(GEP);
  1680. // Must be instruction for multi dim array.
  1681. std::unique_ptr<IRBuilder<> > Builder;
  1682. if (GetElementPtrInst *GEPInst = dyn_cast<GetElementPtrInst>(GEP)) {
  1683. Builder = llvm::make_unique<IRBuilder<> >(GEPInst);
  1684. } else {
  1685. Builder = llvm::make_unique<IRBuilder<> >(GV->getContext());
  1686. }
  1687. for (; GEPIt != E; ++GEPIt) {
  1688. if (GEPIt->isArrayTy()) {
  1689. unsigned arraySize = GEPIt->getArrayNumElements();
  1690. Value * tmpIdx = GEPIt.getOperand();
  1691. if (idx == nullptr)
  1692. idx = tmpIdx;
  1693. else {
  1694. idx = Builder->CreateMul(idx, Builder->getInt32(arraySize));
  1695. idx = Builder->CreateAdd(idx, tmpIdx);
  1696. }
  1697. }
  1698. }
  1699. }
  1700. createHandleArgs[DXIL::OperandIndex::kCreateHandleResIndexOpIdx] = idx;
  1701. createHandleArgs[DXIL::OperandIndex::kCreateHandleIsUniformOpIdx] =
  1702. isUniformRes;
  1703. Value *handle = nullptr;
  1704. if (GetElementPtrInst *GEPInst = dyn_cast<GetElementPtrInst>(GEP)) {
  1705. IRBuilder<> Builder = IRBuilder<>(GEPInst);
  1706. if (DxilMDHelper::IsMarkedNonUniform(GEPInst)) {
  1707. // Mark nonUniform.
  1708. createHandleArgs[DXIL::OperandIndex::kCreateHandleIsUniformOpIdx] =
  1709. hlslOP->GetI1Const(1);
  1710. // Clear nonUniform on GEP.
  1711. GEPInst->setMetadata(DxilMDHelper::kDxilNonUniformAttributeMDName, nullptr);
  1712. }
  1713. createHandleArgs[DXIL::OperandIndex::kCreateHandleResIndexOpIdx] =
  1714. Builder.CreateAdd(idx, resLowerBound);
  1715. handle = Builder.CreateCall(createHandle, createHandleArgs, handleName);
  1716. }
  1717. for (auto GEPU = GEP->user_begin(), GEPE = GEP->user_end();
  1718. GEPU != GEPE;) {
  1719. // Must be load inst.
  1720. LoadInst *ldInst = cast<LoadInst>(*(GEPU++));
  1721. if (handle) {
  1722. ReplaceResourceUserWithHandle(ldInst, handle);
  1723. } else {
  1724. IRBuilder<> Builder = IRBuilder<>(ldInst);
  1725. createHandleArgs[DXIL::OperandIndex::kCreateHandleResIndexOpIdx] =
  1726. Builder.CreateAdd(idx, resLowerBound);
  1727. Value *localHandle =
  1728. Builder.CreateCall(createHandle, createHandleArgs, handleName);
  1729. ReplaceResourceUserWithHandle(ldInst, localHandle);
  1730. }
  1731. }
  1732. if (Instruction *I = dyn_cast<Instruction>(GEP)) {
  1733. I->eraseFromParent();
  1734. }
  1735. }
  1736. }
  1737. // Erase unused handle.
  1738. for (auto It : handleMapOnFunction) {
  1739. Instruction *I = It.second;
  1740. if (I->user_empty())
  1741. I->eraseFromParent();
  1742. }
  1743. }
  1744. void DxilLowerCreateHandleForLib::GenerateDxilResourceHandles() {
  1745. for (size_t i = 0; i < m_DM->GetCBuffers().size(); i++) {
  1746. DxilCBuffer &C = m_DM->GetCBuffer(i);
  1747. TranslateDxilResourceUses(C);
  1748. }
  1749. // Create sampler handle first, may be used by SRV operations.
  1750. for (size_t i = 0; i < m_DM->GetSamplers().size(); i++) {
  1751. DxilSampler &S = m_DM->GetSampler(i);
  1752. TranslateDxilResourceUses(S);
  1753. }
  1754. for (size_t i = 0; i < m_DM->GetSRVs().size(); i++) {
  1755. DxilResource &SRV = m_DM->GetSRV(i);
  1756. TranslateDxilResourceUses(SRV);
  1757. }
  1758. for (size_t i = 0; i < m_DM->GetUAVs().size(); i++) {
  1759. DxilResource &UAV = m_DM->GetUAV(i);
  1760. TranslateDxilResourceUses(UAV);
  1761. }
  1762. }
  1763. // TBuffer.
  1764. namespace {
  1765. void InitTBuffer(const DxilCBuffer *pSource, DxilResource *pDest) {
  1766. pDest->SetKind(pSource->GetKind());
  1767. pDest->SetCompType(DXIL::ComponentType::U32);
  1768. pDest->SetSampleCount(0);
  1769. pDest->SetElementStride(0);
  1770. pDest->SetGloballyCoherent(false);
  1771. pDest->SetHasCounter(false);
  1772. pDest->SetRW(false);
  1773. pDest->SetROV(false);
  1774. pDest->SetID(pSource->GetID());
  1775. pDest->SetSpaceID(pSource->GetSpaceID());
  1776. pDest->SetLowerBound(pSource->GetLowerBound());
  1777. pDest->SetRangeSize(pSource->GetRangeSize());
  1778. pDest->SetGlobalSymbol(pSource->GetGlobalSymbol());
  1779. pDest->SetGlobalName(pSource->GetGlobalName());
  1780. pDest->SetHandle(pSource->GetHandle());
  1781. }
  1782. void PatchTBufferLoad(CallInst *handle, DxilModule &DM) {
  1783. hlsl::OP *hlslOP = DM.GetOP();
  1784. llvm::LLVMContext &Ctx = DM.GetCtx();
  1785. Type *doubleTy = Type::getDoubleTy(Ctx);
  1786. Type *i64Ty = Type::getInt64Ty(Ctx);
  1787. // Replace corresponding cbuffer loads with typed buffer loads
  1788. for (auto U = handle->user_begin(); U != handle->user_end();) {
  1789. CallInst *I = cast<CallInst>(*(U++));
  1790. DXASSERT(I && OP::IsDxilOpFuncCallInst(I),
  1791. "otherwise unexpected user of CreateHandle value");
  1792. DXIL::OpCode opcode = OP::GetDxilOpFuncCallInst(I);
  1793. if (opcode == DXIL::OpCode::CBufferLoadLegacy) {
  1794. DxilInst_CBufferLoadLegacy cbLoad(I);
  1795. // Replace with appropriate buffer load instruction
  1796. IRBuilder<> Builder(I);
  1797. opcode = OP::OpCode::BufferLoad;
  1798. Type *Ty = Type::getInt32Ty(Ctx);
  1799. Function *BufLoad = hlslOP->GetOpFunc(opcode, Ty);
  1800. Constant *opArg = hlslOP->GetU32Const((unsigned)opcode);
  1801. Value *undefI = UndefValue::get(Type::getInt32Ty(Ctx));
  1802. Value *offset = cbLoad.get_regIndex();
  1803. CallInst *load =
  1804. Builder.CreateCall(BufLoad, {opArg, handle, offset, undefI});
  1805. // Find extractelement uses of cbuffer load and replace + generate bitcast
  1806. // as necessary
  1807. for (auto LU = I->user_begin(); LU != I->user_end();) {
  1808. ExtractValueInst *evInst = dyn_cast<ExtractValueInst>(*(LU++));
  1809. DXASSERT(evInst && evInst->getNumIndices() == 1,
  1810. "user of cbuffer load result should be extractvalue");
  1811. uint64_t idx = evInst->getIndices()[0];
  1812. Type *EltTy = evInst->getType();
  1813. IRBuilder<> EEBuilder(evInst);
  1814. Value *result = nullptr;
  1815. if (EltTy != Ty) {
  1816. // extract two values and DXIL::OpCode::MakeDouble or construct i64
  1817. if ((EltTy == doubleTy) || (EltTy == i64Ty)) {
  1818. DXASSERT(idx < 2, "64-bit component index out of range");
  1819. // This assumes big endian order in tbuffer elements (is this
  1820. // correct?)
  1821. Value *low = EEBuilder.CreateExtractValue(load, idx * 2);
  1822. Value *high = EEBuilder.CreateExtractValue(load, idx * 2 + 1);
  1823. if (EltTy == doubleTy) {
  1824. opcode = OP::OpCode::MakeDouble;
  1825. Function *MakeDouble = hlslOP->GetOpFunc(opcode, doubleTy);
  1826. Constant *opArg = hlslOP->GetU32Const((unsigned)opcode);
  1827. result = EEBuilder.CreateCall(MakeDouble, {opArg, low, high});
  1828. } else {
  1829. high = EEBuilder.CreateZExt(high, i64Ty);
  1830. low = EEBuilder.CreateZExt(low, i64Ty);
  1831. high = EEBuilder.CreateShl(high, hlslOP->GetU64Const(32));
  1832. result = EEBuilder.CreateOr(high, low);
  1833. }
  1834. } else {
  1835. result = EEBuilder.CreateExtractValue(load, idx);
  1836. result = EEBuilder.CreateBitCast(result, EltTy);
  1837. }
  1838. } else {
  1839. result = EEBuilder.CreateExtractValue(load, idx);
  1840. }
  1841. evInst->replaceAllUsesWith(result);
  1842. evInst->eraseFromParent();
  1843. }
  1844. } else if (opcode == DXIL::OpCode::CBufferLoad) {
  1845. // TODO: Handle this, or prevent this for tbuffer
  1846. DXASSERT(false, "otherwise CBufferLoad used for tbuffer rather than "
  1847. "CBufferLoadLegacy");
  1848. } else {
  1849. DXASSERT(false, "otherwise unexpected user of CreateHandle value");
  1850. }
  1851. I->eraseFromParent();
  1852. }
  1853. }
  1854. } // namespace
  1855. void DxilLowerCreateHandleForLib::PatchTBufferUse(Value *V, DxilModule &DM) {
  1856. for (User *U : V->users()) {
  1857. if (CallInst *CI = dyn_cast<CallInst>(U)) {
  1858. // Patch dxil call.
  1859. if (hlsl::OP::IsDxilOpFuncCallInst(CI))
  1860. PatchTBufferLoad(CI, DM);
  1861. } else {
  1862. PatchTBufferUse(U, DM);
  1863. }
  1864. }
  1865. }
  1866. bool DxilLowerCreateHandleForLib::PatchTBuffers(DxilModule &DM) {
  1867. bool bChanged = false;
  1868. // move tbuffer resources to SRVs
  1869. unsigned offset = DM.GetSRVs().size();
  1870. Module &M = *DM.GetModule();
  1871. for (auto it = DM.GetCBuffers().begin(); it != DM.GetCBuffers().end(); it++) {
  1872. DxilCBuffer *CB = it->get();
  1873. if (CB->GetKind() == DXIL::ResourceKind::TBuffer) {
  1874. auto srv = make_unique<DxilResource>();
  1875. InitTBuffer(CB, srv.get());
  1876. srv->SetID(offset++);
  1877. DM.AddSRV(std::move(srv));
  1878. GlobalVariable *GV = cast<GlobalVariable>(CB->GetGlobalSymbol());
  1879. PatchTBufferUse(GV, DM);
  1880. // Set global symbol for cbuffer to an unused value so it can be removed
  1881. // in RemoveUnusedResourceSymbols.
  1882. Type *Ty = GV->getType()->getElementType();
  1883. GlobalVariable *NewGV = new GlobalVariable(
  1884. M, Ty, GV->isConstant(), GV->getLinkage(), /*Initializer*/ nullptr,
  1885. GV->getName(),
  1886. /*InsertBefore*/ nullptr, GV->getThreadLocalMode(),
  1887. GV->getType()->getAddressSpace(), GV->isExternallyInitialized());
  1888. CB->SetGlobalSymbol(NewGV);
  1889. bChanged = true;
  1890. }
  1891. }
  1892. return bChanged;
  1893. }
  1894. char DxilLowerCreateHandleForLib::ID = 0;
  1895. ModulePass *llvm::createDxilLowerCreateHandleForLibPass() {
  1896. return new DxilLowerCreateHandleForLib();
  1897. }
  1898. INITIALIZE_PASS(DxilLowerCreateHandleForLib, "hlsl-dxil-lower-handle-for-lib", "DXIL Lower createHandleForLib", false, false)
  1899. class DxilAllocateResourcesForLib : public ModulePass {
  1900. private:
  1901. RemapEntryCollection m_rewrites;
  1902. public:
  1903. static char ID; // Pass identification, replacement for typeid
  1904. explicit DxilAllocateResourcesForLib() : ModulePass(ID), m_AutoBindingSpace(UINT_MAX) {}
  1905. void applyOptions(PassOptions O) override {
  1906. GetPassOptionUInt32(O, "auto-binding-space", &m_AutoBindingSpace, UINT_MAX);
  1907. }
  1908. const char *getPassName() const override { return "DXIL Condense Resources"; }
  1909. bool runOnModule(Module &M) override {
  1910. DxilModule &DM = M.GetOrCreateDxilModule();
  1911. // Must specify a default space, and must apply to library.
  1912. // Use DxilCondenseResources instead for shaders.
  1913. if ((m_AutoBindingSpace == UINT_MAX) || !DM.GetShaderModel()->IsLib())
  1914. return false;
  1915. bool hasResource = DM.GetCBuffers().size() ||
  1916. DM.GetUAVs().size() || DM.GetSRVs().size() || DM.GetSamplers().size();
  1917. if (hasResource) {
  1918. DM.SetAutoBindingSpace(m_AutoBindingSpace);
  1919. AllocateDxilResources(DM);
  1920. }
  1921. return true;
  1922. }
  1923. private:
  1924. uint32_t m_AutoBindingSpace;
  1925. };
  1926. char DxilAllocateResourcesForLib::ID = 0;
  1927. ModulePass *llvm::createDxilAllocateResourcesForLibPass() {
  1928. return new DxilAllocateResourcesForLib();
  1929. }
  1930. INITIALIZE_PASS(DxilAllocateResourcesForLib, "hlsl-dxil-allocate-resources-for-lib", "DXIL Allocate Resources For Library", false, false)