decoration_manager.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. // Copyright (c) 2017 Pierre Moreau
  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/decoration_manager.h"
  15. #include <algorithm>
  16. #include <memory>
  17. #include <set>
  18. #include <stack>
  19. #include <utility>
  20. #include "source/opt/ir_context.h"
  21. namespace spvtools {
  22. namespace opt {
  23. namespace analysis {
  24. void DecorationManager::RemoveDecorationsFrom(
  25. uint32_t id, std::function<bool(const Instruction&)> pred) {
  26. const auto ids_iter = id_to_decoration_insts_.find(id);
  27. if (ids_iter == id_to_decoration_insts_.end()) return;
  28. TargetData& decorations_info = ids_iter->second;
  29. auto context = module_->context();
  30. std::vector<Instruction*> insts_to_kill;
  31. const bool is_group = !decorations_info.decorate_insts.empty();
  32. // Schedule all direct decorations for removal if instructed as such by
  33. // |pred|.
  34. for (Instruction* inst : decorations_info.direct_decorations)
  35. if (pred(*inst)) insts_to_kill.push_back(inst);
  36. // For all groups being directly applied to |id|, remove |id| (and the
  37. // literal if |inst| is an OpGroupMemberDecorate) from the instruction
  38. // applying the group.
  39. std::unordered_set<const Instruction*> indirect_decorations_to_remove;
  40. for (Instruction* inst : decorations_info.indirect_decorations) {
  41. assert(inst->opcode() == SpvOpGroupDecorate ||
  42. inst->opcode() == SpvOpGroupMemberDecorate);
  43. std::vector<Instruction*> group_decorations_to_keep;
  44. const uint32_t group_id = inst->GetSingleWordInOperand(0u);
  45. const auto group_iter = id_to_decoration_insts_.find(group_id);
  46. assert(group_iter != id_to_decoration_insts_.end() &&
  47. "Unknown decoration group");
  48. const auto& group_decorations = group_iter->second.direct_decorations;
  49. for (Instruction* decoration : group_decorations) {
  50. if (!pred(*decoration)) group_decorations_to_keep.push_back(decoration);
  51. }
  52. // If all decorations should be kept, move to the next group
  53. if (group_decorations_to_keep.size() == group_decorations.size()) continue;
  54. // Otherwise, remove |id| from the targets of |group_id|
  55. const uint32_t stride = inst->opcode() == SpvOpGroupDecorate ? 1u : 2u;
  56. bool was_modified = false;
  57. for (uint32_t i = 1u; i < inst->NumInOperands();) {
  58. if (inst->GetSingleWordInOperand(i) != id) {
  59. i += stride;
  60. continue;
  61. }
  62. const uint32_t last_operand_index = inst->NumInOperands() - stride;
  63. if (i < last_operand_index)
  64. inst->GetInOperand(i) = inst->GetInOperand(last_operand_index);
  65. // Remove the associated literal, if it exists.
  66. if (stride == 2u) {
  67. if (i < last_operand_index)
  68. inst->GetInOperand(i + 1u) =
  69. inst->GetInOperand(last_operand_index + 1u);
  70. inst->RemoveInOperand(last_operand_index + 1u);
  71. }
  72. inst->RemoveInOperand(last_operand_index);
  73. was_modified = true;
  74. }
  75. // If the instruction has no targets left, remove the instruction
  76. // altogether.
  77. if (inst->NumInOperands() == 1u) {
  78. indirect_decorations_to_remove.emplace(inst);
  79. insts_to_kill.push_back(inst);
  80. } else if (was_modified) {
  81. context->ForgetUses(inst);
  82. indirect_decorations_to_remove.emplace(inst);
  83. context->AnalyzeUses(inst);
  84. }
  85. // If only some of the decorations should be kept, clone them and apply
  86. // them directly to |id|.
  87. if (!group_decorations_to_keep.empty()) {
  88. for (Instruction* decoration : group_decorations_to_keep) {
  89. // simply clone decoration and change |group_id| to |id|
  90. std::unique_ptr<Instruction> new_inst(
  91. decoration->Clone(module_->context()));
  92. new_inst->SetInOperand(0, {id});
  93. module_->AddAnnotationInst(std::move(new_inst));
  94. auto decoration_iter = --module_->annotation_end();
  95. context->AnalyzeUses(&*decoration_iter);
  96. }
  97. }
  98. }
  99. auto& indirect_decorations = decorations_info.indirect_decorations;
  100. indirect_decorations.erase(
  101. std::remove_if(
  102. indirect_decorations.begin(), indirect_decorations.end(),
  103. [&indirect_decorations_to_remove](const Instruction* inst) {
  104. return indirect_decorations_to_remove.count(inst);
  105. }),
  106. indirect_decorations.end());
  107. for (Instruction* inst : insts_to_kill) context->KillInst(inst);
  108. insts_to_kill.clear();
  109. // Schedule all instructions applying the group for removal if this group no
  110. // longer applies decorations, either directly or indirectly.
  111. if (is_group && decorations_info.direct_decorations.empty() &&
  112. decorations_info.indirect_decorations.empty()) {
  113. for (Instruction* inst : decorations_info.decorate_insts)
  114. insts_to_kill.push_back(inst);
  115. }
  116. for (Instruction* inst : insts_to_kill) context->KillInst(inst);
  117. if (decorations_info.direct_decorations.empty() &&
  118. decorations_info.indirect_decorations.empty() &&
  119. decorations_info.decorate_insts.empty()) {
  120. id_to_decoration_insts_.erase(ids_iter);
  121. // Remove the OpDecorationGroup defining this group.
  122. if (is_group) context->KillInst(context->get_def_use_mgr()->GetDef(id));
  123. }
  124. }
  125. std::vector<Instruction*> DecorationManager::GetDecorationsFor(
  126. uint32_t id, bool include_linkage) {
  127. return InternalGetDecorationsFor<Instruction*>(id, include_linkage);
  128. }
  129. std::vector<const Instruction*> DecorationManager::GetDecorationsFor(
  130. uint32_t id, bool include_linkage) const {
  131. return const_cast<DecorationManager*>(this)
  132. ->InternalGetDecorationsFor<const Instruction*>(id, include_linkage);
  133. }
  134. bool DecorationManager::HaveTheSameDecorations(uint32_t id1,
  135. uint32_t id2) const {
  136. using InstructionList = std::vector<const Instruction*>;
  137. using DecorationSet = std::set<std::u32string>;
  138. const InstructionList decorations_for1 = GetDecorationsFor(id1, false);
  139. const InstructionList decorations_for2 = GetDecorationsFor(id2, false);
  140. // This function splits the decoration instructions into different sets,
  141. // based on their opcode; only OpDecorate, OpDecorateId,
  142. // OpDecorateStringGOOGLE, and OpMemberDecorate are considered, the other
  143. // opcodes are ignored.
  144. const auto fillDecorationSets =
  145. [](const InstructionList& decoration_list, DecorationSet* decorate_set,
  146. DecorationSet* decorate_id_set, DecorationSet* decorate_string_set,
  147. DecorationSet* member_decorate_set) {
  148. for (const Instruction* inst : decoration_list) {
  149. std::u32string decoration_payload;
  150. // Ignore the opcode and the target as we do not want them to be
  151. // compared.
  152. for (uint32_t i = 1u; i < inst->NumInOperands(); ++i) {
  153. for (uint32_t word : inst->GetInOperand(i).words) {
  154. decoration_payload.push_back(word);
  155. }
  156. }
  157. switch (inst->opcode()) {
  158. case SpvOpDecorate:
  159. decorate_set->emplace(std::move(decoration_payload));
  160. break;
  161. case SpvOpMemberDecorate:
  162. member_decorate_set->emplace(std::move(decoration_payload));
  163. break;
  164. case SpvOpDecorateId:
  165. decorate_id_set->emplace(std::move(decoration_payload));
  166. break;
  167. case SpvOpDecorateStringGOOGLE:
  168. decorate_string_set->emplace(std::move(decoration_payload));
  169. break;
  170. default:
  171. break;
  172. }
  173. }
  174. };
  175. DecorationSet decorate_set_for1;
  176. DecorationSet decorate_id_set_for1;
  177. DecorationSet decorate_string_set_for1;
  178. DecorationSet member_decorate_set_for1;
  179. fillDecorationSets(decorations_for1, &decorate_set_for1,
  180. &decorate_id_set_for1, &decorate_string_set_for1,
  181. &member_decorate_set_for1);
  182. DecorationSet decorate_set_for2;
  183. DecorationSet decorate_id_set_for2;
  184. DecorationSet decorate_string_set_for2;
  185. DecorationSet member_decorate_set_for2;
  186. fillDecorationSets(decorations_for2, &decorate_set_for2,
  187. &decorate_id_set_for2, &decorate_string_set_for2,
  188. &member_decorate_set_for2);
  189. const bool result = decorate_set_for1 == decorate_set_for2 &&
  190. decorate_id_set_for1 == decorate_id_set_for2 &&
  191. member_decorate_set_for1 == member_decorate_set_for2 &&
  192. // Compare string sets last in case the strings are long.
  193. decorate_string_set_for1 == decorate_string_set_for2;
  194. return result;
  195. }
  196. // TODO(pierremoreau): If OpDecorateId is referencing an OpConstant, one could
  197. // check that the constants are the same rather than just
  198. // looking at the constant ID.
  199. bool DecorationManager::AreDecorationsTheSame(const Instruction* inst1,
  200. const Instruction* inst2,
  201. bool ignore_target) const {
  202. switch (inst1->opcode()) {
  203. case SpvOpDecorate:
  204. case SpvOpMemberDecorate:
  205. case SpvOpDecorateId:
  206. case SpvOpDecorateStringGOOGLE:
  207. break;
  208. default:
  209. return false;
  210. }
  211. if (inst1->opcode() != inst2->opcode() ||
  212. inst1->NumInOperands() != inst2->NumInOperands())
  213. return false;
  214. for (uint32_t i = ignore_target ? 1u : 0u; i < inst1->NumInOperands(); ++i)
  215. if (inst1->GetInOperand(i) != inst2->GetInOperand(i)) return false;
  216. return true;
  217. }
  218. void DecorationManager::AnalyzeDecorations() {
  219. if (!module_) return;
  220. // For each group and instruction, collect all their decoration instructions.
  221. for (Instruction& inst : module_->annotations()) {
  222. AddDecoration(&inst);
  223. }
  224. }
  225. void DecorationManager::AddDecoration(Instruction* inst) {
  226. switch (inst->opcode()) {
  227. case SpvOpDecorate:
  228. case SpvOpDecorateId:
  229. case SpvOpDecorateStringGOOGLE:
  230. case SpvOpMemberDecorate: {
  231. const auto target_id = inst->GetSingleWordInOperand(0u);
  232. id_to_decoration_insts_[target_id].direct_decorations.push_back(inst);
  233. break;
  234. }
  235. case SpvOpGroupDecorate:
  236. case SpvOpGroupMemberDecorate: {
  237. const uint32_t start = inst->opcode() == SpvOpGroupDecorate ? 1u : 2u;
  238. const uint32_t stride = start;
  239. for (uint32_t i = start; i < inst->NumInOperands(); i += stride) {
  240. const auto target_id = inst->GetSingleWordInOperand(i);
  241. TargetData& target_data = id_to_decoration_insts_[target_id];
  242. target_data.indirect_decorations.push_back(inst);
  243. }
  244. const auto target_id = inst->GetSingleWordInOperand(0u);
  245. id_to_decoration_insts_[target_id].decorate_insts.push_back(inst);
  246. break;
  247. }
  248. default:
  249. break;
  250. }
  251. }
  252. template <typename T>
  253. std::vector<T> DecorationManager::InternalGetDecorationsFor(
  254. uint32_t id, bool include_linkage) {
  255. std::vector<T> decorations;
  256. const auto ids_iter = id_to_decoration_insts_.find(id);
  257. // |id| has no decorations
  258. if (ids_iter == id_to_decoration_insts_.end()) return decorations;
  259. const TargetData& target_data = ids_iter->second;
  260. const auto process_direct_decorations =
  261. [include_linkage,
  262. &decorations](const std::vector<Instruction*>& direct_decorations) {
  263. for (Instruction* inst : direct_decorations) {
  264. const bool is_linkage = inst->opcode() == SpvOpDecorate &&
  265. inst->GetSingleWordInOperand(1u) ==
  266. SpvDecorationLinkageAttributes;
  267. if (include_linkage || !is_linkage) decorations.push_back(inst);
  268. }
  269. };
  270. // Process |id|'s decorations.
  271. process_direct_decorations(ids_iter->second.direct_decorations);
  272. // Process the decorations of all groups applied to |id|.
  273. for (const Instruction* inst : target_data.indirect_decorations) {
  274. const uint32_t group_id = inst->GetSingleWordInOperand(0u);
  275. const auto group_iter = id_to_decoration_insts_.find(group_id);
  276. assert(group_iter != id_to_decoration_insts_.end() && "Unknown group ID");
  277. process_direct_decorations(group_iter->second.direct_decorations);
  278. }
  279. return decorations;
  280. }
  281. bool DecorationManager::WhileEachDecoration(
  282. uint32_t id, uint32_t decoration,
  283. std::function<bool(const Instruction&)> f) {
  284. for (const Instruction* inst : GetDecorationsFor(id, true)) {
  285. switch (inst->opcode()) {
  286. case SpvOpMemberDecorate:
  287. if (inst->GetSingleWordInOperand(2) == decoration) {
  288. if (!f(*inst)) return false;
  289. }
  290. break;
  291. case SpvOpDecorate:
  292. case SpvOpDecorateId:
  293. case SpvOpDecorateStringGOOGLE:
  294. if (inst->GetSingleWordInOperand(1) == decoration) {
  295. if (!f(*inst)) return false;
  296. }
  297. break;
  298. default:
  299. assert(false && "Unexpected decoration instruction");
  300. }
  301. }
  302. return true;
  303. }
  304. void DecorationManager::ForEachDecoration(
  305. uint32_t id, uint32_t decoration,
  306. std::function<void(const Instruction&)> f) {
  307. WhileEachDecoration(id, decoration, [&f](const Instruction& inst) {
  308. f(inst);
  309. return true;
  310. });
  311. }
  312. void DecorationManager::CloneDecorations(uint32_t from, uint32_t to) {
  313. const auto decoration_list = id_to_decoration_insts_.find(from);
  314. if (decoration_list == id_to_decoration_insts_.end()) return;
  315. auto context = module_->context();
  316. for (Instruction* inst : decoration_list->second.direct_decorations) {
  317. // simply clone decoration and change |target-id| to |to|
  318. std::unique_ptr<Instruction> new_inst(inst->Clone(module_->context()));
  319. new_inst->SetInOperand(0, {to});
  320. module_->AddAnnotationInst(std::move(new_inst));
  321. auto decoration_iter = --module_->annotation_end();
  322. context->AnalyzeUses(&*decoration_iter);
  323. }
  324. // We need to copy the list of instructions as ForgetUses and AnalyzeUses are
  325. // going to modify it.
  326. std::vector<Instruction*> indirect_decorations =
  327. decoration_list->second.indirect_decorations;
  328. for (Instruction* inst : indirect_decorations) {
  329. switch (inst->opcode()) {
  330. case SpvOpGroupDecorate:
  331. context->ForgetUses(inst);
  332. // add |to| to list of decorated id's
  333. inst->AddOperand(
  334. Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {to}));
  335. context->AnalyzeUses(inst);
  336. break;
  337. case SpvOpGroupMemberDecorate: {
  338. context->ForgetUses(inst);
  339. // for each (id == from), add (to, literal) as operands
  340. const uint32_t num_operands = inst->NumOperands();
  341. for (uint32_t i = 1; i < num_operands; i += 2) {
  342. Operand op = inst->GetOperand(i);
  343. if (op.words[0] == from) { // add new pair of operands: (to, literal)
  344. inst->AddOperand(
  345. Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {to}));
  346. op = inst->GetOperand(i + 1);
  347. inst->AddOperand(std::move(op));
  348. }
  349. }
  350. context->AnalyzeUses(inst);
  351. break;
  352. }
  353. default:
  354. assert(false && "Unexpected decoration instruction");
  355. }
  356. }
  357. }
  358. void DecorationManager::CloneDecorations(
  359. uint32_t from, uint32_t to,
  360. const std::vector<SpvDecoration>& decorations_to_copy) {
  361. const auto decoration_list = id_to_decoration_insts_.find(from);
  362. if (decoration_list == id_to_decoration_insts_.end()) return;
  363. auto context = module_->context();
  364. for (Instruction* inst : decoration_list->second.direct_decorations) {
  365. if (std::find(decorations_to_copy.begin(), decorations_to_copy.end(),
  366. inst->GetSingleWordInOperand(1)) ==
  367. decorations_to_copy.end()) {
  368. continue;
  369. }
  370. // Clone decoration and change |target-id| to |to|.
  371. std::unique_ptr<Instruction> new_inst(inst->Clone(module_->context()));
  372. new_inst->SetInOperand(0, {to});
  373. module_->AddAnnotationInst(std::move(new_inst));
  374. auto decoration_iter = --module_->annotation_end();
  375. context->AnalyzeUses(&*decoration_iter);
  376. }
  377. // We need to copy the list of instructions as ForgetUses and AnalyzeUses are
  378. // going to modify it.
  379. std::vector<Instruction*> indirect_decorations =
  380. decoration_list->second.indirect_decorations;
  381. for (Instruction* inst : indirect_decorations) {
  382. switch (inst->opcode()) {
  383. case SpvOpGroupDecorate:
  384. CloneDecorations(inst->GetSingleWordInOperand(0), to,
  385. decorations_to_copy);
  386. break;
  387. case SpvOpGroupMemberDecorate: {
  388. assert(false && "The source id is not suppose to be a type.");
  389. break;
  390. }
  391. default:
  392. assert(false && "Unexpected decoration instruction");
  393. }
  394. }
  395. }
  396. void DecorationManager::RemoveDecoration(Instruction* inst) {
  397. const auto remove_from_container = [inst](std::vector<Instruction*>& v) {
  398. v.erase(std::remove(v.begin(), v.end(), inst), v.end());
  399. };
  400. switch (inst->opcode()) {
  401. case SpvOpDecorate:
  402. case SpvOpDecorateId:
  403. case SpvOpDecorateStringGOOGLE:
  404. case SpvOpMemberDecorate: {
  405. const auto target_id = inst->GetSingleWordInOperand(0u);
  406. auto const iter = id_to_decoration_insts_.find(target_id);
  407. if (iter == id_to_decoration_insts_.end()) return;
  408. remove_from_container(iter->second.direct_decorations);
  409. } break;
  410. case SpvOpGroupDecorate:
  411. case SpvOpGroupMemberDecorate: {
  412. const uint32_t stride = inst->opcode() == SpvOpGroupDecorate ? 1u : 2u;
  413. for (uint32_t i = 1u; i < inst->NumInOperands(); i += stride) {
  414. const auto target_id = inst->GetSingleWordInOperand(i);
  415. auto const iter = id_to_decoration_insts_.find(target_id);
  416. if (iter == id_to_decoration_insts_.end()) continue;
  417. remove_from_container(iter->second.indirect_decorations);
  418. }
  419. const auto group_id = inst->GetSingleWordInOperand(0u);
  420. auto const iter = id_to_decoration_insts_.find(group_id);
  421. if (iter == id_to_decoration_insts_.end()) return;
  422. remove_from_container(iter->second.decorate_insts);
  423. } break;
  424. default:
  425. break;
  426. }
  427. }
  428. } // namespace analysis
  429. } // namespace opt
  430. } // namespace spvtools