mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-26 09:30:24 +00:00
JSSpecCompiler: Prepare for building SSA
This commit introduces NamedVariableDeclaration and SSAVariableDeclaration and allows storing both of them in Variable node. Also, it adds additional structures in FunctionDefinition and BasicBlock, which will be used to store SSA form related information.
This commit is contained in:
parent
23164bc570
commit
0aeb7a26e9
Notes:
sideshowbarker
2024-07-17 02:29:45 +09:00
Author: https://github.com/DanShaders Commit: https://github.com/SerenityOS/serenity/commit/0aeb7a26e9 Pull-request: https://github.com/SerenityOS/serenity/pull/22289 Reviewed-by: https://github.com/ADKaster ✅
11 changed files with 173 additions and 13 deletions
|
@ -4,6 +4,8 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/String.h>
|
||||
|
||||
#include "AST/AST.h"
|
||||
|
||||
namespace JSSpecCompiler {
|
||||
|
@ -17,6 +19,9 @@ Tree NodeSubtreePointer::get(Badge<RecursiveASTVisitor>)
|
|||
},
|
||||
[&](Tree* tree) -> Tree {
|
||||
return *tree;
|
||||
},
|
||||
[&](VariableRef* tree) -> Tree {
|
||||
return *tree;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -28,6 +33,9 @@ void NodeSubtreePointer::replace_subtree(Badge<RecursiveASTVisitor>, NullableTre
|
|||
},
|
||||
[&](Tree* tree) {
|
||||
*tree = replacement.release_nonnull();
|
||||
},
|
||||
[&](VariableRef*) {
|
||||
VERIFY_NOT_REACHED();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -131,4 +139,11 @@ Vector<NodeSubtreePointer> FunctionCall::subtrees()
|
|||
return result;
|
||||
}
|
||||
|
||||
String Variable::name() const
|
||||
{
|
||||
if (m_ssa)
|
||||
return MUST(String::formatted("{}@{}", m_name->m_name, m_ssa->m_version));
|
||||
return MUST(String::from_utf8(m_name->m_name));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,16 +33,26 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
NodeSubtreePointer(VariableRef* tree_ptr)
|
||||
: m_tree_ptr(tree_ptr)
|
||||
{
|
||||
}
|
||||
|
||||
Tree get(Badge<RecursiveASTVisitor>);
|
||||
void replace_subtree(Badge<RecursiveASTVisitor>, NullableTree replacement);
|
||||
|
||||
private:
|
||||
Variant<Tree*, NullableTree*> m_tree_ptr;
|
||||
Variant<Tree*, NullableTree*, VariableRef*> m_tree_ptr;
|
||||
};
|
||||
|
||||
class VariableDeclaration : public RefCounted<VariableDeclaration> {
|
||||
public:
|
||||
VariableDeclaration(StringView name)
|
||||
virtual ~VariableDeclaration() = default;
|
||||
};
|
||||
|
||||
class NamedVariableDeclaration : public VariableDeclaration {
|
||||
public:
|
||||
NamedVariableDeclaration(StringView name)
|
||||
: m_name(name)
|
||||
{
|
||||
}
|
||||
|
@ -50,6 +60,17 @@ public:
|
|||
StringView m_name;
|
||||
};
|
||||
|
||||
class SSAVariableDeclaration : public VariableDeclaration {
|
||||
public:
|
||||
SSAVariableDeclaration(u64 version)
|
||||
: m_version(version)
|
||||
{
|
||||
}
|
||||
|
||||
size_t m_index = 0;
|
||||
u64 m_version;
|
||||
};
|
||||
|
||||
class Node : public RefCounted<Node> {
|
||||
public:
|
||||
virtual ~Node() = default;
|
||||
|
@ -60,6 +81,7 @@ public:
|
|||
virtual Vector<NodeSubtreePointer> subtrees() { return {}; }
|
||||
|
||||
virtual bool is_list() const { return false; }
|
||||
virtual bool is_statement() { VERIFY_NOT_REACHED(); }
|
||||
|
||||
protected:
|
||||
template<typename... Parameters>
|
||||
|
@ -78,11 +100,20 @@ protected:
|
|||
// auto tmp3 = d;
|
||||
// a = tmp1 + tmp2;
|
||||
// ```.
|
||||
class Statement : public Node { };
|
||||
class Expression : public Node { };
|
||||
class Statement : public Node {
|
||||
public:
|
||||
bool is_statement() override { return true; }
|
||||
};
|
||||
|
||||
class Expression : public Node {
|
||||
public:
|
||||
bool is_statement() override { return false; }
|
||||
};
|
||||
|
||||
class ControlFlowOperator : public Statement {
|
||||
public:
|
||||
bool is_statement() override { return false; }
|
||||
|
||||
virtual Vector<BasicBlockRef*> references() = 0;
|
||||
};
|
||||
|
||||
|
@ -110,6 +141,7 @@ public:
|
|||
|
||||
VariableRef m_return_value;
|
||||
|
||||
Vector<NodeSubtreePointer> subtrees() override { return { { &m_return_value } }; }
|
||||
Vector<BasicBlockRef*> references() override { return {}; }
|
||||
|
||||
protected:
|
||||
|
@ -143,6 +175,7 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
Vector<NodeSubtreePointer> subtrees() override { return { { &m_condition } }; }
|
||||
Vector<BasicBlockRef*> references() override;
|
||||
|
||||
Tree m_condition;
|
||||
|
@ -448,12 +481,15 @@ protected:
|
|||
|
||||
class Variable : public Expression {
|
||||
public:
|
||||
Variable(VariableDeclarationRef variable_declaration)
|
||||
: m_variable_declaration(move(variable_declaration))
|
||||
Variable(NamedVariableDeclarationRef name)
|
||||
: m_name(move(name))
|
||||
{
|
||||
}
|
||||
|
||||
VariableDeclarationRef m_variable_declaration;
|
||||
NamedVariableDeclarationRef m_name;
|
||||
SSAVariableDeclarationRef m_ssa;
|
||||
|
||||
String name() const;
|
||||
|
||||
protected:
|
||||
void dump_tree(StringBuilder& builder) override;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/TemporaryChange.h>
|
||||
|
||||
|
@ -156,7 +157,7 @@ void SlotName::dump_tree(StringBuilder& builder)
|
|||
|
||||
void Variable::dump_tree(StringBuilder& builder)
|
||||
{
|
||||
dump_node(builder, "Var {}", m_variable_declaration->m_name);
|
||||
dump_node(builder, "Var {}", name());
|
||||
}
|
||||
|
||||
void FunctionPointer::dump_tree(StringBuilder& builder)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
|
||||
#include "AST/AST.h"
|
||||
|
@ -17,6 +18,15 @@ ErrorOr<void> AK::Formatter<ControlFlowGraph>::format(FormatBuilder& format_buil
|
|||
|
||||
for (auto const& block : control_flow_graph.blocks) {
|
||||
builder.appendff("{}:\n", block->m_index);
|
||||
for (auto const& phi_node : block->m_phi_nodes) {
|
||||
builder.appendff("{} = phi(", phi_node.var->name());
|
||||
for (auto const& branches : phi_node.branches) {
|
||||
builder.appendff("{}: {}", branches.block->m_index, branches.value->name());
|
||||
if (&branches != &phi_node.branches.last())
|
||||
builder.appendff(", ");
|
||||
}
|
||||
builder.appendff(")\n");
|
||||
}
|
||||
for (auto const& expression : block->m_expressions)
|
||||
builder.appendff("{}", expression);
|
||||
builder.appendff("{}\n", Tree(block->m_continuation));
|
||||
|
|
|
@ -16,15 +16,28 @@ namespace JSSpecCompiler {
|
|||
|
||||
class BasicBlock : public RefCounted<BasicBlock> {
|
||||
public:
|
||||
struct PhiNode {
|
||||
struct Branch {
|
||||
BasicBlockRef block;
|
||||
VariableRef value;
|
||||
};
|
||||
|
||||
VariableRef var;
|
||||
Vector<Branch> branches;
|
||||
};
|
||||
|
||||
BasicBlock(size_t index, NonnullRefPtr<ControlFlowOperator> continuation)
|
||||
: m_index(index)
|
||||
, m_continuation(move(continuation))
|
||||
, m_immediate_dominator(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
size_t m_index;
|
||||
Vector<PhiNode> m_phi_nodes;
|
||||
Vector<Tree> m_expressions;
|
||||
NonnullRefPtr<ControlFlowOperator> m_continuation;
|
||||
BasicBlockRef m_immediate_dominator;
|
||||
};
|
||||
|
||||
class ControlFlowGraph : public RefCounted<ControlFlowGraph> {
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Dan Klishch <danilklishch@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/NumericLimits.h>
|
||||
#include <AK/Vector.h>
|
||||
|
||||
namespace JSSpecCompiler {
|
||||
|
||||
struct VoidRef { };
|
||||
|
||||
template<typename T, typename NativeNodeRef = VoidRef>
|
||||
class EnableGraphPointers {
|
||||
public:
|
||||
class VertexBase {
|
||||
public:
|
||||
VertexBase() = default;
|
||||
VertexBase(size_t index)
|
||||
: m_index(index)
|
||||
{
|
||||
}
|
||||
|
||||
bool is_invalid() const { return m_index == invalid_node; }
|
||||
operator size_t() const { return m_index; }
|
||||
|
||||
explicit VertexBase(NativeNodeRef const& node)
|
||||
requires(!IsSame<NativeNodeRef, VoidRef>)
|
||||
: VertexBase(node->m_index)
|
||||
{
|
||||
}
|
||||
|
||||
auto& operator*() const { return m_instance->m_nodes[m_index]; }
|
||||
auto* operator->() const { return &m_instance->m_nodes[m_index]; }
|
||||
|
||||
protected:
|
||||
size_t m_index = invalid_node;
|
||||
};
|
||||
|
||||
using Vertex = VertexBase;
|
||||
|
||||
inline static constexpr size_t invalid_node = NumericLimits<size_t>::max();
|
||||
|
||||
template<typename Func>
|
||||
void with_graph(Func func)
|
||||
{
|
||||
m_instance = static_cast<T*>(this);
|
||||
func();
|
||||
m_instance = nullptr;
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
void with_graph(size_t n, Func func)
|
||||
{
|
||||
m_instance = static_cast<T*>(this);
|
||||
m_instance->m_nodes.resize(n);
|
||||
func();
|
||||
m_instance->m_nodes.clear();
|
||||
m_instance = nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
inline static thread_local T* m_instance = nullptr;
|
||||
};
|
||||
|
||||
}
|
|
@ -23,7 +23,7 @@ RecursionDecision ReferenceResolvingPass::on_entry(Tree tree)
|
|||
if (auto variable_name = as<UnresolvedReference>(binary_operation->m_left); variable_name) {
|
||||
auto name = variable_name->m_name;
|
||||
if (!m_function->m_local_variables.contains(name))
|
||||
m_function->m_local_variables.set(name, make_ref_counted<VariableDeclaration>(name));
|
||||
m_function->m_local_variables.set(name, make_ref_counted<NamedVariableDeclaration>(name));
|
||||
}
|
||||
}
|
||||
return RecursionDecision::Recurse;
|
||||
|
|
|
@ -14,6 +14,10 @@ namespace JSSpecCompiler {
|
|||
class NodeSubtreePointer;
|
||||
class VariableDeclaration;
|
||||
using VariableDeclarationRef = NonnullRefPtr<VariableDeclaration>;
|
||||
class NamedVariableDeclaration;
|
||||
using NamedVariableDeclarationRef = NonnullRefPtr<NamedVariableDeclaration>;
|
||||
class SSAVariableDeclaration;
|
||||
using SSAVariableDeclarationRef = RefPtr<SSAVariableDeclaration>;
|
||||
|
||||
class Node;
|
||||
using NullableTree = RefPtr<Node>;
|
||||
|
|
|
@ -28,8 +28,15 @@ FunctionDeclaration::FunctionDeclaration(StringView name)
|
|||
FunctionDefinition::FunctionDefinition(StringView name, Tree ast)
|
||||
: FunctionDeclaration(name)
|
||||
, m_ast(move(ast))
|
||||
, m_return_value(make_ref_counted<VariableDeclaration>("$return"sv))
|
||||
, m_return_value(make_ref_counted<NamedVariableDeclaration>("$return"sv))
|
||||
{
|
||||
}
|
||||
|
||||
void FunctionDefinition::reindex_ssa_variables()
|
||||
{
|
||||
size_t index = 0;
|
||||
for (auto const& var : m_local_ssa_variables)
|
||||
var->m_index = index++;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,9 +37,14 @@ class FunctionDefinition : public FunctionDeclaration {
|
|||
public:
|
||||
FunctionDefinition(StringView name, Tree ast);
|
||||
|
||||
void reindex_ssa_variables();
|
||||
|
||||
Tree m_ast;
|
||||
VariableDeclarationRef m_return_value;
|
||||
HashMap<StringView, VariableDeclarationRef> m_local_variables;
|
||||
NamedVariableDeclarationRef m_return_value;
|
||||
// NOTE: The hash map here is ordered since we do not want random hash changes to break our test
|
||||
// expectations (looking at you, SipHash).
|
||||
OrderedHashMap<StringView, NamedVariableDeclarationRef> m_local_variables;
|
||||
Vector<SSAVariableDeclarationRef> m_local_ssa_variables;
|
||||
RefPtr<ControlFlowGraph> m_cfg;
|
||||
};
|
||||
|
||||
|
|
|
@ -200,7 +200,7 @@ void SpecParsingStep::run(TranslationUnitRef translation_unit)
|
|||
make_ref_counted<FunctionDefinition>(spec_function.m_name, spec_function.m_algorithm.m_tree));
|
||||
|
||||
for (auto const& argument : spec_function.m_arguments)
|
||||
function->m_local_variables.set(argument.name, make_ref_counted<VariableDeclaration>(argument.name));
|
||||
function->m_local_variables.set(argument.name, make_ref_counted<NamedVariableDeclaration>(argument.name));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue