mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 18:07:34 +00:00
JSSpecCompiler: Add control flow graph simplification pass
It removes empty `BasicBlock`s with an unconditional jump continuation and then removes unreferenced blocks from the graph.
This commit is contained in:
parent
475ef6965a
commit
12072dbac5
7 changed files with 215 additions and 1 deletions
|
@ -31,6 +31,16 @@ void NodeSubtreePointer::replace_subtree(Badge<RecursiveASTVisitor>, NullableTre
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector<BasicBlockRef*> ControlFlowJump::references()
|
||||||
|
{
|
||||||
|
return { &m_block };
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<BasicBlockRef*> ControlFlowBranch::references()
|
||||||
|
{
|
||||||
|
return { &m_then, &m_else };
|
||||||
|
}
|
||||||
|
|
||||||
Vector<NodeSubtreePointer> BinaryOperation::subtrees()
|
Vector<NodeSubtreePointer> BinaryOperation::subtrees()
|
||||||
{
|
{
|
||||||
return { { &m_left }, { &m_right } };
|
return { { &m_left }, { &m_right } };
|
||||||
|
|
|
@ -80,7 +80,11 @@ protected:
|
||||||
// ```.
|
// ```.
|
||||||
class Statement : public Node { };
|
class Statement : public Node { };
|
||||||
class Expression : public Node { };
|
class Expression : public Node { };
|
||||||
class ControlFlowOperator : public Statement { };
|
|
||||||
|
class ControlFlowOperator : public Statement {
|
||||||
|
public:
|
||||||
|
virtual Vector<BasicBlockRef*> references() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class ErrorNode : public Expression {
|
class ErrorNode : public Expression {
|
||||||
public:
|
public:
|
||||||
|
@ -106,6 +110,8 @@ public:
|
||||||
|
|
||||||
VariableRef m_return_value;
|
VariableRef m_return_value;
|
||||||
|
|
||||||
|
Vector<BasicBlockRef*> references() override { return {}; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void dump_tree(StringBuilder& builder) override;
|
void dump_tree(StringBuilder& builder) override;
|
||||||
};
|
};
|
||||||
|
@ -117,6 +123,8 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector<BasicBlockRef*> references() override;
|
||||||
|
|
||||||
BasicBlockRef m_block;
|
BasicBlockRef m_block;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -135,6 +143,8 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector<BasicBlockRef*> references() override;
|
||||||
|
|
||||||
Tree m_condition;
|
Tree m_condition;
|
||||||
BasicBlockRef m_then;
|
BasicBlockRef m_then;
|
||||||
BasicBlockRef m_else;
|
BasicBlockRef m_else;
|
||||||
|
|
|
@ -5,6 +5,7 @@ set(SOURCES
|
||||||
Compiler/ControlFlowGraph.cpp
|
Compiler/ControlFlowGraph.cpp
|
||||||
Compiler/GenericASTPass.cpp
|
Compiler/GenericASTPass.cpp
|
||||||
Compiler/Passes/CFGBuildingPass.cpp
|
Compiler/Passes/CFGBuildingPass.cpp
|
||||||
|
Compiler/Passes/CFGSimplificationPass.cpp
|
||||||
Compiler/Passes/FunctionCallCanonicalizationPass.cpp
|
Compiler/Passes/FunctionCallCanonicalizationPass.cpp
|
||||||
Compiler/Passes/IfBranchMergingPass.cpp
|
Compiler/Passes/IfBranchMergingPass.cpp
|
||||||
Compiler/Passes/ReferenceResolvingPass.cpp
|
Compiler/Passes/ReferenceResolvingPass.cpp
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Dan Klishch <danilklishch@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Compiler/Passes/CFGSimplificationPass.h"
|
||||||
|
#include "AST/AST.h"
|
||||||
|
#include "Function.h"
|
||||||
|
|
||||||
|
namespace JSSpecCompiler {
|
||||||
|
|
||||||
|
void CFGSimplificationPass::process_function()
|
||||||
|
{
|
||||||
|
auto& graph = *m_function->m_cfg;
|
||||||
|
|
||||||
|
m_replacement.clear();
|
||||||
|
m_replacement.resize(graph.blocks_count());
|
||||||
|
m_state.clear();
|
||||||
|
m_state.resize(graph.blocks_count());
|
||||||
|
|
||||||
|
for (auto const& block : graph.blocks) {
|
||||||
|
m_replacement[block->m_index] = block;
|
||||||
|
if (block->m_expressions.size() == 0)
|
||||||
|
if (auto jump = as<ControlFlowJump>(block->m_continuation); jump)
|
||||||
|
m_replacement[block->m_index] = jump->m_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < graph.blocks_count(); ++i)
|
||||||
|
if (m_state[i] == State::NotUsed)
|
||||||
|
VERIFY(compute_replacement_block(i));
|
||||||
|
|
||||||
|
// Fixing references
|
||||||
|
graph.start_block = m_replacement[graph.start_block->m_index];
|
||||||
|
for (auto const& block : graph.blocks) {
|
||||||
|
for (auto* next_block : block->m_continuation->references())
|
||||||
|
*next_block = m_replacement[(*next_block)->m_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removing unused nodes
|
||||||
|
m_state.span().fill(State::NotUsed);
|
||||||
|
compute_referenced_blocks(graph.start_block);
|
||||||
|
|
||||||
|
size_t j = 0;
|
||||||
|
for (size_t i = 0; i < graph.blocks_count(); ++i) {
|
||||||
|
if (m_state[graph.blocks[i]->m_index] == State::Used) {
|
||||||
|
graph.blocks[j] = graph.blocks[i];
|
||||||
|
graph.blocks[j]->m_index = j;
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
graph.blocks.shrink(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFGSimplificationPass::compute_replacement_block(size_t i)
|
||||||
|
{
|
||||||
|
if (m_state[i] == State::CurrentlyInside)
|
||||||
|
return false;
|
||||||
|
VERIFY(m_state[i] == State::NotUsed);
|
||||||
|
m_state[i] = State::CurrentlyInside;
|
||||||
|
|
||||||
|
size_t j = m_replacement[i]->m_index;
|
||||||
|
|
||||||
|
if (i == j)
|
||||||
|
return true;
|
||||||
|
if (m_state[j] == State::NotUsed)
|
||||||
|
if (!compute_replacement_block(j))
|
||||||
|
return false;
|
||||||
|
m_replacement[i] = m_replacement[j];
|
||||||
|
|
||||||
|
m_state[i] = State::Used;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFGSimplificationPass::compute_referenced_blocks(BasicBlockRef block)
|
||||||
|
{
|
||||||
|
if (m_state[block->m_index] == State::Used)
|
||||||
|
return;
|
||||||
|
m_state[block->m_index] = State::Used;
|
||||||
|
|
||||||
|
for (auto* next : block->m_continuation->references())
|
||||||
|
compute_referenced_blocks(*next);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Dan Klishch <danilklishch@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Compiler/CompilerPass.h"
|
||||||
|
#include "Compiler/ControlFlowGraph.h"
|
||||||
|
|
||||||
|
namespace JSSpecCompiler {
|
||||||
|
|
||||||
|
// CFGSimplificationPass removes empty `BasicBlock`s with an unconditional jump continuation. It
|
||||||
|
// also removes unreferenced blocks from the graph.
|
||||||
|
class CFGSimplificationPass : public IntraproceduralCompilerPass {
|
||||||
|
public:
|
||||||
|
inline static constexpr StringView name = "cfg-simplification"sv;
|
||||||
|
|
||||||
|
using IntraproceduralCompilerPass::IntraproceduralCompilerPass;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void process_function() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class State : char {
|
||||||
|
NotUsed,
|
||||||
|
CurrentlyInside,
|
||||||
|
Used,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool compute_replacement_block(size_t i);
|
||||||
|
void compute_referenced_blocks(BasicBlockRef block);
|
||||||
|
|
||||||
|
Vector<BasicBlockRef> m_replacement;
|
||||||
|
Vector<State> m_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -182,3 +182,70 @@ BinaryOperation Assignment
|
||||||
Error ""
|
Error ""
|
||||||
ControlFlowJump jump=1
|
ControlFlowJump jump=1
|
||||||
|
|
||||||
|
===== AST after cfg-simplification =====
|
||||||
|
f():
|
||||||
|
TreeList
|
||||||
|
IfElseIfChain
|
||||||
|
UnresolvedReference cond1
|
||||||
|
TreeList
|
||||||
|
BinaryOperation Declaration
|
||||||
|
Var a
|
||||||
|
MathematicalConstant 1
|
||||||
|
IfElseIfChain
|
||||||
|
UnresolvedReference cond2
|
||||||
|
TreeList
|
||||||
|
BinaryOperation Declaration
|
||||||
|
Var b
|
||||||
|
Var a
|
||||||
|
TreeList
|
||||||
|
BinaryOperation Declaration
|
||||||
|
Var b
|
||||||
|
MathematicalConstant 3
|
||||||
|
TreeList
|
||||||
|
BinaryOperation Declaration
|
||||||
|
Var b
|
||||||
|
MathematicalConstant 4
|
||||||
|
ReturnNode
|
||||||
|
Var b
|
||||||
|
|
||||||
|
===== CFG after cfg-simplification =====
|
||||||
|
f():
|
||||||
|
0:
|
||||||
|
ControlFlowBranch true=3 false=6
|
||||||
|
UnresolvedReference cond1
|
||||||
|
|
||||||
|
1:
|
||||||
|
ControlFlowFunctionReturn
|
||||||
|
Var $return
|
||||||
|
|
||||||
|
2:
|
||||||
|
BinaryOperation Assignment
|
||||||
|
Var $return
|
||||||
|
Var b
|
||||||
|
ControlFlowJump jump=1
|
||||||
|
|
||||||
|
3:
|
||||||
|
BinaryOperation Declaration
|
||||||
|
Var a
|
||||||
|
MathematicalConstant 1
|
||||||
|
ControlFlowBranch true=4 false=5
|
||||||
|
UnresolvedReference cond2
|
||||||
|
|
||||||
|
4:
|
||||||
|
BinaryOperation Declaration
|
||||||
|
Var b
|
||||||
|
Var a
|
||||||
|
ControlFlowJump jump=2
|
||||||
|
|
||||||
|
5:
|
||||||
|
BinaryOperation Declaration
|
||||||
|
Var b
|
||||||
|
MathematicalConstant 3
|
||||||
|
ControlFlowJump jump=2
|
||||||
|
|
||||||
|
6:
|
||||||
|
BinaryOperation Declaration
|
||||||
|
Var b
|
||||||
|
MathematicalConstant 4
|
||||||
|
ControlFlowJump jump=2
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <LibMain/Main.h>
|
#include <LibMain/Main.h>
|
||||||
|
|
||||||
#include "Compiler/Passes/CFGBuildingPass.h"
|
#include "Compiler/Passes/CFGBuildingPass.h"
|
||||||
|
#include "Compiler/Passes/CFGSimplificationPass.h"
|
||||||
#include "Compiler/Passes/FunctionCallCanonicalizationPass.h"
|
#include "Compiler/Passes/FunctionCallCanonicalizationPass.h"
|
||||||
#include "Compiler/Passes/IfBranchMergingPass.h"
|
#include "Compiler/Passes/IfBranchMergingPass.h"
|
||||||
#include "Compiler/Passes/ReferenceResolvingPass.h"
|
#include "Compiler/Passes/ReferenceResolvingPass.h"
|
||||||
|
@ -106,6 +107,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
pipeline.add_compilation_pass<IfBranchMergingPass>();
|
pipeline.add_compilation_pass<IfBranchMergingPass>();
|
||||||
pipeline.add_compilation_pass<ReferenceResolvingPass>();
|
pipeline.add_compilation_pass<ReferenceResolvingPass>();
|
||||||
pipeline.add_compilation_pass<CFGBuildingPass>();
|
pipeline.add_compilation_pass<CFGBuildingPass>();
|
||||||
|
pipeline.add_compilation_pass<CFGSimplificationPass>();
|
||||||
|
|
||||||
pipeline.for_each_step_in(passes_to_dump_ast, [](CompilationStepWithDumpOptions& step) {
|
pipeline.for_each_step_in(passes_to_dump_ast, [](CompilationStepWithDumpOptions& step) {
|
||||||
step.dump_ast = true;
|
step.dump_ast = true;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue