summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibJS/Bytecode/Pass/MergeBlocks.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Libraries/LibJS/Bytecode/Pass/MergeBlocks.cpp')
-rw-r--r--Userland/Libraries/LibJS/Bytecode/Pass/MergeBlocks.cpp165
1 files changed, 165 insertions, 0 deletions
diff --git a/Userland/Libraries/LibJS/Bytecode/Pass/MergeBlocks.cpp b/Userland/Libraries/LibJS/Bytecode/Pass/MergeBlocks.cpp
new file mode 100644
index 0000000000..a3b00857b9
--- /dev/null
+++ b/Userland/Libraries/LibJS/Bytecode/Pass/MergeBlocks.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibJS/Bytecode/PassManager.h>
+
+namespace JS::Bytecode::Passes {
+
+void MergeBlocks::perform(PassPipelineExecutable& executable)
+{
+ started();
+
+ VERIFY(executable.cfg.has_value());
+ VERIFY(executable.inverted_cfg.has_value());
+ auto cfg = executable.cfg.release_value();
+ auto inverted_cfg = executable.inverted_cfg.release_value();
+
+ // Figure out which blocks can be merged
+ HashTable<BasicBlock const*> blocks_to_merge;
+ HashMap<BasicBlock const*, BasicBlock const*> blocks_to_replace;
+ Vector<BasicBlock const*> blocks_to_remove;
+ Vector<size_t> boundaries;
+
+ for (auto& entry : cfg) {
+ if (entry.value.size() != 1)
+ continue;
+
+ if (executable.exported_blocks->contains(*entry.value.begin()))
+ continue;
+
+ {
+ InstructionStreamIterator it { entry.key->instruction_stream() };
+ auto& first_instruction = *it;
+ if (first_instruction.is_terminator()) {
+ if (first_instruction.type() == Instruction::Type::Jump) {
+ auto replacing_block = &static_cast<Op::Jump const&>(first_instruction).true_target()->block();
+ if (replacing_block != entry.key)
+ blocks_to_replace.set(entry.key, replacing_block);
+ continue;
+ }
+ }
+ }
+
+ if (auto cfg_entry = inverted_cfg.get(*entry.value.begin()); cfg_entry.has_value()) {
+ auto& predecssor_entry = *cfg_entry;
+ if (predecssor_entry.size() != 1)
+ continue;
+ }
+
+ // The two blocks are safe to merge.
+ blocks_to_merge.set(entry.key);
+ }
+
+ for (auto& entry : blocks_to_replace) {
+ auto replacement = entry.value;
+ for (;;) {
+ auto lookup = blocks_to_replace.get(replacement);
+ if (!lookup.has_value())
+ break;
+ if (replacement == *lookup)
+ break;
+ replacement = *lookup;
+ }
+ entry.value = replacement;
+ }
+
+ auto replace_blocks = [&](auto& blocks, auto& replacement) {
+ Optional<size_t> first_successor_position;
+ for (auto& entry : blocks) {
+ blocks_to_remove.append(entry);
+ auto it = executable.executable.basic_blocks.find_if([entry](auto& block) { return entry == block; });
+ VERIFY(!it.is_end());
+ if (!first_successor_position.has_value())
+ first_successor_position = it.index();
+ }
+ for (auto& block : executable.executable.basic_blocks) {
+ InstructionStreamIterator it { block.instruction_stream() };
+ while (!it.at_end()) {
+ auto& instruction = *it;
+ ++it;
+ for (auto& entry : blocks)
+ const_cast<Instruction&>(instruction).replace_references(*entry, replacement);
+ }
+ }
+ return first_successor_position;
+ };
+
+ for (auto& entry : blocks_to_replace) {
+ AK::Array candidates { entry.key };
+ (void)replace_blocks(candidates, *entry.value);
+ }
+
+ while (!blocks_to_merge.is_empty()) {
+ auto it = blocks_to_merge.begin();
+ auto current_block = *it;
+ blocks_to_merge.remove(it);
+ Vector<BasicBlock const*> successors { current_block };
+ for (;;) {
+ auto last = successors.last();
+ auto entry = cfg.get(last);
+ if (!entry.has_value())
+ break;
+ auto& successor = *entry->begin();
+ successors.append(successor);
+ auto it = blocks_to_merge.find(successor);
+ if (it == blocks_to_merge.end())
+ break;
+ blocks_to_merge.remove(it);
+ }
+
+ auto blocks_to_merge_copy = blocks_to_merge;
+ for (auto& last : blocks_to_merge) {
+ auto entry = cfg.get(last);
+ if (!entry.has_value())
+ continue;
+ auto successor = *entry->begin();
+ if (auto it = successors.find(successor); !it.is_end()) {
+ successors.insert(it.index(), last);
+ blocks_to_merge_copy.remove(last);
+ }
+ }
+
+ blocks_to_merge = move(blocks_to_merge_copy);
+
+ size_t size = 0;
+ StringBuilder builder;
+ builder.append("merge");
+ for (auto& entry : successors) {
+ size += entry->size();
+ builder.append('.');
+ builder.append(entry->name());
+ }
+
+ auto new_block = BasicBlock::create(builder.build(), size);
+ auto& block = *new_block;
+
+ size_t last_successor_index = successors.size() - 1;
+ for (size_t i = 0; i < successors.size(); ++i) {
+ auto& entry = successors[i];
+ InstructionStreamIterator it { entry->instruction_stream() };
+ size_t copy_end = 0;
+ while (!it.at_end()) {
+ auto& instruction = *it;
+ ++it;
+ if (instruction.is_terminator() && last_successor_index != i)
+ break;
+ copy_end = it.offset();
+ }
+ __builtin_memcpy(block.next_slot(), entry->instruction_stream().data(), copy_end);
+ block.grow(copy_end);
+ }
+
+ auto first_successor_position = replace_blocks(successors, *new_block);
+ VERIFY(first_successor_position.has_value());
+ executable.executable.basic_blocks.insert(*first_successor_position, move(new_block));
+ }
+
+ executable.executable.basic_blocks.remove_all_matching([&blocks_to_remove](auto& candidate) { return blocks_to_remove.contains_slow(candidate.ptr()); });
+
+ finished();
+}
+
+}