Переглянути джерело

JSSpecCompiler: Add bare-bones DCE pass

Right now the only dead code it eliminates is the unused phi nodes.
Dan Klishch 1 рік тому
батько
коміт
5338cdd153

+ 1 - 0
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

+ 84 - 0
Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/DeadCodeEliminationPass.cpp

@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2023, Dan Klishch <danilklishch@gmail.com>
+ *
+ * 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<Variable>(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;
+    });
+}
+
+}

+ 45 - 0
Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/DeadCodeEliminationPass.h

@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2023, Dan Klishch <danilklishch@gmail.com>
+ *
+ * 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<DeadCodeEliminationPass, SSAVariableDeclarationRef> {
+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<Vertex> outgoing_edges;
+        Vector<Vertex> incoming_edges;
+
+        bool is_referenced = false;
+    };
+
+    Vector<NodeData> m_nodes;
+};
+
+}

+ 86 - 0
Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/StronglyConnectedComponents.h

@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2023, Dan Klishch <danilklishch@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Vector.h>
+
+#include "Compiler/EnableGraphPointers.h"
+
+namespace JSSpecCompiler {
+
+namespace Detail {
+template<typename GraphVertex, typename GraphNode>
+class StronglyConnectedComponents
+    : private EnableGraphPointers<StronglyConnectedComponents<GraphVertex, GraphNode>> {
+    using Self = StronglyConnectedComponents<GraphVertex, GraphNode>;
+    using Vertex = typename EnableGraphPointers<Self>::Vertex;
+
+public:
+    StronglyConnectedComponents(Vector<GraphNode> const& graph)
+        : m_graph(graph)
+    {
+    }
+
+    Vector<Vector<GraphVertex>> find()
+    {
+        Vector<Vector<GraphVertex>> 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<Self>;
+
+    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<GraphVertex>& 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<GraphNode> const& m_graph;
+    Vector<NodeData> m_nodes;
+    Vector<Vertex> m_order;
+};
+}
+
+template<typename NodeData>
+auto find_strongly_connected_components(Vector<NodeData> const& graph)
+{
+    using Vertex = RemoveCVReference<decltype(graph[0].outgoing_edges[0])>;
+    return Detail::StronglyConnectedComponents<Vertex, NodeData>(graph).find();
+}
+
+}

+ 68 - 0
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
+

+ 2 - 0
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<int> serenity_main(Main::Arguments arguments)
     pipeline.add_compilation_pass<CFGBuildingPass>();
     pipeline.add_compilation_pass<CFGSimplificationPass>();
     pipeline.add_compilation_pass<SSABuildingPass>();
+    pipeline.add_compilation_pass<DeadCodeEliminationPass>();
 
     pipeline.for_each_step_in(passes_to_dump_ast, [](CompilationStepWithDumpOptions& step) {
         step.dump_ast = true;