mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-26 06:18:59 +00:00
This is essentially a LeaveUnwind+Jump, so lets just do that, that will make it easier to optimize it, or see unwind state transitions
104 lines
3.9 KiB
C++
104 lines
3.9 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 GenerateCFG::perform(PassPipelineExecutable& executable)
|
|
{
|
|
started();
|
|
|
|
executable.cfg = HashMap<BasicBlock const*, HashTable<BasicBlock const*>> {};
|
|
executable.inverted_cfg = HashMap<BasicBlock const*, HashTable<BasicBlock const*>> {};
|
|
executable.exported_blocks = HashTable<BasicBlock const*> {};
|
|
Vector<InstructionStreamIterator> iterators;
|
|
Vector<BasicBlock const&> entered_blocks;
|
|
HashTable<BasicBlock const*> seen_blocks;
|
|
|
|
auto enter_label = [&](auto const& label, auto& entering_block, bool exported = false) {
|
|
auto& entry = executable.cfg->ensure(&entering_block);
|
|
entry.set(&label->block());
|
|
auto& inverse_entry = executable.inverted_cfg->ensure(&label->block());
|
|
inverse_entry.set(&entering_block);
|
|
|
|
if (exported)
|
|
executable.exported_blocks->set(&label->block());
|
|
|
|
if (!seen_blocks.contains(&label->block())) {
|
|
seen_blocks.set(&label->block());
|
|
entered_blocks.append(label->block());
|
|
iterators.empend(label->block().instruction_stream());
|
|
}
|
|
};
|
|
|
|
seen_blocks.set(&executable.executable.basic_blocks.first());
|
|
entered_blocks.append(executable.executable.basic_blocks.first());
|
|
iterators.empend(executable.executable.basic_blocks.first().instruction_stream());
|
|
|
|
while (!entered_blocks.is_empty()) {
|
|
if (iterators.last().at_end()) {
|
|
entered_blocks.take_last();
|
|
iterators.take_last();
|
|
continue;
|
|
}
|
|
auto const& instruction = *iterators.last();
|
|
++iterators.last();
|
|
if (!instruction.is_terminator())
|
|
continue;
|
|
|
|
auto const& current_block = entered_blocks.last();
|
|
|
|
using enum Instruction::Type;
|
|
switch (instruction.type()) {
|
|
case Jump: {
|
|
auto const& true_target = static_cast<Op::Jump const&>(instruction).true_target();
|
|
enter_label(true_target, current_block);
|
|
continue;
|
|
}
|
|
case JumpConditional:
|
|
case JumpNullish:
|
|
case JumpUndefined: {
|
|
auto const& true_target = static_cast<Op::Jump const&>(instruction).true_target();
|
|
enter_label(true_target, current_block);
|
|
auto const& false_target = static_cast<Op::Jump const&>(instruction).false_target();
|
|
enter_label(false_target, current_block);
|
|
continue;
|
|
}
|
|
case Yield: {
|
|
auto const& continuation = static_cast<Op::Yield const&>(instruction).continuation();
|
|
if (continuation.has_value())
|
|
enter_label(continuation, current_block, true);
|
|
continue;
|
|
}
|
|
case EnterUnwindContext: {
|
|
auto const& entry_point = static_cast<Op::EnterUnwindContext const&>(instruction).entry_point();
|
|
auto const& handler_target = static_cast<Op::EnterUnwindContext const&>(instruction).handler_target();
|
|
auto const& finalizer_target = static_cast<Op::EnterUnwindContext const&>(instruction).finalizer_target();
|
|
enter_label(&entry_point, current_block);
|
|
if (handler_target.has_value())
|
|
enter_label(handler_target, current_block);
|
|
if (finalizer_target.has_value())
|
|
enter_label(finalizer_target, current_block);
|
|
continue;
|
|
}
|
|
case ContinuePendingUnwind: {
|
|
auto const& resume_target = static_cast<Op::ContinuePendingUnwind const&>(instruction).resume_target();
|
|
enter_label(&resume_target, current_block);
|
|
continue;
|
|
}
|
|
default:
|
|
// Otherwise, pop the current block off, it doesn't jump anywhere.
|
|
iterators.take_last();
|
|
entered_blocks.take_last();
|
|
continue;
|
|
}
|
|
}
|
|
|
|
finished();
|
|
}
|
|
|
|
}
|