Ver Fonte

JSSpecCompiler: Add control flow building pass

Dan Klishch há 1 ano atrás
pai
commit
ff44aea917

+ 3 - 2
Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/ASTPrinting.cpp

@@ -8,6 +8,7 @@
 #include <AK/TemporaryChange.h>
 #include <AK/TemporaryChange.h>
 
 
 #include "AST/AST.h"
 #include "AST/AST.h"
+#include "Compiler/ControlFlowGraph.h"
 #include "Function.h"
 #include "Function.h"
 
 
 namespace JSSpecCompiler {
 namespace JSSpecCompiler {
@@ -41,12 +42,12 @@ void ControlFlowFunctionReturn::dump_tree(StringBuilder& builder)
 
 
 void ControlFlowJump::dump_tree(StringBuilder& builder)
 void ControlFlowJump::dump_tree(StringBuilder& builder)
 {
 {
-    dump_node(builder, "ControlFlowJump jump={:p}", m_block);
+    dump_node(builder, "ControlFlowJump jump={}", m_block->m_index);
 }
 }
 
 
 void ControlFlowBranch::dump_tree(StringBuilder& builder)
 void ControlFlowBranch::dump_tree(StringBuilder& builder)
 {
 {
-    dump_node(builder, "ControlFlowBranch true={:p} false={:p}", m_then, m_else);
+    dump_node(builder, "ControlFlowBranch true={} false={}", m_then->m_index, m_else->m_index);
     m_condition->format_tree(builder);
     m_condition->format_tree(builder);
 }
 }
 
 

+ 1 - 0
Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CMakeLists.txt

@@ -3,6 +3,7 @@ set(SOURCES
     AST/ASTPrinting.cpp
     AST/ASTPrinting.cpp
     Compiler/CompilerPass.cpp
     Compiler/CompilerPass.cpp
     Compiler/GenericASTPass.cpp
     Compiler/GenericASTPass.cpp
+    Compiler/Passes/CFGBuildingPass.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

+ 41 - 0
Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/ControlFlowGraph.h

@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2023, Dan Klishch <danilklishch@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/RefCounted.h>
+#include <AK/RefPtr.h>
+#include <AK/Vector.h>
+
+#include "Forward.h"
+
+namespace JSSpecCompiler {
+
+class BasicBlock : public RefCounted<BasicBlock> {
+public:
+    BasicBlock(size_t index, NonnullRefPtr<ControlFlowOperator> continuation)
+        : m_index(index)
+        , m_continuation(move(continuation))
+    {
+    }
+
+    size_t m_index;
+    Vector<Tree> m_expressions;
+    NonnullRefPtr<ControlFlowOperator> m_continuation;
+};
+
+class ControlFlowGraph : public RefCounted<ControlFlowGraph> {
+public:
+    ControlFlowGraph() { }
+
+    size_t blocks_count() const { return blocks.size(); }
+
+    Vector<NonnullRefPtr<BasicBlock>> blocks;
+    BasicBlockRef start_block;
+    BasicBlockRef end_block;
+};
+
+}

+ 107 - 0
Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/CFGBuildingPass.cpp

@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2023, Dan Klishch <danilklishch@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "Compiler/Passes/CFGBuildingPass.h"
+#include "AST/AST.h"
+#include "Function.h"
+
+namespace JSSpecCompiler {
+
+void CFGBuildingPass::process_function()
+{
+    m_cfg = m_function->m_cfg = make_ref_counted<ControlFlowGraph>();
+    m_current_block = m_cfg->start_block = create_empty_block();
+    m_cfg->end_block = create_empty_block();
+    m_cfg->end_block->m_continuation = make_ref_counted<ControlFlowFunctionReturn>(
+        make_ref_counted<Variable>(m_function->m_return_value));
+    m_is_expression_stack = { false };
+
+    run_in_subtree(m_function->m_ast);
+
+    // FIXME: What should we do if control flow reached the end of the function? Returning
+    //        error_tree will 100% confuse future passes.
+    m_current_block->m_expressions.append(make_ref_counted<BinaryOperation>(
+        BinaryOperator::Assignment,
+        make_ref_counted<Variable>(m_function->m_return_value),
+        error_tree));
+    m_current_block->m_continuation = make_ref_counted<ControlFlowJump>(m_cfg->end_block);
+}
+
+RecursionDecision CFGBuildingPass::on_entry(Tree tree)
+{
+    m_is_expression_stack.append(!as<Expression>(tree).is_null());
+
+    if (auto if_else_if_chain = as<IfElseIfChain>(tree); if_else_if_chain) {
+        auto* end_block = create_empty_block();
+
+        for (size_t i = 0; i < if_else_if_chain->branches_count(); ++i) {
+            auto current_condition = if_else_if_chain->m_conditions[i];
+
+            run_in_subtree(current_condition);
+            will_be_used_as_expression(current_condition);
+            auto* condition_block = exchange_current_with_empty();
+
+            auto* branch_entry = m_current_block;
+            run_in_subtree(if_else_if_chain->m_branches[i]);
+            auto* branch_return = exchange_current_with_empty();
+            branch_return->m_continuation = make_ref_counted<ControlFlowJump>(end_block);
+
+            condition_block->m_continuation = make_ref_counted<ControlFlowBranch>(current_condition, branch_entry, m_current_block);
+        }
+
+        if (if_else_if_chain->m_else_branch)
+            run_in_const_subtree(if_else_if_chain->m_else_branch);
+        m_current_block->m_continuation = make_ref_counted<ControlFlowJump>(end_block);
+
+        m_current_block = end_block;
+        return RecursionDecision::Continue;
+    }
+
+    if (auto return_node = as<ReturnNode>(tree); return_node) {
+        Tree return_assignment = make_ref_counted<BinaryOperation>(
+            BinaryOperator::Assignment,
+            make_ref_counted<Variable>(m_function->m_return_value),
+            return_node->m_return_value);
+        run_in_subtree(return_assignment);
+        auto* return_block = exchange_current_with_empty();
+        return_block->m_continuation = make_ref_counted<ControlFlowJump>(m_cfg->end_block);
+
+        return RecursionDecision::Continue;
+    }
+
+    return RecursionDecision::Recurse;
+}
+
+void CFGBuildingPass::on_leave(Tree tree)
+{
+    (void)m_is_expression_stack.take_last();
+
+    if (!m_is_expression_stack.last() && as<Expression>(tree))
+        m_current_block->m_expressions.append(tree);
+}
+
+BasicBlockRef CFGBuildingPass::create_empty_block()
+{
+    m_cfg->blocks.append(make_ref_counted<BasicBlock>(m_cfg->blocks_count(), invalid_continuation));
+    return m_cfg->blocks.last();
+}
+
+BasicBlockRef CFGBuildingPass::exchange_current_with_empty()
+{
+    auto* new_block = create_empty_block();
+    swap(new_block, m_current_block);
+    return new_block;
+}
+
+void CFGBuildingPass::will_be_used_as_expression(Tree const& tree)
+{
+    if (m_current_block->m_expressions.is_empty())
+        VERIFY(is<Statement>(tree.ptr()));
+    else
+        VERIFY(m_current_block->m_expressions.take_last() == tree);
+}
+
+}

+ 39 - 0
Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/CFGBuildingPass.h

@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2023, Dan Klishch <danilklishch@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/TypeCasts.h>
+
+#include "Compiler/ControlFlowGraph.h"
+#include "Compiler/GenericASTPass.h"
+
+namespace JSSpecCompiler {
+
+class CFGBuildingPass
+    : public IntraproceduralCompilerPass
+    , private RecursiveASTVisitor {
+public:
+    inline static constexpr StringView name = "cfg-building"sv;
+
+    using IntraproceduralCompilerPass::IntraproceduralCompilerPass;
+
+protected:
+    void process_function() override;
+    RecursionDecision on_entry(Tree tree) override;
+    void on_leave(Tree tree) override;
+
+private:
+    BasicBlockRef create_empty_block();
+    BasicBlockRef exchange_current_with_empty();
+    void will_be_used_as_expression(Tree const& tree);
+
+    ControlFlowGraph* m_cfg;
+    BasicBlockRef m_current_block;
+    Vector<bool> m_is_expression_stack;
+};
+
+}

+ 1 - 0
Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Forward.h

@@ -49,6 +49,7 @@ using FunctionPointerRef = NonnullRefPtr<FunctionPointer>;
 // Compiler/ControlFlowGraph.h
 // Compiler/ControlFlowGraph.h
 class BasicBlock;
 class BasicBlock;
 using BasicBlockRef = BasicBlock*;
 using BasicBlockRef = BasicBlock*;
+class ControlFlowGraph;
 
 
 // Compiler/GenericASTPass.h
 // Compiler/GenericASTPass.h
 class RecursiveASTVisitor;
 class RecursiveASTVisitor;

+ 1 - 0
Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.cpp

@@ -6,6 +6,7 @@
 
 
 #include "Function.h"
 #include "Function.h"
 #include "AST/AST.h"
 #include "AST/AST.h"
+#include "Compiler/ControlFlowGraph.h"
 
 
 namespace JSSpecCompiler {
 namespace JSSpecCompiler {
 
 

+ 1 - 0
Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h

@@ -40,6 +40,7 @@ public:
     Tree m_ast;
     Tree m_ast;
     VariableDeclarationRef m_return_value;
     VariableDeclarationRef m_return_value;
     HashMap<StringView, VariableDeclarationRef> m_local_variables;
     HashMap<StringView, VariableDeclarationRef> m_local_variables;
+    RefPtr<ControlFlowGraph> m_cfg;
 };
 };
 
 
 }
 }

+ 26 - 0
Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Tests/simple.cpp.expectation

@@ -106,3 +106,29 @@ TreeList
   ReturnNode
   ReturnNode
     Var b
     Var b
 
 
+===== AST after cfg-building =====
+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
+

+ 2 - 0
Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp

@@ -8,6 +8,7 @@
 #include <LibCore/ArgsParser.h>
 #include <LibCore/ArgsParser.h>
 #include <LibMain/Main.h>
 #include <LibMain/Main.h>
 
 
+#include "Compiler/Passes/CFGBuildingPass.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"
@@ -100,6 +101,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
     pipeline.add_compilation_pass<FunctionCallCanonicalizationPass>();
     pipeline.add_compilation_pass<FunctionCallCanonicalizationPass>();
     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.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;