mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 23:17:45 +00:00
LibJS: Start fleshing out a bytecode for the JavaScript engine :^)
This patch begins the work of implementing JavaScript execution in a bytecode VM instead of an AST tree-walk interpreter. It's probably quite naive, but we have to start somewhere. The basic idea is that you call Bytecode::Generator::generate() on an AST node and it hands you back a Bytecode::Block filled with instructions that can then be interpreted by a Bytecode::Interpreter. This first version only implements two instructions: Load and Add. :^) Each bytecode block has infinity registers, and the interpreter resizes its register file to fit the block being executed. Two new `js` options are added in this patch as well: `-d` will dump the generated bytecode `-b` will execute the generated bytecode Note that unless `-d` and/or `-b` are specified, none of the bytecode related stuff in LibJS runs at all. This is implemented in parallel with the existing AST interpreter. :^)
This commit is contained in:
parent
f9395efaac
commit
69dddd4ef5
16 changed files with 487 additions and 24 deletions
38
Userland/Libraries/LibJS/Bytecode/Block.cpp
Normal file
38
Userland/Libraries/LibJS/Bytecode/Block.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <LibJS/Bytecode/Block.h>
|
||||
#include <LibJS/Bytecode/Instruction.h>
|
||||
|
||||
namespace JS::Bytecode {
|
||||
|
||||
NonnullOwnPtr<Block> Block::create()
|
||||
{
|
||||
return adopt_own(*new Block);
|
||||
}
|
||||
|
||||
Block::Block()
|
||||
{
|
||||
}
|
||||
|
||||
Block::~Block()
|
||||
{
|
||||
}
|
||||
|
||||
void Block::append(Badge<Bytecode::Generator>, NonnullOwnPtr<Instruction> instruction)
|
||||
{
|
||||
m_instructions.append(move(instruction));
|
||||
}
|
||||
|
||||
void Block::dump() const
|
||||
{
|
||||
for (size_t i = 0; i < m_instructions.size(); ++i) {
|
||||
warnln("[{:3}] {}", i, m_instructions[i].to_string());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
35
Userland/Libraries/LibJS/Bytecode/Block.h
Normal file
35
Userland/Libraries/LibJS/Bytecode/Block.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Badge.h>
|
||||
#include <AK/NonnullOwnPtrVector.h>
|
||||
#include <LibJS/Forward.h>
|
||||
|
||||
namespace JS::Bytecode {
|
||||
|
||||
class Block {
|
||||
public:
|
||||
static NonnullOwnPtr<Block> create();
|
||||
~Block();
|
||||
|
||||
NonnullOwnPtrVector<Instruction> const& instructions() const { return m_instructions; }
|
||||
void dump() const;
|
||||
|
||||
size_t register_count() const { return m_register_count; }
|
||||
|
||||
void append(Badge<Bytecode::Generator>, NonnullOwnPtr<Instruction>);
|
||||
void set_register_count(Badge<Bytecode::Generator>, size_t count) { m_register_count = count; }
|
||||
|
||||
private:
|
||||
Block();
|
||||
|
||||
size_t m_register_count { 0 };
|
||||
NonnullOwnPtrVector<Instruction> m_instructions;
|
||||
};
|
||||
|
||||
}
|
45
Userland/Libraries/LibJS/Bytecode/Generator.cpp
Normal file
45
Userland/Libraries/LibJS/Bytecode/Generator.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <LibJS/AST.h>
|
||||
#include <LibJS/Bytecode/Block.h>
|
||||
#include <LibJS/Bytecode/Generator.h>
|
||||
#include <LibJS/Bytecode/Instruction.h>
|
||||
#include <LibJS/Bytecode/Register.h>
|
||||
#include <LibJS/Forward.h>
|
||||
|
||||
namespace JS::Bytecode {
|
||||
|
||||
Generator::Generator()
|
||||
{
|
||||
m_block = Block::create();
|
||||
}
|
||||
|
||||
Generator::~Generator()
|
||||
{
|
||||
}
|
||||
|
||||
OwnPtr<Block> Generator::generate(ASTNode const& node)
|
||||
{
|
||||
Generator generator;
|
||||
[[maybe_unused]] auto dummy = node.generate_bytecode(generator);
|
||||
generator.m_block->set_register_count({}, generator.m_next_register);
|
||||
return move(generator.m_block);
|
||||
}
|
||||
|
||||
void Generator::append(NonnullOwnPtr<Instruction> instruction)
|
||||
{
|
||||
m_block->append({}, move(instruction));
|
||||
}
|
||||
|
||||
Register Generator::allocate_register()
|
||||
{
|
||||
VERIFY(m_next_register != NumericLimits<u32>::max());
|
||||
return Register { m_next_register++ };
|
||||
}
|
||||
|
||||
}
|
37
Userland/Libraries/LibJS/Bytecode/Generator.h
Normal file
37
Userland/Libraries/LibJS/Bytecode/Generator.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <LibJS/Forward.h>
|
||||
|
||||
namespace JS::Bytecode {
|
||||
|
||||
class Generator {
|
||||
public:
|
||||
static OwnPtr<Block> generate(ASTNode const&);
|
||||
|
||||
Register allocate_register();
|
||||
|
||||
template<typename OpType, typename... Args>
|
||||
void emit(Args&&... args)
|
||||
{
|
||||
auto instruction = make<OpType>(forward<Args>(args)...);
|
||||
append(move(instruction));
|
||||
}
|
||||
|
||||
private:
|
||||
Generator();
|
||||
~Generator();
|
||||
|
||||
void append(NonnullOwnPtr<Instruction>);
|
||||
|
||||
OwnPtr<Block> m_block;
|
||||
u32 m_next_register { 1 };
|
||||
};
|
||||
|
||||
}
|
15
Userland/Libraries/LibJS/Bytecode/Instruction.cpp
Normal file
15
Userland/Libraries/LibJS/Bytecode/Instruction.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Bytecode/Instruction.h>
|
||||
|
||||
namespace JS::Bytecode {
|
||||
|
||||
Instruction::~Instruction()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
22
Userland/Libraries/LibJS/Bytecode/Instruction.h
Normal file
22
Userland/Libraries/LibJS/Bytecode/Instruction.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <LibJS/Forward.h>
|
||||
|
||||
namespace JS::Bytecode {
|
||||
|
||||
class Instruction {
|
||||
public:
|
||||
virtual ~Instruction();
|
||||
|
||||
virtual String to_string() const = 0;
|
||||
virtual void execute(Bytecode::Interpreter&) const = 0;
|
||||
};
|
||||
|
||||
}
|
42
Userland/Libraries/LibJS/Bytecode/Interpreter.cpp
Normal file
42
Userland/Libraries/LibJS/Bytecode/Interpreter.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Bytecode/Block.h>
|
||||
#include <LibJS/Bytecode/Instruction.h>
|
||||
#include <LibJS/Bytecode/Interpreter.h>
|
||||
|
||||
namespace JS::Bytecode {
|
||||
|
||||
Interpreter::Interpreter(GlobalObject& global_object)
|
||||
: m_global_object(global_object)
|
||||
{
|
||||
}
|
||||
|
||||
Interpreter::~Interpreter()
|
||||
{
|
||||
}
|
||||
|
||||
void Interpreter::run(Bytecode::Block const& block)
|
||||
{
|
||||
dbgln("Bytecode::Interpreter will run block {:p}", &block);
|
||||
|
||||
m_registers.resize(block.register_count());
|
||||
|
||||
for (auto& instruction : block.instructions())
|
||||
instruction.execute(*this);
|
||||
|
||||
dbgln("Bytecode::Interpreter did run block {:p}", &block);
|
||||
for (size_t i = 0; i < m_registers.size(); ++i) {
|
||||
String value_string;
|
||||
if (m_registers[i].is_empty())
|
||||
value_string = "(empty)";
|
||||
else
|
||||
value_string = m_registers[i].to_string_without_side_effects();
|
||||
dbgln("[{:3}] {}", i, value_string);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
32
Userland/Libraries/LibJS/Bytecode/Interpreter.h
Normal file
32
Userland/Libraries/LibJS/Bytecode/Interpreter.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Bytecode/Register.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Heap/Cell.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
|
||||
namespace JS::Bytecode {
|
||||
|
||||
class Interpreter {
|
||||
public:
|
||||
explicit Interpreter(GlobalObject&);
|
||||
~Interpreter();
|
||||
|
||||
GlobalObject& global_object() { return m_global_object; }
|
||||
|
||||
void run(Bytecode::Block const&);
|
||||
|
||||
Value& reg(Register const& r) { return m_registers[r.index()]; }
|
||||
|
||||
private:
|
||||
GlobalObject& m_global_object;
|
||||
Vector<Value> m_registers;
|
||||
};
|
||||
|
||||
}
|
33
Userland/Libraries/LibJS/Bytecode/Op.cpp
Normal file
33
Userland/Libraries/LibJS/Bytecode/Op.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Bytecode/Interpreter.h>
|
||||
#include <LibJS/Bytecode/Op.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
|
||||
namespace JS::Bytecode::Op {
|
||||
|
||||
void Load::execute(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
interpreter.reg(m_dst) = m_value;
|
||||
}
|
||||
|
||||
void Add::execute(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
interpreter.reg(m_dst) = add(interpreter.global_object(), interpreter.reg(m_src1), interpreter.reg(m_src2));
|
||||
}
|
||||
|
||||
String Load::to_string() const
|
||||
{
|
||||
return String::formatted("Load dst:r{}, value:{}", m_dst.index(), m_value.to_string_without_side_effects());
|
||||
}
|
||||
|
||||
String Add::to_string() const
|
||||
{
|
||||
return String::formatted("Add dst:r{}, src1:r{}, src2:r{}", m_dst.index(), m_src1.index(), m_src2.index());
|
||||
}
|
||||
|
||||
}
|
52
Userland/Libraries/LibJS/Bytecode/Op.h
Normal file
52
Userland/Libraries/LibJS/Bytecode/Op.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Bytecode/Instruction.h>
|
||||
#include <LibJS/Bytecode/Register.h>
|
||||
#include <LibJS/Heap/Cell.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
|
||||
namespace JS::Bytecode::Op {
|
||||
|
||||
class Load final : public Instruction {
|
||||
public:
|
||||
Load(Register dst, Value value)
|
||||
: m_dst(dst)
|
||||
, m_value(value)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~Load() override { }
|
||||
virtual void execute(Bytecode::Interpreter&) const override;
|
||||
virtual String to_string() const override;
|
||||
|
||||
private:
|
||||
Register m_dst;
|
||||
Value m_value;
|
||||
};
|
||||
|
||||
class Add final : public Instruction {
|
||||
public:
|
||||
Add(Register dst, Register src1, Register src2)
|
||||
: m_dst(dst)
|
||||
, m_src1(src1)
|
||||
, m_src2(src2)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~Add() override { }
|
||||
virtual void execute(Bytecode::Interpreter&) const override;
|
||||
virtual String to_string() const override;
|
||||
|
||||
private:
|
||||
Register m_dst;
|
||||
Register m_src1;
|
||||
Register m_src2;
|
||||
};
|
||||
|
||||
}
|
26
Userland/Libraries/LibJS/Bytecode/Register.h
Normal file
26
Userland/Libraries/LibJS/Bytecode/Register.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace JS::Bytecode {
|
||||
|
||||
class Register {
|
||||
public:
|
||||
explicit Register(u32 index)
|
||||
: m_index(index)
|
||||
{
|
||||
}
|
||||
|
||||
u32 index() const { return m_index; }
|
||||
|
||||
private:
|
||||
u32 m_index { 0 };
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue