Skip to content

Commit 69dddd4

Browse files
committed
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. :^)
1 parent f9395ef commit 69dddd4

File tree

16 files changed

+487
-24
lines changed

16 files changed

+487
-24
lines changed

Userland/Libraries/LibJS/AST.cpp

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
2+
* Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
33
* Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
44
*
55
* SPDX-License-Identifier: BSD-2-Clause
@@ -13,6 +13,9 @@
1313
#include <AK/TemporaryChange.h>
1414
#include <LibCrypto/BigInt/SignedBigInteger.h>
1515
#include <LibJS/AST.h>
16+
#include <LibJS/Bytecode/Generator.h>
17+
#include <LibJS/Bytecode/Op.h>
18+
#include <LibJS/Bytecode/Register.h>
1619
#include <LibJS/Interpreter.h>
1720
#include <LibJS/Runtime/Accessor.h>
1821
#include <LibJS/Runtime/Array.h>
@@ -2234,4 +2237,49 @@ void ScopeNode::add_functions(NonnullRefPtrVector<FunctionDeclaration> functions
22342237
m_functions.append(move(functions));
22352238
}
22362239

2240+
Optional<Bytecode::Register> ASTNode::generate_bytecode(Bytecode::Generator&) const
2241+
{
2242+
dbgln("Missing generate_bytecode()");
2243+
TODO();
2244+
}
2245+
2246+
Optional<Bytecode::Register> ScopeNode::generate_bytecode(Bytecode::Generator& generator) const
2247+
{
2248+
for (auto& child : children()) {
2249+
[[maybe_unused]] auto reg = child.generate_bytecode(generator);
2250+
}
2251+
return {};
2252+
}
2253+
2254+
Optional<Bytecode::Register> ExpressionStatement::generate_bytecode(Bytecode::Generator& generator) const
2255+
{
2256+
return m_expression->generate_bytecode(generator);
2257+
}
2258+
2259+
Optional<Bytecode::Register> BinaryExpression::generate_bytecode(Bytecode::Generator& generator) const
2260+
{
2261+
auto lhs_reg = m_lhs->generate_bytecode(generator);
2262+
auto rhs_reg = m_rhs->generate_bytecode(generator);
2263+
2264+
VERIFY(lhs_reg.has_value());
2265+
VERIFY(rhs_reg.has_value());
2266+
2267+
switch (m_op) {
2268+
case BinaryOp::Addition: {
2269+
auto dst_reg = generator.allocate_register();
2270+
generator.emit<Bytecode::Op::Add>(dst_reg, *lhs_reg, *rhs_reg);
2271+
return dst_reg;
2272+
}
2273+
default:
2274+
TODO();
2275+
}
2276+
}
2277+
2278+
Optional<Bytecode::Register> NumericLiteral::generate_bytecode(Bytecode::Generator& generator) const
2279+
{
2280+
auto dst = generator.allocate_register();
2281+
generator.emit<Bytecode::Op::Load>(dst, m_value);
2282+
return dst;
2283+
}
2284+
22372285
}

Userland/Libraries/LibJS/AST.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
2+
* Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
33
* Copyright (c) 2020, Linus Groh <linusg@serenityos.org>
44
*
55
* SPDX-License-Identifier: BSD-2-Clause
@@ -37,6 +37,7 @@ class ASTNode : public RefCounted<ASTNode> {
3737
public:
3838
virtual ~ASTNode() { }
3939
virtual Value execute(Interpreter&, GlobalObject&) const = 0;
40+
virtual Optional<Bytecode::Register> generate_bytecode(Bytecode::Generator&) const;
4041
virtual void dump(int indent) const;
4142

4243
const SourceRange& source_range() const { return m_source_range; }
@@ -96,6 +97,7 @@ class ExpressionStatement final : public Statement {
9697

9798
virtual Value execute(Interpreter&, GlobalObject&) const override;
9899
virtual void dump(int indent) const override;
100+
virtual Optional<Bytecode::Register> generate_bytecode(Bytecode::Generator&) const override;
99101

100102
const Expression& expression() const { return m_expression; };
101103

@@ -120,6 +122,7 @@ class ScopeNode : public Statement {
120122
const NonnullRefPtrVector<Statement>& children() const { return m_children; }
121123
virtual Value execute(Interpreter&, GlobalObject&) const override;
122124
virtual void dump(int indent) const override;
125+
virtual Optional<Bytecode::Register> generate_bytecode(Bytecode::Generator&) const override;
123126

124127
void add_variables(NonnullRefPtrVector<VariableDeclaration>);
125128
void add_functions(NonnullRefPtrVector<FunctionDeclaration>);
@@ -522,6 +525,7 @@ class BinaryExpression final : public Expression {
522525

523526
virtual Value execute(Interpreter&, GlobalObject&) const override;
524527
virtual void dump(int indent) const override;
528+
virtual Optional<Bytecode::Register> generate_bytecode(Bytecode::Generator&) const override;
525529

526530
private:
527531
BinaryOp m_op;
@@ -630,6 +634,7 @@ class NumericLiteral final : public Literal {
630634

631635
virtual Value execute(Interpreter&, GlobalObject&) const override;
632636
virtual void dump(int indent) const override;
637+
virtual Optional<Bytecode::Register> generate_bytecode(Bytecode::Generator&) const override;
633638

634639
private:
635640
Value m_value;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#include <AK/String.h>
8+
#include <LibJS/Bytecode/Block.h>
9+
#include <LibJS/Bytecode/Instruction.h>
10+
11+
namespace JS::Bytecode {
12+
13+
NonnullOwnPtr<Block> Block::create()
14+
{
15+
return adopt_own(*new Block);
16+
}
17+
18+
Block::Block()
19+
{
20+
}
21+
22+
Block::~Block()
23+
{
24+
}
25+
26+
void Block::append(Badge<Bytecode::Generator>, NonnullOwnPtr<Instruction> instruction)
27+
{
28+
m_instructions.append(move(instruction));
29+
}
30+
31+
void Block::dump() const
32+
{
33+
for (size_t i = 0; i < m_instructions.size(); ++i) {
34+
warnln("[{:3}] {}", i, m_instructions[i].to_string());
35+
}
36+
}
37+
38+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#pragma once
8+
9+
#include <AK/Badge.h>
10+
#include <AK/NonnullOwnPtrVector.h>
11+
#include <LibJS/Forward.h>
12+
13+
namespace JS::Bytecode {
14+
15+
class Block {
16+
public:
17+
static NonnullOwnPtr<Block> create();
18+
~Block();
19+
20+
NonnullOwnPtrVector<Instruction> const& instructions() const { return m_instructions; }
21+
void dump() const;
22+
23+
size_t register_count() const { return m_register_count; }
24+
25+
void append(Badge<Bytecode::Generator>, NonnullOwnPtr<Instruction>);
26+
void set_register_count(Badge<Bytecode::Generator>, size_t count) { m_register_count = count; }
27+
28+
private:
29+
Block();
30+
31+
size_t m_register_count { 0 };
32+
NonnullOwnPtrVector<Instruction> m_instructions;
33+
};
34+
35+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#include <AK/OwnPtr.h>
8+
#include <LibJS/AST.h>
9+
#include <LibJS/Bytecode/Block.h>
10+
#include <LibJS/Bytecode/Generator.h>
11+
#include <LibJS/Bytecode/Instruction.h>
12+
#include <LibJS/Bytecode/Register.h>
13+
#include <LibJS/Forward.h>
14+
15+
namespace JS::Bytecode {
16+
17+
Generator::Generator()
18+
{
19+
m_block = Block::create();
20+
}
21+
22+
Generator::~Generator()
23+
{
24+
}
25+
26+
OwnPtr<Block> Generator::generate(ASTNode const& node)
27+
{
28+
Generator generator;
29+
[[maybe_unused]] auto dummy = node.generate_bytecode(generator);
30+
generator.m_block->set_register_count({}, generator.m_next_register);
31+
return move(generator.m_block);
32+
}
33+
34+
void Generator::append(NonnullOwnPtr<Instruction> instruction)
35+
{
36+
m_block->append({}, move(instruction));
37+
}
38+
39+
Register Generator::allocate_register()
40+
{
41+
VERIFY(m_next_register != NumericLimits<u32>::max());
42+
return Register { m_next_register++ };
43+
}
44+
45+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#pragma once
8+
9+
#include <AK/OwnPtr.h>
10+
#include <LibJS/Forward.h>
11+
12+
namespace JS::Bytecode {
13+
14+
class Generator {
15+
public:
16+
static OwnPtr<Block> generate(ASTNode const&);
17+
18+
Register allocate_register();
19+
20+
template<typename OpType, typename... Args>
21+
void emit(Args&&... args)
22+
{
23+
auto instruction = make<OpType>(forward<Args>(args)...);
24+
append(move(instruction));
25+
}
26+
27+
private:
28+
Generator();
29+
~Generator();
30+
31+
void append(NonnullOwnPtr<Instruction>);
32+
33+
OwnPtr<Block> m_block;
34+
u32 m_next_register { 1 };
35+
};
36+
37+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#include <LibJS/Bytecode/Instruction.h>
8+
9+
namespace JS::Bytecode {
10+
11+
Instruction::~Instruction()
12+
{
13+
}
14+
15+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#pragma once
8+
9+
#include <AK/Forward.h>
10+
#include <LibJS/Forward.h>
11+
12+
namespace JS::Bytecode {
13+
14+
class Instruction {
15+
public:
16+
virtual ~Instruction();
17+
18+
virtual String to_string() const = 0;
19+
virtual void execute(Bytecode::Interpreter&) const = 0;
20+
};
21+
22+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#include <LibJS/Bytecode/Block.h>
8+
#include <LibJS/Bytecode/Instruction.h>
9+
#include <LibJS/Bytecode/Interpreter.h>
10+
11+
namespace JS::Bytecode {
12+
13+
Interpreter::Interpreter(GlobalObject& global_object)
14+
: m_global_object(global_object)
15+
{
16+
}
17+
18+
Interpreter::~Interpreter()
19+
{
20+
}
21+
22+
void Interpreter::run(Bytecode::Block const& block)
23+
{
24+
dbgln("Bytecode::Interpreter will run block {:p}", &block);
25+
26+
m_registers.resize(block.register_count());
27+
28+
for (auto& instruction : block.instructions())
29+
instruction.execute(*this);
30+
31+
dbgln("Bytecode::Interpreter did run block {:p}", &block);
32+
for (size_t i = 0; i < m_registers.size(); ++i) {
33+
String value_string;
34+
if (m_registers[i].is_empty())
35+
value_string = "(empty)";
36+
else
37+
value_string = m_registers[i].to_string_without_side_effects();
38+
dbgln("[{:3}] {}", i, value_string);
39+
}
40+
}
41+
42+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#pragma once
8+
9+
#include <LibJS/Bytecode/Register.h>
10+
#include <LibJS/Forward.h>
11+
#include <LibJS/Heap/Cell.h>
12+
#include <LibJS/Runtime/Value.h>
13+
14+
namespace JS::Bytecode {
15+
16+
class Interpreter {
17+
public:
18+
explicit Interpreter(GlobalObject&);
19+
~Interpreter();
20+
21+
GlobalObject& global_object() { return m_global_object; }
22+
23+
void run(Bytecode::Block const&);
24+
25+
Value& reg(Register const& r) { return m_registers[r.index()]; }
26+
27+
private:
28+
GlobalObject& m_global_object;
29+
Vector<Value> m_registers;
30+
};
31+
32+
}

0 commit comments

Comments
 (0)