123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- /*
- * 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;
- if (!entry.key->is_terminated())
- continue;
- if (entry.key->terminator()->type() != Instruction::Type::Jump)
- continue;
- {
- InstructionStreamIterator it { entry.key->instruction_stream() };
- auto& first_instruction = *it;
- if (first_instruction.type() == Instruction::Type::Jump) {
- auto const* 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_iter = inverted_cfg.find(*entry.value.begin()); cfg_iter != inverted_cfg.end()) {
- auto& predecessor_entry = cfg_iter->value;
- if (predecessor_entry.size() != 1)
- continue;
- }
- // The two blocks are safe to merge.
- blocks_to_merge.set(entry.key);
- }
- for (auto& entry : blocks_to_replace) {
- auto const* 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 const* current_block = *it;
- blocks_to_merge.remove(it);
- Vector<BasicBlock const*> successors { current_block };
- for (;;) {
- auto const* last = successors.last();
- auto entry = cfg.find(last);
- if (entry == cfg.end())
- break;
- auto const* successor = *entry->value.begin();
- successors.append(successor);
- if (!blocks_to_merge.remove(successor))
- break;
- }
- auto blocks_to_merge_copy = blocks_to_merge;
- // We need to do the following multiple times, due to it not being
- // guaranteed, that the blocks are in sequential order
- bool did_prepend = true;
- while (did_prepend) {
- did_prepend = false;
- for (auto const* last : blocks_to_merge) {
- auto entry = cfg.find(last);
- if (entry == cfg.end())
- continue;
- auto const* successor = *entry->value.begin();
- if (successor == successors.first()) {
- successors.prepend(last);
- blocks_to_merge_copy.remove(last);
- did_prepend = true;
- }
- }
- }
- blocks_to_merge = move(blocks_to_merge_copy);
- size_t size = 0;
- StringBuilder builder;
- builder.append("merge"sv);
- for (auto& entry : successors) {
- size += entry->size();
- builder.append('.');
- builder.append(entry->name());
- }
- auto new_block = BasicBlock::create(builder.to_deprecated_string(), size);
- auto& block = *new_block;
- auto first_successor_position = replace_blocks(successors, *new_block);
- VERIFY(first_successor_position.has_value());
- 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() };
- while (!it.at_end()) {
- auto& instruction = *it;
- ++it;
- if (instruction.is_terminator() && last_successor_index != i)
- break;
- // FIXME: Op::NewBigInt is not trivially copyable, so we cant use
- // a simple memcpy to transfer them.
- // When this is resolved we can use a single memcpy to copy
- // the whole block at once
- if (instruction.type() == Instruction::Type::NewBigInt) {
- new (block.next_slot()) Op::NewBigInt(static_cast<Op::NewBigInt const&>(instruction));
- block.grow(sizeof(Op::NewBigInt));
- } else {
- auto instruction_size = instruction.length();
- memcpy(block.next_slot(), &instruction, instruction_size);
- block.grow(instruction_size);
- }
- }
- }
- 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();
- }
- }
|