eliminate_dead_members_pass.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. // Copyright (c) 2019 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "source/opt/eliminate_dead_members_pass.h"
  15. #include "ir_builder.h"
  16. #include "source/opt/ir_context.h"
  17. namespace spvtools {
  18. namespace opt {
  19. namespace {
  20. constexpr uint32_t kRemovedMember = 0xFFFFFFFF;
  21. constexpr uint32_t kSpecConstOpOpcodeIdx = 0;
  22. constexpr uint32_t kArrayElementTypeIdx = 0;
  23. } // namespace
  24. Pass::Status EliminateDeadMembersPass::Process() {
  25. if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader))
  26. return Status::SuccessWithoutChange;
  27. FindLiveMembers();
  28. if (RemoveDeadMembers()) {
  29. return Status::SuccessWithChange;
  30. }
  31. return Status::SuccessWithoutChange;
  32. }
  33. void EliminateDeadMembersPass::FindLiveMembers() {
  34. // Until we have implemented the rewriting of OpSpecConsantOp instructions,
  35. // we have to mark them as fully used just to be safe.
  36. for (auto& inst : get_module()->types_values()) {
  37. if (inst.opcode() == spv::Op::OpSpecConstantOp) {
  38. switch (spv::Op(inst.GetSingleWordInOperand(kSpecConstOpOpcodeIdx))) {
  39. case spv::Op::OpCompositeExtract:
  40. MarkMembersAsLiveForExtract(&inst);
  41. break;
  42. case spv::Op::OpCompositeInsert:
  43. // Nothing specific to do.
  44. break;
  45. case spv::Op::OpAccessChain:
  46. case spv::Op::OpInBoundsAccessChain:
  47. case spv::Op::OpPtrAccessChain:
  48. case spv::Op::OpInBoundsPtrAccessChain:
  49. assert(false && "Not implemented yet.");
  50. break;
  51. default:
  52. break;
  53. }
  54. } else if (inst.opcode() == spv::Op::OpVariable) {
  55. switch (spv::StorageClass(inst.GetSingleWordInOperand(0))) {
  56. case spv::StorageClass::Input:
  57. case spv::StorageClass::Output:
  58. MarkPointeeTypeAsFullUsed(inst.type_id());
  59. break;
  60. default:
  61. // Ignore structured buffers as layout(offset) qualifiers cannot be
  62. // applied to structure fields
  63. if (inst.IsVulkanStorageBufferVariable())
  64. MarkPointeeTypeAsFullUsed(inst.type_id());
  65. break;
  66. }
  67. } else if (inst.opcode() == spv::Op::OpTypePointer) {
  68. uint32_t storage_class = inst.GetSingleWordInOperand(0);
  69. if (storage_class == uint32_t(spv::StorageClass::PhysicalStorageBuffer)) {
  70. MarkTypeAsFullyUsed(inst.GetSingleWordInOperand(1));
  71. }
  72. }
  73. }
  74. for (const Function& func : *get_module()) {
  75. FindLiveMembers(func);
  76. }
  77. }
  78. void EliminateDeadMembersPass::FindLiveMembers(const Function& function) {
  79. function.ForEachInst(
  80. [this](const Instruction* inst) { FindLiveMembers(inst); });
  81. }
  82. void EliminateDeadMembersPass::FindLiveMembers(const Instruction* inst) {
  83. switch (inst->opcode()) {
  84. case spv::Op::OpStore:
  85. MarkMembersAsLiveForStore(inst);
  86. break;
  87. case spv::Op::OpCopyMemory:
  88. case spv::Op::OpCopyMemorySized:
  89. MarkMembersAsLiveForCopyMemory(inst);
  90. break;
  91. case spv::Op::OpCompositeExtract:
  92. MarkMembersAsLiveForExtract(inst);
  93. break;
  94. case spv::Op::OpAccessChain:
  95. case spv::Op::OpInBoundsAccessChain:
  96. case spv::Op::OpPtrAccessChain:
  97. case spv::Op::OpInBoundsPtrAccessChain:
  98. MarkMembersAsLiveForAccessChain(inst);
  99. break;
  100. case spv::Op::OpReturnValue:
  101. // This should be an issue only if we are returning from the entry point.
  102. // However, for now I will keep it more conservative because functions are
  103. // often inlined leaving only the entry points.
  104. MarkOperandTypeAsFullyUsed(inst, 0);
  105. break;
  106. case spv::Op::OpArrayLength:
  107. MarkMembersAsLiveForArrayLength(inst);
  108. break;
  109. case spv::Op::OpLoad:
  110. case spv::Op::OpCompositeInsert:
  111. case spv::Op::OpCompositeConstruct:
  112. break;
  113. default:
  114. // This path is here for safety. All instructions that can reference
  115. // structs in a function body should be handled above. However, this will
  116. // keep the pass valid, but not optimal, as new instructions get added
  117. // or if something was missed.
  118. MarkStructOperandsAsFullyUsed(inst);
  119. break;
  120. }
  121. }
  122. void EliminateDeadMembersPass::MarkMembersAsLiveForStore(
  123. const Instruction* inst) {
  124. // We should only have to mark the members as live if the store is to
  125. // memory that is read outside of the shader. Other passes can remove all
  126. // store to memory that is not visible outside of the shader, so we do not
  127. // complicate the code for now.
  128. assert(inst->opcode() == spv::Op::OpStore);
  129. uint32_t object_id = inst->GetSingleWordInOperand(1);
  130. Instruction* object_inst = context()->get_def_use_mgr()->GetDef(object_id);
  131. uint32_t object_type_id = object_inst->type_id();
  132. MarkTypeAsFullyUsed(object_type_id);
  133. }
  134. void EliminateDeadMembersPass::MarkTypeAsFullyUsed(uint32_t type_id) {
  135. Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
  136. assert(type_inst != nullptr);
  137. switch (type_inst->opcode()) {
  138. case spv::Op::OpTypeStruct:
  139. // Mark every member and its type as fully used.
  140. for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) {
  141. used_members_[type_id].insert(i);
  142. MarkTypeAsFullyUsed(type_inst->GetSingleWordInOperand(i));
  143. }
  144. break;
  145. case spv::Op::OpTypeArray:
  146. case spv::Op::OpTypeRuntimeArray:
  147. MarkTypeAsFullyUsed(
  148. type_inst->GetSingleWordInOperand(kArrayElementTypeIdx));
  149. break;
  150. default:
  151. break;
  152. }
  153. }
  154. void EliminateDeadMembersPass::MarkPointeeTypeAsFullUsed(uint32_t ptr_type_id) {
  155. Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
  156. assert(ptr_type_inst->opcode() == spv::Op::OpTypePointer);
  157. MarkTypeAsFullyUsed(ptr_type_inst->GetSingleWordInOperand(1));
  158. }
  159. void EliminateDeadMembersPass::MarkMembersAsLiveForCopyMemory(
  160. const Instruction* inst) {
  161. uint32_t target_id = inst->GetSingleWordInOperand(0);
  162. Instruction* target_inst = get_def_use_mgr()->GetDef(target_id);
  163. uint32_t pointer_type_id = target_inst->type_id();
  164. Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
  165. uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
  166. MarkTypeAsFullyUsed(type_id);
  167. }
  168. void EliminateDeadMembersPass::MarkMembersAsLiveForExtract(
  169. const Instruction* inst) {
  170. assert(inst->opcode() == spv::Op::OpCompositeExtract ||
  171. (inst->opcode() == spv::Op::OpSpecConstantOp &&
  172. spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) ==
  173. spv::Op::OpCompositeExtract));
  174. uint32_t first_operand =
  175. (inst->opcode() == spv::Op::OpSpecConstantOp ? 1 : 0);
  176. uint32_t composite_id = inst->GetSingleWordInOperand(first_operand);
  177. Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id);
  178. uint32_t type_id = composite_inst->type_id();
  179. for (uint32_t i = first_operand + 1; i < inst->NumInOperands(); ++i) {
  180. Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
  181. uint32_t member_idx = inst->GetSingleWordInOperand(i);
  182. switch (type_inst->opcode()) {
  183. case spv::Op::OpTypeStruct:
  184. used_members_[type_id].insert(member_idx);
  185. type_id = type_inst->GetSingleWordInOperand(member_idx);
  186. break;
  187. case spv::Op::OpTypeArray:
  188. case spv::Op::OpTypeRuntimeArray:
  189. case spv::Op::OpTypeVector:
  190. case spv::Op::OpTypeMatrix:
  191. case spv::Op::OpTypeCooperativeMatrixNV:
  192. case spv::Op::OpTypeCooperativeMatrixKHR:
  193. case spv::Op::OpTypeCooperativeVectorNV:
  194. type_id = type_inst->GetSingleWordInOperand(0);
  195. break;
  196. default:
  197. assert(false);
  198. }
  199. }
  200. }
  201. void EliminateDeadMembersPass::MarkMembersAsLiveForAccessChain(
  202. const Instruction* inst) {
  203. assert(inst->opcode() == spv::Op::OpAccessChain ||
  204. inst->opcode() == spv::Op::OpInBoundsAccessChain ||
  205. inst->opcode() == spv::Op::OpPtrAccessChain ||
  206. inst->opcode() == spv::Op::OpInBoundsPtrAccessChain);
  207. uint32_t pointer_id = inst->GetSingleWordInOperand(0);
  208. Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id);
  209. uint32_t pointer_type_id = pointer_inst->type_id();
  210. Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
  211. uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
  212. analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
  213. // For a pointer access chain, we need to skip the |element| index. It is not
  214. // a reference to the member of a struct, and it does not change the type.
  215. uint32_t i = (inst->opcode() == spv::Op::OpAccessChain ||
  216. inst->opcode() == spv::Op::OpInBoundsAccessChain
  217. ? 1
  218. : 2);
  219. for (; i < inst->NumInOperands(); ++i) {
  220. Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
  221. switch (type_inst->opcode()) {
  222. case spv::Op::OpTypeStruct: {
  223. const analysis::IntConstant* member_idx =
  224. const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i))
  225. ->AsIntConstant();
  226. assert(member_idx);
  227. uint32_t index =
  228. static_cast<uint32_t>(member_idx->GetZeroExtendedValue());
  229. used_members_[type_id].insert(index);
  230. type_id = type_inst->GetSingleWordInOperand(index);
  231. } break;
  232. case spv::Op::OpTypeArray:
  233. case spv::Op::OpTypeRuntimeArray:
  234. case spv::Op::OpTypeVector:
  235. case spv::Op::OpTypeMatrix:
  236. case spv::Op::OpTypeCooperativeMatrixNV:
  237. case spv::Op::OpTypeCooperativeMatrixKHR:
  238. case spv::Op::OpTypeCooperativeVectorNV:
  239. type_id = type_inst->GetSingleWordInOperand(0);
  240. break;
  241. default:
  242. assert(false);
  243. }
  244. }
  245. }
  246. void EliminateDeadMembersPass::MarkOperandTypeAsFullyUsed(
  247. const Instruction* inst, uint32_t in_idx) {
  248. uint32_t op_id = inst->GetSingleWordInOperand(in_idx);
  249. Instruction* op_inst = get_def_use_mgr()->GetDef(op_id);
  250. MarkTypeAsFullyUsed(op_inst->type_id());
  251. }
  252. void EliminateDeadMembersPass::MarkMembersAsLiveForArrayLength(
  253. const Instruction* inst) {
  254. assert(inst->opcode() == spv::Op::OpArrayLength);
  255. uint32_t object_id = inst->GetSingleWordInOperand(0);
  256. Instruction* object_inst = get_def_use_mgr()->GetDef(object_id);
  257. uint32_t pointer_type_id = object_inst->type_id();
  258. Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
  259. uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
  260. used_members_[type_id].insert(inst->GetSingleWordInOperand(1));
  261. }
  262. bool EliminateDeadMembersPass::RemoveDeadMembers() {
  263. bool modified = false;
  264. // First update all of the OpTypeStruct instructions.
  265. get_module()->ForEachInst([&modified, this](Instruction* inst) {
  266. switch (inst->opcode()) {
  267. case spv::Op::OpTypeStruct:
  268. modified |= UpdateOpTypeStruct(inst);
  269. break;
  270. default:
  271. break;
  272. }
  273. });
  274. // Now update all of the instructions that reference the OpTypeStructs.
  275. get_module()->ForEachInst([&modified, this](Instruction* inst) {
  276. switch (inst->opcode()) {
  277. case spv::Op::OpMemberName:
  278. modified |= UpdateOpMemberNameOrDecorate(inst);
  279. break;
  280. case spv::Op::OpMemberDecorate:
  281. modified |= UpdateOpMemberNameOrDecorate(inst);
  282. break;
  283. case spv::Op::OpGroupMemberDecorate:
  284. modified |= UpdateOpGroupMemberDecorate(inst);
  285. break;
  286. case spv::Op::OpSpecConstantComposite:
  287. case spv::Op::OpConstantComposite:
  288. case spv::Op::OpCompositeConstruct:
  289. modified |= UpdateConstantComposite(inst);
  290. break;
  291. case spv::Op::OpAccessChain:
  292. case spv::Op::OpInBoundsAccessChain:
  293. case spv::Op::OpPtrAccessChain:
  294. case spv::Op::OpInBoundsPtrAccessChain:
  295. modified |= UpdateAccessChain(inst);
  296. break;
  297. case spv::Op::OpCompositeExtract:
  298. modified |= UpdateCompsiteExtract(inst);
  299. break;
  300. case spv::Op::OpCompositeInsert:
  301. modified |= UpdateCompositeInsert(inst);
  302. break;
  303. case spv::Op::OpArrayLength:
  304. modified |= UpdateOpArrayLength(inst);
  305. break;
  306. case spv::Op::OpSpecConstantOp:
  307. switch (spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx))) {
  308. case spv::Op::OpCompositeExtract:
  309. modified |= UpdateCompsiteExtract(inst);
  310. break;
  311. case spv::Op::OpCompositeInsert:
  312. modified |= UpdateCompositeInsert(inst);
  313. break;
  314. case spv::Op::OpAccessChain:
  315. case spv::Op::OpInBoundsAccessChain:
  316. case spv::Op::OpPtrAccessChain:
  317. case spv::Op::OpInBoundsPtrAccessChain:
  318. assert(false && "Not implemented yet.");
  319. break;
  320. default:
  321. break;
  322. }
  323. break;
  324. default:
  325. break;
  326. }
  327. });
  328. return modified;
  329. }
  330. bool EliminateDeadMembersPass::UpdateOpTypeStruct(Instruction* inst) {
  331. assert(inst->opcode() == spv::Op::OpTypeStruct);
  332. const auto& live_members = used_members_[inst->result_id()];
  333. if (live_members.size() == inst->NumInOperands()) {
  334. return false;
  335. }
  336. Instruction::OperandList new_operands;
  337. for (uint32_t idx : live_members) {
  338. new_operands.emplace_back(inst->GetInOperand(idx));
  339. }
  340. inst->SetInOperands(std::move(new_operands));
  341. context()->UpdateDefUse(inst);
  342. return true;
  343. }
  344. bool EliminateDeadMembersPass::UpdateOpMemberNameOrDecorate(Instruction* inst) {
  345. assert(inst->opcode() == spv::Op::OpMemberName ||
  346. inst->opcode() == spv::Op::OpMemberDecorate);
  347. uint32_t type_id = inst->GetSingleWordInOperand(0);
  348. auto live_members = used_members_.find(type_id);
  349. if (live_members == used_members_.end()) {
  350. return false;
  351. }
  352. uint32_t orig_member_idx = inst->GetSingleWordInOperand(1);
  353. uint32_t new_member_idx = GetNewMemberIndex(type_id, orig_member_idx);
  354. if (new_member_idx == kRemovedMember) {
  355. context()->KillInst(inst);
  356. return true;
  357. }
  358. if (new_member_idx == orig_member_idx) {
  359. return false;
  360. }
  361. inst->SetInOperand(1, {new_member_idx});
  362. return true;
  363. }
  364. bool EliminateDeadMembersPass::UpdateOpGroupMemberDecorate(Instruction* inst) {
  365. assert(inst->opcode() == spv::Op::OpGroupMemberDecorate);
  366. bool modified = false;
  367. Instruction::OperandList new_operands;
  368. new_operands.emplace_back(inst->GetInOperand(0));
  369. for (uint32_t i = 1; i < inst->NumInOperands(); i += 2) {
  370. uint32_t type_id = inst->GetSingleWordInOperand(i);
  371. uint32_t member_idx = inst->GetSingleWordInOperand(i + 1);
  372. uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
  373. if (new_member_idx == kRemovedMember) {
  374. modified = true;
  375. continue;
  376. }
  377. new_operands.emplace_back(inst->GetOperand(i));
  378. if (new_member_idx != member_idx) {
  379. new_operands.emplace_back(
  380. Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}}));
  381. modified = true;
  382. } else {
  383. new_operands.emplace_back(inst->GetOperand(i + 1));
  384. }
  385. }
  386. if (!modified) {
  387. return false;
  388. }
  389. if (new_operands.size() == 1) {
  390. context()->KillInst(inst);
  391. return true;
  392. }
  393. inst->SetInOperands(std::move(new_operands));
  394. context()->UpdateDefUse(inst);
  395. return true;
  396. }
  397. bool EliminateDeadMembersPass::UpdateConstantComposite(Instruction* inst) {
  398. assert(inst->opcode() == spv::Op::OpSpecConstantComposite ||
  399. inst->opcode() == spv::Op::OpConstantComposite ||
  400. inst->opcode() == spv::Op::OpCompositeConstruct);
  401. uint32_t type_id = inst->type_id();
  402. bool modified = false;
  403. Instruction::OperandList new_operands;
  404. for (uint32_t i = 0; i < inst->NumInOperands(); ++i) {
  405. uint32_t new_idx = GetNewMemberIndex(type_id, i);
  406. if (new_idx == kRemovedMember) {
  407. modified = true;
  408. } else {
  409. new_operands.emplace_back(inst->GetInOperand(i));
  410. }
  411. }
  412. inst->SetInOperands(std::move(new_operands));
  413. context()->UpdateDefUse(inst);
  414. return modified;
  415. }
  416. bool EliminateDeadMembersPass::UpdateAccessChain(Instruction* inst) {
  417. assert(inst->opcode() == spv::Op::OpAccessChain ||
  418. inst->opcode() == spv::Op::OpInBoundsAccessChain ||
  419. inst->opcode() == spv::Op::OpPtrAccessChain ||
  420. inst->opcode() == spv::Op::OpInBoundsPtrAccessChain);
  421. uint32_t pointer_id = inst->GetSingleWordInOperand(0);
  422. Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id);
  423. uint32_t pointer_type_id = pointer_inst->type_id();
  424. Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
  425. uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
  426. analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
  427. Instruction::OperandList new_operands;
  428. bool modified = false;
  429. new_operands.emplace_back(inst->GetInOperand(0));
  430. // For pointer access chains we want to copy the element operand.
  431. if (inst->opcode() == spv::Op::OpPtrAccessChain ||
  432. inst->opcode() == spv::Op::OpInBoundsPtrAccessChain) {
  433. new_operands.emplace_back(inst->GetInOperand(1));
  434. }
  435. for (uint32_t i = static_cast<uint32_t>(new_operands.size());
  436. i < inst->NumInOperands(); ++i) {
  437. Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
  438. switch (type_inst->opcode()) {
  439. case spv::Op::OpTypeStruct: {
  440. const analysis::IntConstant* member_idx =
  441. const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i))
  442. ->AsIntConstant();
  443. assert(member_idx);
  444. uint32_t orig_member_idx =
  445. static_cast<uint32_t>(member_idx->GetZeroExtendedValue());
  446. uint32_t new_member_idx = GetNewMemberIndex(type_id, orig_member_idx);
  447. assert(new_member_idx != kRemovedMember);
  448. if (orig_member_idx != new_member_idx) {
  449. InstructionBuilder ir_builder(
  450. context(), inst,
  451. IRContext::kAnalysisDefUse |
  452. IRContext::kAnalysisInstrToBlockMapping);
  453. uint32_t const_id =
  454. ir_builder.GetUintConstant(new_member_idx)->result_id();
  455. new_operands.emplace_back(Operand({SPV_OPERAND_TYPE_ID, {const_id}}));
  456. modified = true;
  457. } else {
  458. new_operands.emplace_back(inst->GetInOperand(i));
  459. }
  460. // The type will have already been rewritten, so use the new member
  461. // index.
  462. type_id = type_inst->GetSingleWordInOperand(new_member_idx);
  463. } break;
  464. case spv::Op::OpTypeArray:
  465. case spv::Op::OpTypeRuntimeArray:
  466. case spv::Op::OpTypeVector:
  467. case spv::Op::OpTypeMatrix:
  468. case spv::Op::OpTypeCooperativeMatrixNV:
  469. case spv::Op::OpTypeCooperativeMatrixKHR:
  470. case spv::Op::OpTypeCooperativeVectorNV:
  471. new_operands.emplace_back(inst->GetInOperand(i));
  472. type_id = type_inst->GetSingleWordInOperand(0);
  473. break;
  474. default:
  475. assert(false);
  476. break;
  477. }
  478. }
  479. if (!modified) {
  480. return false;
  481. }
  482. inst->SetInOperands(std::move(new_operands));
  483. context()->UpdateDefUse(inst);
  484. return true;
  485. }
  486. uint32_t EliminateDeadMembersPass::GetNewMemberIndex(uint32_t type_id,
  487. uint32_t member_idx) {
  488. auto live_members = used_members_.find(type_id);
  489. if (live_members == used_members_.end()) {
  490. return member_idx;
  491. }
  492. auto current_member = live_members->second.find(member_idx);
  493. if (current_member == live_members->second.end()) {
  494. return kRemovedMember;
  495. }
  496. return static_cast<uint32_t>(
  497. std::distance(live_members->second.begin(), current_member));
  498. }
  499. bool EliminateDeadMembersPass::UpdateCompsiteExtract(Instruction* inst) {
  500. assert(inst->opcode() == spv::Op::OpCompositeExtract ||
  501. (inst->opcode() == spv::Op::OpSpecConstantOp &&
  502. spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) ==
  503. spv::Op::OpCompositeExtract));
  504. uint32_t first_operand = 0;
  505. if (inst->opcode() == spv::Op::OpSpecConstantOp) {
  506. first_operand = 1;
  507. }
  508. uint32_t object_id = inst->GetSingleWordInOperand(first_operand);
  509. Instruction* object_inst = get_def_use_mgr()->GetDef(object_id);
  510. uint32_t type_id = object_inst->type_id();
  511. Instruction::OperandList new_operands;
  512. bool modified = false;
  513. for (uint32_t i = 0; i < first_operand + 1; i++) {
  514. new_operands.emplace_back(inst->GetInOperand(i));
  515. }
  516. for (uint32_t i = first_operand + 1; i < inst->NumInOperands(); ++i) {
  517. uint32_t member_idx = inst->GetSingleWordInOperand(i);
  518. uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
  519. assert(new_member_idx != kRemovedMember);
  520. if (member_idx != new_member_idx) {
  521. modified = true;
  522. }
  523. new_operands.emplace_back(
  524. Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}}));
  525. Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
  526. switch (type_inst->opcode()) {
  527. case spv::Op::OpTypeStruct:
  528. // The type will have already been rewritten, so use the new member
  529. // index.
  530. type_id = type_inst->GetSingleWordInOperand(new_member_idx);
  531. break;
  532. case spv::Op::OpTypeArray:
  533. case spv::Op::OpTypeRuntimeArray:
  534. case spv::Op::OpTypeVector:
  535. case spv::Op::OpTypeMatrix:
  536. case spv::Op::OpTypeCooperativeMatrixNV:
  537. case spv::Op::OpTypeCooperativeMatrixKHR:
  538. case spv::Op::OpTypeCooperativeVectorNV:
  539. type_id = type_inst->GetSingleWordInOperand(0);
  540. break;
  541. default:
  542. assert(false);
  543. }
  544. }
  545. if (!modified) {
  546. return false;
  547. }
  548. inst->SetInOperands(std::move(new_operands));
  549. context()->UpdateDefUse(inst);
  550. return true;
  551. }
  552. bool EliminateDeadMembersPass::UpdateCompositeInsert(Instruction* inst) {
  553. assert(inst->opcode() == spv::Op::OpCompositeInsert ||
  554. (inst->opcode() == spv::Op::OpSpecConstantOp &&
  555. spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) ==
  556. spv::Op::OpCompositeInsert));
  557. uint32_t first_operand = 0;
  558. if (inst->opcode() == spv::Op::OpSpecConstantOp) {
  559. first_operand = 1;
  560. }
  561. uint32_t composite_id = inst->GetSingleWordInOperand(first_operand + 1);
  562. Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id);
  563. uint32_t type_id = composite_inst->type_id();
  564. Instruction::OperandList new_operands;
  565. bool modified = false;
  566. for (uint32_t i = 0; i < first_operand + 2; ++i) {
  567. new_operands.emplace_back(inst->GetInOperand(i));
  568. }
  569. for (uint32_t i = first_operand + 2; i < inst->NumInOperands(); ++i) {
  570. uint32_t member_idx = inst->GetSingleWordInOperand(i);
  571. uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
  572. if (new_member_idx == kRemovedMember) {
  573. context()->KillInst(inst);
  574. return true;
  575. }
  576. if (member_idx != new_member_idx) {
  577. modified = true;
  578. }
  579. new_operands.emplace_back(
  580. Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}}));
  581. Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
  582. switch (type_inst->opcode()) {
  583. case spv::Op::OpTypeStruct:
  584. // The type will have already been rewritten, so use the new member
  585. // index.
  586. type_id = type_inst->GetSingleWordInOperand(new_member_idx);
  587. break;
  588. case spv::Op::OpTypeArray:
  589. case spv::Op::OpTypeRuntimeArray:
  590. case spv::Op::OpTypeVector:
  591. case spv::Op::OpTypeMatrix:
  592. case spv::Op::OpTypeCooperativeMatrixNV:
  593. case spv::Op::OpTypeCooperativeMatrixKHR:
  594. case spv::Op::OpTypeCooperativeVectorNV:
  595. type_id = type_inst->GetSingleWordInOperand(0);
  596. break;
  597. default:
  598. assert(false);
  599. }
  600. }
  601. if (!modified) {
  602. return false;
  603. }
  604. inst->SetInOperands(std::move(new_operands));
  605. context()->UpdateDefUse(inst);
  606. return true;
  607. }
  608. bool EliminateDeadMembersPass::UpdateOpArrayLength(Instruction* inst) {
  609. uint32_t struct_id = inst->GetSingleWordInOperand(0);
  610. Instruction* struct_inst = get_def_use_mgr()->GetDef(struct_id);
  611. uint32_t pointer_type_id = struct_inst->type_id();
  612. Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
  613. uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
  614. uint32_t member_idx = inst->GetSingleWordInOperand(1);
  615. uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
  616. assert(new_member_idx != kRemovedMember);
  617. if (member_idx == new_member_idx) {
  618. return false;
  619. }
  620. inst->SetInOperand(1, {new_member_idx});
  621. context()->UpdateDefUse(inst);
  622. return true;
  623. }
  624. void EliminateDeadMembersPass::MarkStructOperandsAsFullyUsed(
  625. const Instruction* inst) {
  626. if (inst->type_id() != 0) {
  627. MarkTypeAsFullyUsed(inst->type_id());
  628. }
  629. inst->ForEachInId([this](const uint32_t* id) {
  630. Instruction* instruction = get_def_use_mgr()->GetDef(*id);
  631. if (instruction->type_id() != 0) {
  632. MarkTypeAsFullyUsed(instruction->type_id());
  633. }
  634. });
  635. }
  636. } // namespace opt
  637. } // namespace spvtools