mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 17:32:44 +00:00 
			
		
		
		
	JSSpecCompiler: Add control flow building pass
This commit is contained in:
		
							parent
							
								
									c74e2d04d1
								
							
						
					
					
						commit
						ff44aea917
					
				
					 10 changed files with 222 additions and 2 deletions
				
			
		|  | @ -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); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -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); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -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; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
|  | @ -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 { | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Klishch
						Dan Klishch