1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 21:57:43 +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:
Dan Klishch 2023-08-19 16:20:04 -04:00 committed by Andrew Kaster
parent 475ef6965a
commit 12072dbac5
7 changed files with 215 additions and 1 deletions

View file

@ -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()
{
return { { &m_left }, { &m_right } };

View file

@ -80,7 +80,11 @@ protected:
// ```.
class Statement : public Node { };
class Expression : public Node { };
class ControlFlowOperator : public Statement { };
class ControlFlowOperator : public Statement {
public:
virtual Vector<BasicBlockRef*> references() = 0;
};
class ErrorNode : public Expression {
public:
@ -106,6 +110,8 @@ public:
VariableRef m_return_value;
Vector<BasicBlockRef*> references() override { return {}; }
protected:
void dump_tree(StringBuilder& builder) override;
};
@ -117,6 +123,8 @@ public:
{
}
Vector<BasicBlockRef*> references() override;
BasicBlockRef m_block;
protected:
@ -135,6 +143,8 @@ public:
{
}
Vector<BasicBlockRef*> references() override;
Tree m_condition;
BasicBlockRef m_then;
BasicBlockRef m_else;

View file

@ -5,6 +5,7 @@ set(SOURCES
Compiler/ControlFlowGraph.cpp
Compiler/GenericASTPass.cpp
Compiler/Passes/CFGBuildingPass.cpp
Compiler/Passes/CFGSimplificationPass.cpp
Compiler/Passes/FunctionCallCanonicalizationPass.cpp
Compiler/Passes/IfBranchMergingPass.cpp
Compiler/Passes/ReferenceResolvingPass.cpp

View file

@ -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);
}
}

View file

@ -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;
};
}

View file

@ -182,3 +182,70 @@ BinaryOperation Assignment
Error ""
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

View file

@ -9,6 +9,7 @@
#include <LibMain/Main.h>
#include "Compiler/Passes/CFGBuildingPass.h"
#include "Compiler/Passes/CFGSimplificationPass.h"
#include "Compiler/Passes/FunctionCallCanonicalizationPass.h"
#include "Compiler/Passes/IfBranchMergingPass.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<ReferenceResolvingPass>();
pipeline.add_compilation_pass<CFGBuildingPass>();
pipeline.add_compilation_pass<CFGSimplificationPass>();
pipeline.for_each_step_in(passes_to_dump_ast, [](CompilationStepWithDumpOptions& step) {
step.dump_ast = true;