From 5338cdd1531e033479dc4b6878a3ee4d9a3c629a Mon Sep 17 00:00:00 2001 From: Dan Klishch Date: Tue, 29 Aug 2023 15:47:39 -0400 Subject: [PATCH] JSSpecCompiler: Add bare-bones DCE pass Right now the only dead code it eliminates is the unused phi nodes. --- .../JSSpecCompiler/CMakeLists.txt | 1 + .../Passes/DeadCodeEliminationPass.cpp | 84 ++++++++++++++++++ .../Compiler/Passes/DeadCodeEliminationPass.h | 45 ++++++++++ .../Compiler/StronglyConnectedComponents.h | 86 +++++++++++++++++++ .../Tests/simple.cpp.expectation | 68 +++++++++++++++ .../CodeGenerators/JSSpecCompiler/main.cpp | 2 + 6 files changed, 286 insertions(+) create mode 100644 Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/DeadCodeEliminationPass.cpp create mode 100644 Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/DeadCodeEliminationPass.h create mode 100644 Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/StronglyConnectedComponents.h diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CMakeLists.txt b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CMakeLists.txt index 3206048ed1..d99056b74a 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CMakeLists.txt +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CMakeLists.txt @@ -6,6 +6,7 @@ set(SOURCES Compiler/GenericASTPass.cpp Compiler/Passes/CFGBuildingPass.cpp Compiler/Passes/CFGSimplificationPass.cpp + Compiler/Passes/DeadCodeEliminationPass.cpp Compiler/Passes/FunctionCallCanonicalizationPass.cpp Compiler/Passes/IfBranchMergingPass.cpp Compiler/Passes/ReferenceResolvingPass.cpp diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/DeadCodeEliminationPass.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/DeadCodeEliminationPass.cpp new file mode 100644 index 0000000000..48ec6d728a --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/DeadCodeEliminationPass.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2023, Dan Klishch + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "Compiler/Passes/DeadCodeEliminationPass.h" +#include "AST/AST.h" +#include "Compiler/ControlFlowGraph.h" +#include "Compiler/StronglyConnectedComponents.h" +#include "Function.h" + +namespace JSSpecCompiler { + +void DeadCodeEliminationPass::process_function() +{ + with_graph(m_function->m_local_ssa_variables.size(), [&] { + remove_unused_phi_nodes(); + }); + m_function->reindex_ssa_variables(); +} + +DeadCodeEliminationPass::Vertex DeadCodeEliminationPass::as_vertex(Variable* variable) +{ + return Vertex(variable->m_ssa); +} + +RecursionDecision DeadCodeEliminationPass::on_entry(Tree tree) +{ + if (tree->is_statement()) + TODO(); + return RecursionDecision::Recurse; +} + +void DeadCodeEliminationPass::on_leave(Tree tree) +{ + if (auto variable = as(tree); variable) + as_vertex(variable)->is_referenced = true; +} + +void DeadCodeEliminationPass::remove_unused_phi_nodes() +{ + for (auto const& block : m_function->m_cfg->blocks) { + for (auto const& phi_node : block->m_phi_nodes) { + auto to = as_vertex(phi_node.var); + for (auto const& branch : phi_node.branches) { + auto from = as_vertex(branch.value); + + from->outgoing_edges.append(to); + to->incoming_edges.append(from); + } + } + + for (auto& expr : block->m_expressions) + run_in_subtree(expr); + run_in_const_subtree(block->m_continuation); + } + + // FIXME?: There surely must be a way to do this in a linear time without finding strongly + // connected components. + for (auto const& component : find_strongly_connected_components(m_nodes)) { + bool is_referenced = false; + + for (Vertex u : component) + for (Vertex v : u->outgoing_edges) + is_referenced |= v->is_referenced; + + if (is_referenced) + for (Vertex u : component) + u->is_referenced = true; + } + + for (auto const& block : m_function->m_cfg->blocks) { + block->m_phi_nodes.remove_all_matching([&](auto const& node) { + return !as_vertex(node.var)->is_referenced; + }); + } + + m_function->m_local_ssa_variables.remove_all_matching([&](auto const& variable) { + return !Vertex(variable)->is_referenced; + }); +} + +} diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/DeadCodeEliminationPass.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/DeadCodeEliminationPass.h new file mode 100644 index 0000000000..f8b3c02222 --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/DeadCodeEliminationPass.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023, Dan Klishch + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include "Compiler/EnableGraphPointers.h" +#include "Compiler/GenericASTPass.h" +#include "Compiler/StronglyConnectedComponents.h" + +namespace JSSpecCompiler { + +class DeadCodeEliminationPass + : public IntraproceduralCompilerPass + , private RecursiveASTVisitor + , private EnableGraphPointers { +public: + inline static constexpr StringView name = "dce"sv; + + using IntraproceduralCompilerPass::IntraproceduralCompilerPass; + +protected: + void process_function() override; + +private: + friend EnableGraphPointers; + + static Vertex as_vertex(Variable* variable); + RecursionDecision on_entry(Tree tree) override; + void on_leave(Tree tree) override; + void remove_unused_phi_nodes(); + + struct NodeData { + Vector outgoing_edges; + Vector incoming_edges; + + bool is_referenced = false; + }; + + Vector m_nodes; +}; + +} diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/StronglyConnectedComponents.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/StronglyConnectedComponents.h new file mode 100644 index 0000000000..f0c6ac2fe6 --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/StronglyConnectedComponents.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2023, Dan Klishch + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +#include "Compiler/EnableGraphPointers.h" + +namespace JSSpecCompiler { + +namespace Detail { +template +class StronglyConnectedComponents + : private EnableGraphPointers> { + using Self = StronglyConnectedComponents; + using Vertex = typename EnableGraphPointers::Vertex; + +public: + StronglyConnectedComponents(Vector const& graph) + : m_graph(graph) + { + } + + Vector> find() + { + Vector> result; + size_t n = m_graph.size(); + Self::with_graph(n, [&] { + for (size_t i = 0; i < m_graph.size(); ++i) + find_order(i); + for (size_t i = n; i--;) { + if (!m_order[i]->is_processed) { + result.empend(); + find_component(GraphVertex(m_order[i]), result.last()); + } + } + }); + return result; + } + +private: + friend EnableGraphPointers; + + void find_order(Vertex u) + { + if (u->is_visited) + return; + u->is_visited = true; + + for (auto v : GraphVertex(u)->incoming_edges) + find_order(Vertex(v)); + m_order.append(u); + } + + void find_component(GraphVertex u, Vector& current_scc) + { + current_scc.empend(u); + Vertex(u)->is_processed = true; + for (auto v : u->outgoing_edges) + if (!Vertex(v)->is_processed) + find_component(v, current_scc); + } + + struct NodeData { + bool is_visited = false; + bool is_processed = false; + }; + + Vector const& m_graph; + Vector m_nodes; + Vector m_order; +}; +} + +template +auto find_strongly_connected_components(Vector const& graph) +{ + using Vertex = RemoveCVReference; + return Detail::StronglyConnectedComponents(graph).find(); +} + +} diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Tests/simple.cpp.expectation b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Tests/simple.cpp.expectation index 2ecb952867..4317b6d8c2 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Tests/simple.cpp.expectation +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Tests/simple.cpp.expectation @@ -318,3 +318,71 @@ BinaryOperation Assignment MathematicalConstant 4 ControlFlowJump jump=3 +===== AST after dce ===== +f(): +TreeList + IfElseIfChain + UnresolvedReference cond1 + TreeList + BinaryOperation Assignment + Var a@1 + MathematicalConstant 1 + IfElseIfChain + UnresolvedReference cond2 + TreeList + BinaryOperation Assignment + Var b@1 + Var a@1 + TreeList + BinaryOperation Assignment + Var b@3 + MathematicalConstant 3 + TreeList + BinaryOperation Assignment + Var b@4 + MathematicalConstant 4 + ReturnNode + Var b@2 + +===== CFG after dce ===== +f(): +0: +ControlFlowBranch true=1 false=6 + UnresolvedReference cond1 + +1: +BinaryOperation Assignment + Var a@1 + MathematicalConstant 1 +ControlFlowBranch true=2 false=5 + UnresolvedReference cond2 + +2: +BinaryOperation Assignment + Var b@1 + Var a@1 +ControlFlowJump jump=3 + +3: +b@2 = phi(2: b@1, 5: b@3, 6: b@4) +BinaryOperation Assignment + Var $return@1 + Var b@2 +ControlFlowJump jump=4 + +4: +ControlFlowFunctionReturn + Var $return@1 + +5: +BinaryOperation Assignment + Var b@3 + MathematicalConstant 3 +ControlFlowJump jump=3 + +6: +BinaryOperation Assignment + Var b@4 + MathematicalConstant 4 +ControlFlowJump jump=3 + diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp index b8cbf3bb95..05ff1772f3 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp @@ -10,6 +10,7 @@ #include "Compiler/Passes/CFGBuildingPass.h" #include "Compiler/Passes/CFGSimplificationPass.h" +#include "Compiler/Passes/DeadCodeEliminationPass.h" #include "Compiler/Passes/FunctionCallCanonicalizationPass.h" #include "Compiler/Passes/IfBranchMergingPass.h" #include "Compiler/Passes/ReferenceResolvingPass.h" @@ -110,6 +111,7 @@ ErrorOr serenity_main(Main::Arguments arguments) pipeline.add_compilation_pass(); pipeline.add_compilation_pass(); pipeline.add_compilation_pass(); + pipeline.add_compilation_pass(); pipeline.for_each_step_in(passes_to_dump_ast, [](CompilationStepWithDumpOptions& step) { step.dump_ast = true;