decoration_manager.cpp 20 KB

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