mirror of
https://github.com/RGBCube/serenity
synced 2025-05-20 18:05:07 +00:00
LibJS: Add basic support for while loops in the bytecode engine
This introduces two new instructions: Jump and JumpIfFalse. Jumps are made to a Bytecode::Label, which is a simple object that represents a location in the bytecode stream. Note that you may not always know the target of a jump when adding the jump instruction itself, but we can just update the instruction later on during codegen once we know where the jump target is. The Bytecode::Interpreter now implements jumping via a jump slot that gets checked after each instruction to see if a jump is pending. If not, we just increment the PC as usual.
This commit is contained in:
parent
91640d0727
commit
6ae9346cd3
9 changed files with 128 additions and 1 deletions
|
@ -370,6 +370,7 @@ public:
|
|||
|
||||
virtual Value execute(Interpreter&, GlobalObject&) const override;
|
||||
virtual void dump(int indent) const override;
|
||||
virtual Optional<Bytecode::Register> generate_bytecode(Bytecode::Generator&) const override;
|
||||
|
||||
private:
|
||||
NonnullRefPtr<Expression> m_test;
|
||||
|
|
|
@ -90,4 +90,16 @@ Optional<Bytecode::Register> AssignmentExpression::generate_bytecode(Bytecode::G
|
|||
TODO();
|
||||
}
|
||||
|
||||
Optional<Bytecode::Register> WhileStatement::generate_bytecode(Bytecode::Generator& generator) const
|
||||
{
|
||||
auto test_label = generator.make_label();
|
||||
auto test_result_reg = m_test->generate_bytecode(generator);
|
||||
VERIFY(test_result_reg.has_value());
|
||||
auto& test_jump = generator.emit<Bytecode::Op::JumpIfFalse>(*test_result_reg);
|
||||
auto body_result_reg = m_body->generate_bytecode(generator);
|
||||
generator.emit<Bytecode::Op::Jump>(test_label);
|
||||
test_jump.set_target(generator.make_label());
|
||||
return body_result_reg;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -42,4 +42,9 @@ Register Generator::allocate_register()
|
|||
return Register { m_next_register++ };
|
||||
}
|
||||
|
||||
Label Generator::make_label() const
|
||||
{
|
||||
return Label { m_block->instructions().size() };
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <LibJS/Bytecode/Label.h>
|
||||
#include <LibJS/Forward.h>
|
||||
|
||||
namespace JS::Bytecode {
|
||||
|
@ -26,6 +27,8 @@ public:
|
|||
return *ptr;
|
||||
}
|
||||
|
||||
Label make_label() const;
|
||||
|
||||
private:
|
||||
Generator();
|
||||
~Generator();
|
||||
|
|
|
@ -27,8 +27,16 @@ void Interpreter::run(Bytecode::Block const& block)
|
|||
|
||||
m_registers.resize(block.register_count());
|
||||
|
||||
for (auto& instruction : block.instructions())
|
||||
size_t pc = 0;
|
||||
while (pc < block.instructions().size()) {
|
||||
auto& instruction = block.instructions()[pc];
|
||||
instruction.execute(*this);
|
||||
if (m_pending_jump.has_value()) {
|
||||
pc = m_pending_jump.release_value();
|
||||
continue;
|
||||
}
|
||||
++pc;
|
||||
}
|
||||
|
||||
dbgln("Bytecode::Interpreter did run block {:p}", &block);
|
||||
for (size_t i = 0; i < m_registers.size(); ++i) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Bytecode/Label.h>
|
||||
#include <LibJS/Bytecode/Register.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Heap/Cell.h>
|
||||
|
@ -25,10 +26,13 @@ public:
|
|||
|
||||
Value& reg(Register const& r) { return m_registers[r.index()]; }
|
||||
|
||||
void jump(Label const& label) { m_pending_jump = label.address(); }
|
||||
|
||||
private:
|
||||
VM& m_vm;
|
||||
GlobalObject& m_global_object;
|
||||
Vector<Value> m_registers;
|
||||
Optional<size_t> m_pending_jump;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
34
Userland/Libraries/LibJS/Bytecode/Label.h
Normal file
34
Userland/Libraries/LibJS/Bytecode/Label.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Format.h>
|
||||
|
||||
namespace JS::Bytecode {
|
||||
|
||||
class Label {
|
||||
public:
|
||||
explicit Label(size_t address)
|
||||
: m_address(address)
|
||||
{
|
||||
}
|
||||
|
||||
size_t address() const { return m_address; }
|
||||
|
||||
private:
|
||||
size_t m_address { 0 };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
struct AK::Formatter<JS::Bytecode::Label> : AK::Formatter<FormatString> {
|
||||
void format(FormatBuilder& builder, JS::Bytecode::Label const& value)
|
||||
{
|
||||
return AK::Formatter<FormatString>::format(builder, "@{}", value.address());
|
||||
}
|
||||
};
|
|
@ -46,6 +46,19 @@ void SetVariable::execute(Bytecode::Interpreter& interpreter) const
|
|||
interpreter.vm().set_variable(m_identifier, interpreter.reg(m_src), interpreter.global_object());
|
||||
}
|
||||
|
||||
void Jump::execute(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
interpreter.jump(m_target);
|
||||
}
|
||||
|
||||
void JumpIfFalse::execute(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
VERIFY(m_target.has_value());
|
||||
auto result = interpreter.reg(m_result);
|
||||
if (!result.as_bool())
|
||||
interpreter.jump(m_target.value());
|
||||
}
|
||||
|
||||
String Load::to_string() const
|
||||
{
|
||||
return String::formatted("Load dst:{}, value:{}", m_dst, m_value.to_string_without_side_effects());
|
||||
|
@ -81,4 +94,16 @@ String SetVariable::to_string() const
|
|||
return String::formatted("SetVariable identifier:{}, src:{}", m_identifier, m_src);
|
||||
}
|
||||
|
||||
String Jump::to_string() const
|
||||
{
|
||||
return String::formatted("Jump {}", m_target);
|
||||
}
|
||||
|
||||
String JumpIfFalse::to_string() const
|
||||
{
|
||||
if (m_target.has_value())
|
||||
return String::formatted("JumpIfFalse result:{}, target:{}", m_result, m_target.value());
|
||||
return String::formatted("JumpIfFalse result:{}, target:<empty>", m_result);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <AK/FlyString.h>
|
||||
#include <LibJS/Bytecode/Instruction.h>
|
||||
#include <LibJS/Bytecode/Label.h>
|
||||
#include <LibJS/Bytecode/Register.h>
|
||||
#include <LibJS/Heap/Cell.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
|
@ -139,4 +140,38 @@ private:
|
|||
FlyString m_identifier;
|
||||
};
|
||||
|
||||
class Jump final : public Instruction {
|
||||
public:
|
||||
explicit Jump(Label target)
|
||||
: m_target(target)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~Jump() override { }
|
||||
virtual void execute(Bytecode::Interpreter&) const override;
|
||||
virtual String to_string() const override;
|
||||
|
||||
private:
|
||||
Label m_target;
|
||||
};
|
||||
|
||||
class JumpIfFalse final : public Instruction {
|
||||
public:
|
||||
explicit JumpIfFalse(Register result, Optional<Label> target = {})
|
||||
: m_result(result)
|
||||
, m_target(move(target))
|
||||
{
|
||||
}
|
||||
|
||||
void set_target(Optional<Label> target) { m_target = move(target); }
|
||||
|
||||
virtual ~JumpIfFalse() override { }
|
||||
virtual void execute(Bytecode::Interpreter&) const override;
|
||||
virtual String to_string() const override;
|
||||
|
||||
private:
|
||||
Register m_result;
|
||||
Optional<Label> m_target;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue