MergeBlocks.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. /*
  2. * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibJS/Bytecode/PassManager.h>
  7. namespace JS::Bytecode::Passes {
  8. void MergeBlocks::perform(PassPipelineExecutable& executable)
  9. {
  10. started();
  11. VERIFY(executable.cfg.has_value());
  12. VERIFY(executable.inverted_cfg.has_value());
  13. auto cfg = executable.cfg.release_value();
  14. auto inverted_cfg = executable.inverted_cfg.release_value();
  15. // Figure out which blocks can be merged
  16. HashTable<BasicBlock const*> blocks_to_merge;
  17. HashMap<BasicBlock const*, BasicBlock const*> blocks_to_replace;
  18. Vector<BasicBlock const*> blocks_to_remove;
  19. Vector<size_t> boundaries;
  20. for (auto& entry : cfg) {
  21. if (entry.value.size() != 1)
  22. continue;
  23. if (executable.exported_blocks->contains(*entry.value.begin()))
  24. continue;
  25. if (!entry.key->is_terminated())
  26. continue;
  27. if (entry.key->terminator()->type() != Instruction::Type::Jump)
  28. continue;
  29. {
  30. InstructionStreamIterator it { entry.key->instruction_stream() };
  31. auto& first_instruction = *it;
  32. if (first_instruction.type() == Instruction::Type::Jump) {
  33. auto const* replacing_block = &static_cast<Op::Jump const&>(first_instruction).true_target()->block();
  34. if (replacing_block != entry.key) {
  35. blocks_to_replace.set(entry.key, replacing_block);
  36. }
  37. continue;
  38. }
  39. }
  40. if (auto cfg_iter = inverted_cfg.find(*entry.value.begin()); cfg_iter != inverted_cfg.end()) {
  41. auto& predecessor_entry = cfg_iter->value;
  42. if (predecessor_entry.size() != 1)
  43. continue;
  44. }
  45. // The two blocks are safe to merge.
  46. blocks_to_merge.set(entry.key);
  47. }
  48. for (auto& entry : blocks_to_replace) {
  49. auto const* replacement = entry.value;
  50. for (;;) {
  51. auto lookup = blocks_to_replace.get(replacement);
  52. if (!lookup.has_value())
  53. break;
  54. if (replacement == *lookup)
  55. break;
  56. replacement = *lookup;
  57. }
  58. entry.value = replacement;
  59. }
  60. auto replace_blocks = [&](auto& blocks, auto& replacement) {
  61. Optional<size_t> first_successor_position;
  62. for (auto& entry : blocks) {
  63. blocks_to_remove.append(entry);
  64. auto it = executable.executable.basic_blocks.find_if([entry](auto& block) { return entry == block; });
  65. VERIFY(!it.is_end());
  66. if (!first_successor_position.has_value())
  67. first_successor_position = it.index();
  68. }
  69. for (auto& block : executable.executable.basic_blocks) {
  70. InstructionStreamIterator it { block->instruction_stream() };
  71. while (!it.at_end()) {
  72. auto& instruction = *it;
  73. ++it;
  74. for (auto& entry : blocks)
  75. const_cast<Instruction&>(instruction).replace_references(*entry, replacement);
  76. }
  77. }
  78. return first_successor_position;
  79. };
  80. for (auto& entry : blocks_to_replace) {
  81. AK::Array candidates { entry.key };
  82. (void)replace_blocks(candidates, *entry.value);
  83. }
  84. while (!blocks_to_merge.is_empty()) {
  85. auto it = blocks_to_merge.begin();
  86. auto const* current_block = *it;
  87. blocks_to_merge.remove(it);
  88. Vector<BasicBlock const*> successors { current_block };
  89. for (;;) {
  90. auto const* last = successors.last();
  91. auto entry = cfg.find(last);
  92. if (entry == cfg.end())
  93. break;
  94. auto const* successor = *entry->value.begin();
  95. successors.append(successor);
  96. if (!blocks_to_merge.remove(successor))
  97. break;
  98. }
  99. auto blocks_to_merge_copy = blocks_to_merge;
  100. // We need to do the following multiple times, due to it not being
  101. // guaranteed, that the blocks are in sequential order
  102. bool did_prepend = true;
  103. while (did_prepend) {
  104. did_prepend = false;
  105. for (auto const* last : blocks_to_merge) {
  106. auto entry = cfg.find(last);
  107. if (entry == cfg.end())
  108. continue;
  109. auto const* successor = *entry->value.begin();
  110. if (successor == successors.first()) {
  111. successors.prepend(last);
  112. blocks_to_merge_copy.remove(last);
  113. did_prepend = true;
  114. }
  115. }
  116. }
  117. blocks_to_merge = move(blocks_to_merge_copy);
  118. size_t size = 0;
  119. StringBuilder builder;
  120. builder.append("merge"sv);
  121. for (auto& entry : successors) {
  122. size += entry->size();
  123. builder.append('.');
  124. builder.append(entry->name());
  125. }
  126. auto new_block = BasicBlock::create(builder.to_deprecated_string(), size);
  127. auto& block = *new_block;
  128. auto first_successor_position = replace_blocks(successors, *new_block);
  129. VERIFY(first_successor_position.has_value());
  130. size_t last_successor_index = successors.size() - 1;
  131. for (size_t i = 0; i < successors.size(); ++i) {
  132. auto& entry = successors[i];
  133. InstructionStreamIterator it { entry->instruction_stream() };
  134. while (!it.at_end()) {
  135. auto& instruction = *it;
  136. ++it;
  137. if (instruction.is_terminator() && last_successor_index != i)
  138. break;
  139. // FIXME: Op::NewBigInt is not trivially copyable, so we cant use
  140. // a simple memcpy to transfer them.
  141. // When this is resolved we can use a single memcpy to copy
  142. // the whole block at once
  143. if (instruction.type() == Instruction::Type::NewBigInt) {
  144. new (block.next_slot()) Op::NewBigInt(static_cast<Op::NewBigInt const&>(instruction));
  145. block.grow(sizeof(Op::NewBigInt));
  146. } else {
  147. auto instruction_size = instruction.length();
  148. memcpy(block.next_slot(), &instruction, instruction_size);
  149. block.grow(instruction_size);
  150. }
  151. }
  152. }
  153. executable.executable.basic_blocks.insert(*first_successor_position, move(new_block));
  154. }
  155. executable.executable.basic_blocks.remove_all_matching([&blocks_to_remove](auto& candidate) { return blocks_to_remove.contains_slow(candidate.ptr()); });
  156. finished();
  157. }
  158. }