1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 10:48:11 +00:00

LibJS+CI: Remove bytecode optimization passes for now

These passes have not been shown to actually optimize any JS, and tests
have become very flaky with optimizations enabled. Until some measurable
benefit is shown, remove the optimization passes to reduce overhead of
maintaining bytecode operations and to reduce CI churn. The framework
for optimizations will live on in git history, and can be restored once
proven useful.
This commit is contained in:
Timothy Flynn 2023-07-21 09:59:50 -04:00 committed by Ali Mohammad Pur
parent 164c132928
commit 77d7f715e3
17 changed files with 1 additions and 1311 deletions

View file

@ -1,185 +0,0 @@
/*
* 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();
}
}