ladybird/Userland/Libraries/LibJS/Bytecode/Pass/MergeBlocks.cpp
sin-ack 3f3f45580a Everywhere: Add sv suffix to strings relying on StringView(char const*)
Each of these strings would previously rely on StringView's char const*
constructor overload, which would call __builtin_strlen on the string.
Since we now have operator ""sv, we can replace these with much simpler
versions. This opens the door to being able to remove
StringView(char const*).

No functional changes.
2022-07-12 23:11:35 +02:00

165 lines
5.7 KiB
C++

/*
* 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_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 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.find(last);
if (entry == cfg.end())
break;
auto& successor = *entry->value.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.find(last);
if (entry == cfg.end())
continue;
auto successor = *entry->value.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"sv);
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;
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() };
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);
}
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();
}
}