1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-22 16:55:09 +00:00

LibJS: Introduce an accumulator register to Bytecode::Interpreter

This commit introduces the concept of an accumulator register to
LibJS's bytecode interpreter. The accumulator register is always
register 0, and most simple instructions use it for reading and
writing.

Not only does this slim down the AST, but it also simplifies a lot of
the code. For example, the generate_bytecode methods no longer need
to return an Optional<Register>, as any opcode which has a "return"
value will always put it into the accumulator.

This also renames the old Op::Load to Op::LoadImmediate, and uses
Op::Load to load from a register into the accumulator. There is
also an Op::Store to put the value in the accumulator into another
register.
This commit is contained in:
Matthew Olsson 2021-06-07 20:58:36 -07:00 committed by Andreas Kling
parent 6c256bb400
commit 9bed2e4f4a
8 changed files with 377 additions and 432 deletions

View file

@ -37,7 +37,7 @@ class ASTNode : public RefCounted<ASTNode> {
public:
virtual ~ASTNode() { }
virtual Value execute(Interpreter&, GlobalObject&) const = 0;
virtual Optional<Bytecode::Register> generate_bytecode(Bytecode::Generator&) const;
virtual void generate_bytecode(Bytecode::Generator&) const;
virtual void dump(int indent) const;
const SourceRange& source_range() const { return m_source_range; }
@ -76,7 +76,7 @@ public:
{
}
Value execute(Interpreter&, GlobalObject&) const override { return {}; }
virtual Optional<Bytecode::Register> generate_bytecode(Bytecode::Generator&) const override;
virtual void generate_bytecode(Bytecode::Generator&) const override;
};
class ErrorStatement final : public Statement {
@ -98,7 +98,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
const Expression& expression() const { return m_expression; };
@ -123,7 +123,7 @@ public:
const NonnullRefPtrVector<Statement>& children() const { return m_children; }
virtual Value execute(Interpreter&, GlobalObject&) const override;
virtual void dump(int indent) const override;
virtual Optional<Bytecode::Register> generate_bytecode(Bytecode::Generator&) const override;
virtual void generate_bytecode(Bytecode::Generator&) const override;
void add_variables(NonnullRefPtrVector<VariableDeclaration>);
void add_functions(NonnullRefPtrVector<FunctionDeclaration>);
@ -273,7 +273,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
};
class FunctionExpression final
@ -330,7 +330,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
private:
RefPtr<Expression> m_argument;
@ -352,7 +352,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
private:
NonnullRefPtr<Expression> m_predicate;
@ -374,7 +374,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
private:
NonnullRefPtr<Expression> m_test;
@ -395,7 +395,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
private:
NonnullRefPtr<Expression> m_test;
@ -440,7 +440,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
private:
RefPtr<ASTNode> m_init;
@ -532,7 +532,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
private:
BinaryOp m_op;
@ -558,7 +558,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
private:
LogicalOp m_op;
@ -587,7 +587,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
private:
UnaryOp m_op;
@ -605,7 +605,7 @@ public:
virtual void dump(int indent) const override;
virtual Value execute(Interpreter&, GlobalObject&) const override;
virtual Optional<Bytecode::Register> generate_bytecode(Bytecode::Generator&) const override;
virtual void generate_bytecode(Bytecode::Generator&) const override;
private:
NonnullRefPtrVector<Expression> m_expressions;
@ -629,7 +629,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
private:
bool m_value { false };
@ -645,7 +645,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
private:
Value m_value;
@ -661,7 +661,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
private:
String m_value;
@ -678,7 +678,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
StringView value() const { return m_value; }
bool is_use_strict_directive() const { return m_is_use_strict_directive; };
@ -697,7 +697,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
};
class RegExpLiteral final : public Literal {
@ -733,7 +733,7 @@ public:
virtual Value execute(Interpreter&, GlobalObject&) const override;
virtual void dump(int indent) const override;
virtual Reference to_reference(Interpreter&, GlobalObject&) const override;
virtual Optional<Bytecode::Register> generate_bytecode(Bytecode::Generator&) const override;
virtual void generate_bytecode(Bytecode::Generator&) const override;
private:
FlyString m_string;
@ -860,7 +860,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
private:
struct ThisAndCallee {
@ -912,7 +912,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
private:
AssignmentOp m_op;
@ -1052,7 +1052,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
private:
NonnullRefPtrVector<ObjectProperty> m_properties;
@ -1092,7 +1092,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;
virtual void generate_bytecode(Bytecode::Generator&) const override;
const NonnullRefPtrVector<Expression>& expressions() const { return m_expressions; }
const NonnullRefPtrVector<Expression>& raw_strings() const { return m_raw_strings; }
@ -1132,7 +1132,7 @@ public:
virtual Value execute(Interpreter&, GlobalObject&) const override;
virtual void dump(int indent) const override;
virtual Reference to_reference(Interpreter&, GlobalObject&) const override;
virtual Optional<Bytecode::Register> generate_bytecode(Bytecode::Generator&) const override;
virtual void generate_bytecode(Bytecode::Generator&) const override;
bool is_computed() const { return m_computed; }
const Expression& object() const { return *m_object; }
@ -1180,7 +1180,7 @@ public:
virtual void dump(int indent) const override;
virtual Value execute(Interpreter&, GlobalObject&) const override;
virtual Optional<Bytecode::Register> generate_bytecode(Bytecode::Generator&) const override;
virtual void generate_bytecode(Bytecode::Generator&) const override;
private:
NonnullRefPtr<Expression> m_test;
@ -1310,7 +1310,7 @@ public:
}
virtual Value execute(Interpreter&, GlobalObject&) const override;
virtual Optional<Bytecode::Register> generate_bytecode(Bytecode::Generator&) const override;
virtual void generate_bytecode(Bytecode::Generator&) const override;
const FlyString& target_label() const { return m_target_label; }
@ -1326,7 +1326,7 @@ public:
}
virtual Value execute(Interpreter&, GlobalObject&) const override;
virtual Optional<Bytecode::Register> generate_bytecode(Bytecode::Generator&) const override;
virtual void generate_bytecode(Bytecode::Generator&) const override;
};
template<typename C>

View file

@ -13,489 +13,422 @@
namespace JS {
Optional<Bytecode::Register> ASTNode::generate_bytecode(Bytecode::Generator&) const
void ASTNode::generate_bytecode(Bytecode::Generator&) const
{
dbgln("Missing generate_bytecode()");
TODO();
}
Optional<Bytecode::Register> ScopeNode::generate_bytecode(Bytecode::Generator& generator) const
void ScopeNode::generate_bytecode(Bytecode::Generator& generator) const
{
generator.emit<Bytecode::Op::EnterScope>(*this);
Optional<Bytecode::Register> last_value_reg;
for (auto& child : children()) {
last_value_reg = child.generate_bytecode(generator);
}
return last_value_reg;
for (auto& child : children())
child.generate_bytecode(generator);
}
Optional<Bytecode::Register> EmptyStatement::generate_bytecode(Bytecode::Generator&) const
void EmptyStatement::generate_bytecode(Bytecode::Generator&) const
{
return {};
}
Optional<Bytecode::Register> ExpressionStatement::generate_bytecode(Bytecode::Generator& generator) const
void ExpressionStatement::generate_bytecode(Bytecode::Generator& generator) const
{
return m_expression->generate_bytecode(generator);
m_expression->generate_bytecode(generator);
}
Optional<Bytecode::Register> BinaryExpression::generate_bytecode(Bytecode::Generator& generator) const
void BinaryExpression::generate_bytecode(Bytecode::Generator& generator) const
{
auto lhs_reg = m_lhs->generate_bytecode(generator);
auto rhs_reg = m_rhs->generate_bytecode(generator);
m_lhs->generate_bytecode(generator);
auto lhs_reg = generator.allocate_register();
generator.emit<Bytecode::Op::Store>(lhs_reg);
VERIFY(lhs_reg.has_value());
VERIFY(rhs_reg.has_value());
auto dst_reg = generator.allocate_register();
m_rhs->generate_bytecode(generator);
switch (m_op) {
case BinaryOp::Addition:
generator.emit<Bytecode::Op::Add>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::Add>(lhs_reg);
break;
case BinaryOp::Subtraction:
generator.emit<Bytecode::Op::Sub>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::Sub>(lhs_reg);
break;
case BinaryOp::Multiplication:
generator.emit<Bytecode::Op::Mul>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::Mul>(lhs_reg);
break;
case BinaryOp::Division:
generator.emit<Bytecode::Op::Div>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::Div>(lhs_reg);
break;
case BinaryOp::Modulo:
generator.emit<Bytecode::Op::Mod>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::Mod>(lhs_reg);
break;
case BinaryOp::Exponentiation:
generator.emit<Bytecode::Op::Exp>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::Exp>(lhs_reg);
break;
case BinaryOp::GreaterThan:
generator.emit<Bytecode::Op::GreaterThan>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::GreaterThan>(lhs_reg);
break;
case BinaryOp::GreaterThanEquals:
generator.emit<Bytecode::Op::GreaterThanEquals>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::GreaterThanEquals>(lhs_reg);
break;
case BinaryOp::LessThan:
generator.emit<Bytecode::Op::LessThan>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::LessThan>(lhs_reg);
break;
case BinaryOp::LessThanEquals:
generator.emit<Bytecode::Op::LessThanEquals>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::LessThanEquals>(lhs_reg);
break;
case BinaryOp::AbstractInequals:
generator.emit<Bytecode::Op::AbstractInequals>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::AbstractInequals>(lhs_reg);
break;
case BinaryOp::AbstractEquals:
generator.emit<Bytecode::Op::AbstractEquals>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::AbstractEquals>(lhs_reg);
break;
case BinaryOp::TypedInequals:
generator.emit<Bytecode::Op::TypedInequals>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::TypedInequals>(lhs_reg);
break;
case BinaryOp::TypedEquals:
generator.emit<Bytecode::Op::TypedEquals>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::TypedEquals>(lhs_reg);
break;
case BinaryOp::BitwiseAnd:
generator.emit<Bytecode::Op::BitwiseAnd>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::BitwiseAnd>(lhs_reg);
break;
case BinaryOp::BitwiseOr:
generator.emit<Bytecode::Op::BitwiseOr>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::BitwiseOr>(lhs_reg);
break;
case BinaryOp::BitwiseXor:
generator.emit<Bytecode::Op::BitwiseXor>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::BitwiseXor>(lhs_reg);
break;
case BinaryOp::LeftShift:
generator.emit<Bytecode::Op::LeftShift>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::LeftShift>(lhs_reg);
break;
case BinaryOp::RightShift:
generator.emit<Bytecode::Op::RightShift>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::RightShift>(lhs_reg);
break;
case BinaryOp::UnsignedRightShift:
generator.emit<Bytecode::Op::UnsignedRightShift>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::UnsignedRightShift>(lhs_reg);
break;
case BinaryOp::In:
generator.emit<Bytecode::Op::In>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::In>(lhs_reg);
break;
case BinaryOp::InstanceOf:
generator.emit<Bytecode::Op::InstanceOf>(dst_reg, *lhs_reg, *rhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::InstanceOf>(lhs_reg);
break;
default:
VERIFY_NOT_REACHED();
}
}
Optional<Bytecode::Register> LogicalExpression::generate_bytecode(Bytecode::Generator& generator) const
void LogicalExpression::generate_bytecode(Bytecode::Generator& generator) const
{
auto result_reg = generator.allocate_register();
auto lhs_reg = m_lhs->generate_bytecode(generator);
m_lhs->generate_bytecode(generator);
Bytecode::Op::Jump* test_instr;
switch (m_op) {
case LogicalOp::And:
test_instr = &generator.emit<Bytecode::Op::JumpIfTrue>(*lhs_reg);
test_instr = &generator.emit<Bytecode::Op::JumpIfFalse>();
break;
case LogicalOp::Or:
test_instr = &generator.emit<Bytecode::Op::JumpIfFalse>(*lhs_reg);
test_instr = &generator.emit<Bytecode::Op::JumpIfTrue>();
break;
case LogicalOp::NullishCoalescing:
test_instr = &generator.emit<Bytecode::Op::JumpIfNullish>(*lhs_reg);
test_instr = &generator.emit<Bytecode::Op::JumpIfNotNullish>();
break;
default:
VERIFY_NOT_REACHED();
}
generator.emit<Bytecode::Op::LoadRegister>(result_reg, *lhs_reg);
auto& end_jump = generator.emit<Bytecode::Op::Jump>();
auto rhs_label = generator.make_label();
test_instr->set_target(rhs_label);
auto rhs_reg = m_rhs->generate_bytecode(generator);
generator.emit<Bytecode::Op::LoadRegister>(result_reg, *rhs_reg);
end_jump.set_target(generator.make_label());
return result_reg;
m_rhs->generate_bytecode(generator);
test_instr->set_target(generator.make_label());
}
Optional<Bytecode::Register> UnaryExpression::generate_bytecode(Bytecode::Generator& generator) const
void UnaryExpression::generate_bytecode(Bytecode::Generator& generator) const
{
auto lhs_reg = m_lhs->generate_bytecode(generator);
VERIFY(lhs_reg.has_value());
auto dst_reg = generator.allocate_register();
m_lhs->generate_bytecode(generator);
switch (m_op) {
case UnaryOp::BitwiseNot:
generator.emit<Bytecode::Op::BitwiseNot>(dst_reg, *lhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::BitwiseNot>();
break;
case UnaryOp::Not:
generator.emit<Bytecode::Op::Not>(dst_reg, *lhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::Not>();
break;
case UnaryOp::Plus:
generator.emit<Bytecode::Op::UnaryPlus>(dst_reg, *lhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::UnaryPlus>();
break;
case UnaryOp::Minus:
generator.emit<Bytecode::Op::UnaryMinus>(dst_reg, *lhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::UnaryMinus>();
break;
case UnaryOp::Typeof:
generator.emit<Bytecode::Op::Typeof>(dst_reg, *lhs_reg);
return dst_reg;
generator.emit<Bytecode::Op::Typeof>();
break;
case UnaryOp::Void:
generator.emit<Bytecode::Op::Load>(dst_reg, js_undefined());
return dst_reg;
generator.emit<Bytecode::Op::LoadImmediate>(js_undefined());
break;
default:
TODO();
}
}
Optional<Bytecode::Register> NumericLiteral::generate_bytecode(Bytecode::Generator& generator) const
void NumericLiteral::generate_bytecode(Bytecode::Generator& generator) const
{
auto dst = generator.allocate_register();
generator.emit<Bytecode::Op::Load>(dst, m_value);
return dst;
generator.emit<Bytecode::Op::LoadImmediate>(m_value);
}
Optional<Bytecode::Register> BooleanLiteral::generate_bytecode(Bytecode::Generator& generator) const
void BooleanLiteral::generate_bytecode(Bytecode::Generator& generator) const
{
auto dst = generator.allocate_register();
generator.emit<Bytecode::Op::Load>(dst, Value(m_value));
return dst;
generator.emit<Bytecode::Op::LoadImmediate>(Value(m_value));
}
Optional<Bytecode::Register> NullLiteral::generate_bytecode(Bytecode::Generator& generator) const
void NullLiteral::generate_bytecode(Bytecode::Generator& generator) const
{
auto dst = generator.allocate_register();
generator.emit<Bytecode::Op::Load>(dst, js_null());
return dst;
generator.emit<Bytecode::Op::LoadImmediate>(js_null());
}
Optional<Bytecode::Register> BigIntLiteral::generate_bytecode(Bytecode::Generator& generator) const
void BigIntLiteral::generate_bytecode(Bytecode::Generator& generator) const
{
auto dst = generator.allocate_register();
generator.emit<Bytecode::Op::NewBigInt>(dst, Crypto::SignedBigInteger::from_base10(m_value.substring(0, m_value.length() - 1)));
return dst;
generator.emit<Bytecode::Op::NewBigInt>(Crypto::SignedBigInteger::from_base10(m_value.substring(0, m_value.length() - 1)));
}
Optional<Bytecode::Register> StringLiteral::generate_bytecode(Bytecode::Generator& generator) const
void StringLiteral::generate_bytecode(Bytecode::Generator& generator) const
{
auto dst = generator.allocate_register();
generator.emit<Bytecode::Op::NewString>(dst, m_value);
return dst;
generator.emit<Bytecode::Op::NewString>(m_value);
}
Optional<Bytecode::Register> Identifier::generate_bytecode(Bytecode::Generator& generator) const
void Identifier::generate_bytecode(Bytecode::Generator& generator) const
{
auto reg = generator.allocate_register();
generator.emit<Bytecode::Op::GetVariable>(reg, m_string);
return reg;
generator.emit<Bytecode::Op::GetVariable>(m_string);
}
Optional<Bytecode::Register> AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) const
void AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) const
{
if (is<Identifier>(*m_lhs)) {
auto& identifier = static_cast<Identifier const&>(*m_lhs);
auto rhs_reg = m_rhs->generate_bytecode(generator);
VERIFY(rhs_reg.has_value());
if (m_op == AssignmentOp::Assignment) {
generator.emit<Bytecode::Op::SetVariable>(identifier.string(), *rhs_reg);
return rhs_reg;
m_rhs->generate_bytecode(generator);
generator.emit<Bytecode::Op::SetVariable>(identifier.string());
return;
}
auto lhs_reg = m_lhs->generate_bytecode(generator);
auto dst_reg = generator.allocate_register();
m_lhs->generate_bytecode(generator);
auto lhs_reg = generator.allocate_register();
generator.emit<Bytecode::Op::Store>(lhs_reg);
m_rhs->generate_bytecode(generator);
switch (m_op) {
case AssignmentOp::AdditionAssignment:
generator.emit<Bytecode::Op::Add>(dst_reg, *lhs_reg, *rhs_reg);
generator.emit<Bytecode::Op::Add>(lhs_reg);
break;
case AssignmentOp::SubtractionAssignment:
generator.emit<Bytecode::Op::Sub>(dst_reg, *lhs_reg, *rhs_reg);
generator.emit<Bytecode::Op::Sub>(lhs_reg);
break;
case AssignmentOp::MultiplicationAssignment:
generator.emit<Bytecode::Op::Mul>(dst_reg, *lhs_reg, *rhs_reg);
generator.emit<Bytecode::Op::Mul>(lhs_reg);
break;
case AssignmentOp::DivisionAssignment:
generator.emit<Bytecode::Op::Div>(dst_reg, *lhs_reg, *rhs_reg);
generator.emit<Bytecode::Op::Div>(lhs_reg);
break;
case AssignmentOp::ModuloAssignment:
generator.emit<Bytecode::Op::Mod>(dst_reg, *lhs_reg, *rhs_reg);
generator.emit<Bytecode::Op::Mod>(lhs_reg);
break;
case AssignmentOp::ExponentiationAssignment:
generator.emit<Bytecode::Op::Exp>(dst_reg, *lhs_reg, *rhs_reg);
generator.emit<Bytecode::Op::Exp>(lhs_reg);
break;
case AssignmentOp::BitwiseAndAssignment:
generator.emit<Bytecode::Op::BitwiseAnd>(dst_reg, *lhs_reg, *rhs_reg);
generator.emit<Bytecode::Op::BitwiseAnd>(lhs_reg);
break;
case AssignmentOp::BitwiseOrAssignment:
generator.emit<Bytecode::Op::BitwiseOr>(dst_reg, *lhs_reg, *rhs_reg);
generator.emit<Bytecode::Op::BitwiseOr>(lhs_reg);
break;
case AssignmentOp::BitwiseXorAssignment:
generator.emit<Bytecode::Op::BitwiseXor>(dst_reg, *lhs_reg, *rhs_reg);
generator.emit<Bytecode::Op::BitwiseXor>(lhs_reg);
break;
case AssignmentOp::LeftShiftAssignment:
generator.emit<Bytecode::Op::LeftShift>(dst_reg, *lhs_reg, *rhs_reg);
generator.emit<Bytecode::Op::LeftShift>(lhs_reg);
break;
case AssignmentOp::RightShiftAssignment:
generator.emit<Bytecode::Op::RightShift>(dst_reg, *lhs_reg, *rhs_reg);
generator.emit<Bytecode::Op::RightShift>(lhs_reg);
break;
case AssignmentOp::UnsignedRightShiftAssignment:
generator.emit<Bytecode::Op::UnsignedRightShift>(dst_reg, *lhs_reg, *rhs_reg);
generator.emit<Bytecode::Op::UnsignedRightShift>(lhs_reg);
break;
default:
TODO();
}
generator.emit<Bytecode::Op::SetVariable>(identifier.string(), dst_reg);
generator.emit<Bytecode::Op::SetVariable>(identifier.string());
return dst_reg;
return;
}
if (is<MemberExpression>(*m_lhs)) {
auto& expression = static_cast<MemberExpression const&>(*m_lhs);
auto object_reg = expression.object().generate_bytecode(generator);
expression.object().generate_bytecode(generator);
auto object_reg = generator.allocate_register();
generator.emit<Bytecode::Op::Store>(object_reg);
if (expression.is_computed()) {
TODO();
} else {
VERIFY(is<Identifier>(expression.property()));
auto rhs_reg = m_rhs->generate_bytecode(generator);
generator.emit<Bytecode::Op::PutById>(*object_reg, static_cast<Identifier const&>(expression.property()).string(), *rhs_reg);
return rhs_reg;
m_rhs->generate_bytecode(generator);
generator.emit<Bytecode::Op::PutById>(object_reg, static_cast<Identifier const&>(expression.property()).string());
return;
}
}
TODO();
}
Optional<Bytecode::Register> WhileStatement::generate_bytecode(Bytecode::Generator& generator) const
void WhileStatement::generate_bytecode(Bytecode::Generator& generator) const
{
generator.begin_continuable_scope();
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);
m_test->generate_bytecode(generator);
auto& test_jump = generator.emit<Bytecode::Op::JumpIfFalse>();
m_body->generate_bytecode(generator);
generator.emit<Bytecode::Op::Jump>(test_label);
test_jump.set_target(generator.make_label());
generator.end_continuable_scope();
return body_result_reg;
}
Optional<Bytecode::Register> DoWhileStatement::generate_bytecode(Bytecode::Generator& generator) const
void DoWhileStatement::generate_bytecode(Bytecode::Generator& generator) const
{
generator.begin_continuable_scope();
auto head_label = generator.make_label();
auto body_result_reg = m_body->generate_bytecode(generator);
m_body->generate_bytecode(generator);
generator.end_continuable_scope();
auto test_result_reg = m_test->generate_bytecode(generator);
VERIFY(test_result_reg.has_value());
generator.emit<Bytecode::Op::JumpIfTrue>(*test_result_reg, head_label);
return body_result_reg;
m_test->generate_bytecode(generator);
generator.emit<Bytecode::Op::JumpIfTrue>(head_label);
}
Optional<Bytecode::Register> ForStatement::generate_bytecode(Bytecode::Generator& generator) const
void ForStatement::generate_bytecode(Bytecode::Generator& generator) const
{
Bytecode::Op::Jump* test_jump { nullptr };
if (m_init) {
[[maybe_unused]] auto init_result_reg = m_init->generate_bytecode(generator);
}
if (m_init)
m_init->generate_bytecode(generator);
generator.begin_continuable_scope();
auto jump_label = generator.make_label();
if (m_test) {
auto test_result_reg = m_test->generate_bytecode(generator);
VERIFY(test_result_reg.has_value());
test_jump = &generator.emit<Bytecode::Op::JumpIfFalse>(*test_result_reg);
}
auto body_result_reg = m_body->generate_bytecode(generator);
if (m_update) {
[[maybe_unused]] auto update_result_reg = m_update->generate_bytecode(generator);
m_test->generate_bytecode(generator);
test_jump = &generator.emit<Bytecode::Op::JumpIfFalse>();
}
m_body->generate_bytecode(generator);
if (m_update)
m_update->generate_bytecode(generator);
generator.emit<Bytecode::Op::Jump>(jump_label);
if (m_test)
test_jump->set_target(generator.make_label());
generator.end_continuable_scope();
return body_result_reg;
}
Optional<Bytecode::Register> ObjectExpression::generate_bytecode(Bytecode::Generator& generator) const
void ObjectExpression::generate_bytecode(Bytecode::Generator& generator) const
{
auto reg = generator.allocate_register();
generator.emit<Bytecode::Op::NewObject>(reg);
generator.emit<Bytecode::Op::NewObject>();
if (!m_properties.is_empty()) {
if (!m_properties.is_empty())
TODO();
}
return reg;
}
Optional<Bytecode::Register> MemberExpression::generate_bytecode(Bytecode::Generator& generator) const
void MemberExpression::generate_bytecode(Bytecode::Generator& generator) const
{
auto object_reg = object().generate_bytecode(generator);
object().generate_bytecode(generator);
if (is_computed()) {
TODO();
} else {
VERIFY(is<Identifier>(property()));
auto dst_reg = generator.allocate_register();
generator.emit<Bytecode::Op::GetById>(dst_reg, *object_reg, static_cast<Identifier const&>(property()).string());
return dst_reg;
generator.emit<Bytecode::Op::GetById>(static_cast<Identifier const&>(property()).string());
}
}
Optional<Bytecode::Register> FunctionDeclaration::generate_bytecode(Bytecode::Generator&) const
void FunctionDeclaration::generate_bytecode(Bytecode::Generator&) const
{
return {};
}
Optional<Bytecode::Register> CallExpression::generate_bytecode(Bytecode::Generator& generator) const
void CallExpression::generate_bytecode(Bytecode::Generator& generator) const
{
auto callee_reg = m_callee->generate_bytecode(generator);
m_callee->generate_bytecode(generator);
auto callee_reg = generator.allocate_register();
generator.emit<Bytecode::Op::Store>(callee_reg);
// FIXME: Load the correct 'this' value into 'this_reg'.
auto this_reg = generator.allocate_register();
generator.emit<Bytecode::Op::Load>(this_reg, js_undefined());
generator.emit<Bytecode::Op::LoadImmediate>(js_undefined());
generator.emit<Bytecode::Op::Store>(this_reg);
Vector<Bytecode::Register> argument_registers;
for (auto& arg : m_arguments)
argument_registers.append(*arg.value->generate_bytecode(generator));
auto dst_reg = generator.allocate_register();
generator.emit_with_extra_register_slots<Bytecode::Op::Call>(argument_registers.size(), dst_reg, *callee_reg, this_reg, argument_registers);
return dst_reg;
for (auto& arg : m_arguments) {
arg.value->generate_bytecode(generator);
auto arg_reg = generator.allocate_register();
generator.emit<Bytecode::Op::Store>(arg_reg);
argument_registers.append(arg_reg);
}
generator.emit_with_extra_register_slots<Bytecode::Op::Call>(argument_registers.size(), callee_reg, this_reg, argument_registers);
}
Optional<Bytecode::Register> ReturnStatement::generate_bytecode(Bytecode::Generator& generator) const
void ReturnStatement::generate_bytecode(Bytecode::Generator& generator) const
{
Optional<Bytecode::Register> argument_reg;
if (m_argument)
argument_reg = m_argument->generate_bytecode(generator);
generator.emit<Bytecode::Op::Return>(argument_reg);
return argument_reg;
generator.emit<Bytecode::Op::Return>();
}
Optional<Bytecode::Register> IfStatement::generate_bytecode(Bytecode::Generator& generator) const
void IfStatement::generate_bytecode(Bytecode::Generator& generator) const
{
auto result_reg = generator.allocate_register();
auto predicate_reg = m_predicate->generate_bytecode(generator);
auto& else_jump = generator.emit<Bytecode::Op::JumpIfFalse>(*predicate_reg);
m_predicate->generate_bytecode(generator);
auto& else_jump = generator.emit<Bytecode::Op::JumpIfFalse>();
auto consequent_reg = m_consequent->generate_bytecode(generator);
generator.emit<Bytecode::Op::LoadRegister>(result_reg, *consequent_reg);
auto& end_jump = generator.emit<Bytecode::Op::Jump>();
else_jump.set_target(generator.make_label());
m_consequent->generate_bytecode(generator);
if (m_alternate) {
auto alternative_reg = m_alternate->generate_bytecode(generator);
generator.emit<Bytecode::Op::LoadRegister>(result_reg, *alternative_reg);
auto& if_jump = generator.emit<Bytecode::Op::Jump>();
else_jump.set_target(generator.make_label());
m_alternate->generate_bytecode(generator);
if_jump.set_target(generator.make_label());
} else {
generator.emit<Bytecode::Op::Load>(result_reg, js_undefined());
else_jump.set_target(generator.make_label());
}
}
end_jump.set_target(generator.make_label());
return result_reg;
}
Optional<Bytecode::Register> ContinueStatement::generate_bytecode(Bytecode::Generator& generator) const
void ContinueStatement::generate_bytecode(Bytecode::Generator& generator) const
{
generator.emit<Bytecode::Op::Jump>(generator.nearest_continuable_scope());
return {};
}
Optional<Bytecode::Register> DebuggerStatement::generate_bytecode(Bytecode::Generator&) const
void DebuggerStatement::generate_bytecode(Bytecode::Generator&) const
{
return {};
}
Optional<Bytecode::Register> ConditionalExpression::generate_bytecode(Bytecode::Generator& generator) const
void ConditionalExpression::generate_bytecode(Bytecode::Generator& generator) const
{
auto result_reg = generator.allocate_register();
auto test_reg = m_test->generate_bytecode(generator);
auto& alternate_jump = generator.emit<Bytecode::Op::JumpIfFalse>(*test_reg);
m_test->generate_bytecode(generator);
auto& alternate_jump = generator.emit<Bytecode::Op::JumpIfFalse>();
auto consequent_reg = m_consequent->generate_bytecode(generator);
generator.emit<Bytecode::Op::LoadRegister>(result_reg, *consequent_reg);
m_consequent->generate_bytecode(generator);
auto& end_jump = generator.emit<Bytecode::Op::Jump>();
alternate_jump.set_target(generator.make_label());
auto alternative_reg = m_alternate->generate_bytecode(generator);
generator.emit<Bytecode::Op::LoadRegister>(result_reg, *alternative_reg);
m_alternate->generate_bytecode(generator);
end_jump.set_target(generator.make_label());
return result_reg;
}
Optional<Bytecode::Register> SequenceExpression::generate_bytecode(Bytecode::Generator& generator) const
void SequenceExpression::generate_bytecode(Bytecode::Generator& generator) const
{
Optional<Bytecode::Register> last_reg;
for (auto& expression : m_expressions)
last_reg = expression.generate_bytecode(generator);
return last_reg;
expression.generate_bytecode(generator);
}
Optional<Bytecode::Register> TemplateLiteral::generate_bytecode(Bytecode::Generator& generator) const
void TemplateLiteral::generate_bytecode(Bytecode::Generator& generator) const
{
Optional<Bytecode::Register> result_reg;
auto string_reg = generator.allocate_register();
for (auto& expression : m_expressions) {
auto expr_reg = expression.generate_bytecode(generator);
if (!result_reg.has_value())
result_reg = expr_reg;
else
generator.emit<Bytecode::Op::Add>(*result_reg, *result_reg, *expr_reg);
for (size_t i = 0; i < m_expressions.size(); i++) {
m_expressions[i].generate_bytecode(generator);
if (i == 0) {
generator.emit<Bytecode::Op::Store>(string_reg);
} else {
generator.emit<Bytecode::Op::ConcatString>(string_reg);
}
if (!result_reg.has_value()) {
result_reg = generator.allocate_register();
generator.emit<Bytecode::Op::NewString>(*result_reg, "");
}
return result_reg;
}
}

View file

@ -26,7 +26,7 @@ Generator::~Generator()
OwnPtr<Block> Generator::generate(ASTNode const& node)
{
Generator generator;
[[maybe_unused]] auto dummy = node.generate_bytecode(generator);
node.generate_bytecode(generator);
generator.m_block->set_register_count({}, generator.m_next_register);
generator.m_block->seal();
return move(generator.m_block);

View file

@ -11,7 +11,8 @@
#define ENUMERATE_BYTECODE_OPS(O) \
O(Load) \
O(LoadRegister) \
O(LoadImmediate) \
O(Store) \
O(Add) \
O(Sub) \
O(Mul) \
@ -36,7 +37,7 @@
O(Jump) \
O(JumpIfFalse) \
O(JumpIfTrue) \
O(JumpIfNullish) \
O(JumpIfNotNullish) \
O(Call) \
O(EnterScope) \
O(Return) \
@ -52,7 +53,8 @@
O(RightShift) \
O(UnsignedRightShift) \
O(In) \
O(InstanceOf)
O(InstanceOf) \
O(ConcatString)
namespace JS::Bytecode {

View file

@ -30,6 +30,7 @@ public:
Value run(Bytecode::Block const&);
ALWAYS_INLINE Value& accumulator() { return reg(Register::accumulator()); }
Value& reg(Register const& r) { return registers()[r.index()]; }
void jump(Label const& label) { m_pending_jump = label.address(); }

View file

@ -51,12 +51,17 @@ namespace JS::Bytecode::Op {
void Load::execute(Bytecode::Interpreter& interpreter) const
{
interpreter.reg(m_dst) = m_value;
interpreter.accumulator() = interpreter.reg(m_src);
}
void LoadRegister::execute(Bytecode::Interpreter& interpreter) const
void LoadImmediate::execute(Bytecode::Interpreter& interpreter) const
{
interpreter.reg(m_dst) = interpreter.reg(m_src);
interpreter.accumulator() = m_value;
}
void Store::execute(Bytecode::Interpreter& interpreter) const
{
interpreter.reg(m_dst) = interpreter.accumulator();
}
static Value abstract_inequals(GlobalObject& global_object, Value src1, Value src2)
@ -82,11 +87,13 @@ static Value typed_equals(GlobalObject&, Value src1, Value src2)
#define JS_DEFINE_COMMON_BINARY_OP(OpTitleCase, op_snake_case) \
void OpTitleCase::execute(Bytecode::Interpreter& interpreter) const \
{ \
interpreter.reg(m_dst) = op_snake_case(interpreter.global_object(), interpreter.reg(m_src1), interpreter.reg(m_src2)); \
auto lhs = interpreter.reg(m_lhs_reg); \
auto rhs = interpreter.accumulator(); \
interpreter.accumulator() = op_snake_case(interpreter.global_object(), lhs, rhs); \
} \
String OpTitleCase::to_string() const \
{ \
return String::formatted(#OpTitleCase " dst:{}, src1:{}, src2:{}", m_dst, m_src1, m_src2); \
return String::formatted(#OpTitleCase " lhs:{}", m_lhs_reg); \
}
JS_ENUMERATE_COMMON_BINARY_OPS(JS_DEFINE_COMMON_BINARY_OP)
@ -104,50 +111,55 @@ static Value typeof_(GlobalObject& global_object, Value value)
#define JS_DEFINE_COMMON_UNARY_OP(OpTitleCase, op_snake_case) \
void OpTitleCase::execute(Bytecode::Interpreter& interpreter) const \
{ \
interpreter.reg(m_dst) = op_snake_case(interpreter.global_object(), interpreter.reg(m_src)); \
interpreter.accumulator() = op_snake_case(interpreter.global_object(), interpreter.accumulator()); \
} \
String OpTitleCase::to_string() const \
{ \
return String::formatted(#OpTitleCase " dst:{}, src:{}", m_dst, m_src); \
return #OpTitleCase; \
}
JS_ENUMERATE_COMMON_UNARY_OPS(JS_DEFINE_COMMON_UNARY_OP)
void NewBigInt::execute(Bytecode::Interpreter& interpreter) const
{
interpreter.reg(m_dst) = js_bigint(interpreter.vm().heap(), m_bigint);
interpreter.accumulator() = js_bigint(interpreter.vm().heap(), m_bigint);
}
void NewString::execute(Bytecode::Interpreter& interpreter) const
{
interpreter.reg(m_dst) = js_string(interpreter.vm(), m_string);
interpreter.accumulator() = js_string(interpreter.vm(), m_string);
}
void NewObject::execute(Bytecode::Interpreter& interpreter) const
{
interpreter.reg(m_dst) = Object::create_empty(interpreter.global_object());
interpreter.accumulator() = Object::create_empty(interpreter.global_object());
}
void ConcatString::execute(Bytecode::Interpreter& interpreter) const
{
interpreter.reg(m_lhs) = add(interpreter.global_object(), interpreter.reg(m_lhs), interpreter.accumulator());
}
void GetVariable::execute(Bytecode::Interpreter& interpreter) const
{
interpreter.reg(m_dst) = interpreter.vm().get_variable(m_identifier, interpreter.global_object());
interpreter.accumulator() = interpreter.vm().get_variable(m_identifier, interpreter.global_object());
}
void SetVariable::execute(Bytecode::Interpreter& interpreter) const
{
interpreter.vm().set_variable(m_identifier, interpreter.reg(m_src), interpreter.global_object());
interpreter.vm().set_variable(m_identifier, interpreter.accumulator(), interpreter.global_object());
}
void GetById::execute(Bytecode::Interpreter& interpreter) const
{
if (auto* object = interpreter.reg(m_base).to_object(interpreter.global_object()))
interpreter.reg(m_dst) = object->get(m_property);
if (auto* object = interpreter.accumulator().to_object(interpreter.global_object()))
interpreter.accumulator() = object->get(m_property);
}
void PutById::execute(Bytecode::Interpreter& interpreter) const
{
if (auto* object = interpreter.reg(m_base).to_object(interpreter.global_object()))
object->put(m_property, interpreter.reg(m_src));
object->put(m_property, interpreter.accumulator());
}
void Jump::execute(Bytecode::Interpreter& interpreter) const
@ -158,7 +170,7 @@ void Jump::execute(Bytecode::Interpreter& interpreter) const
void JumpIfFalse::execute(Bytecode::Interpreter& interpreter) const
{
VERIFY(m_target.has_value());
auto result = interpreter.reg(m_result);
auto result = interpreter.accumulator();
if (!result.to_boolean())
interpreter.jump(m_target.value());
}
@ -166,16 +178,16 @@ void JumpIfFalse::execute(Bytecode::Interpreter& interpreter) const
void JumpIfTrue::execute(Bytecode::Interpreter& interpreter) const
{
VERIFY(m_target.has_value());
auto result = interpreter.reg(m_result);
auto result = interpreter.accumulator();
if (result.to_boolean())
interpreter.jump(m_target.value());
}
void JumpIfNullish::execute(Bytecode::Interpreter& interpreter) const
void JumpIfNotNullish::execute(Bytecode::Interpreter& interpreter) const
{
VERIFY(m_target.has_value());
auto result = interpreter.reg(m_result);
if (result.is_nullish())
auto result = interpreter.accumulator();
if (!result.is_nullish())
interpreter.jump(m_target.value());
}
@ -201,7 +213,7 @@ void Call::execute(Bytecode::Interpreter& interpreter) const
return_value = interpreter.vm().call(function, this_value, move(argument_values));
}
interpreter.reg(m_dst) = return_value;
interpreter.accumulator() = return_value;
}
void EnterScope::execute(Bytecode::Interpreter& interpreter) const
@ -223,53 +235,62 @@ void EnterScope::execute(Bytecode::Interpreter& interpreter) const
void Return::execute(Bytecode::Interpreter& interpreter) const
{
auto return_value = m_argument.has_value() ? interpreter.reg(m_argument.value()) : js_undefined();
interpreter.do_return(return_value);
interpreter.do_return(interpreter.accumulator().value_or(js_undefined()));
}
String Load::to_string() const
{
return String::formatted("Load dst:{}, value:{}", m_dst, m_value.to_string_without_side_effects());
return String::formatted("Load src:{}", m_src);
}
String LoadRegister::to_string() const
String LoadImmediate::to_string() const
{
return String::formatted("LoadRegister dst:{}, src:{}", m_dst, m_src);
return String::formatted("LoadImmediate value:{}", m_value);
}
String Store::to_string() const
{
return String::formatted("Store dst:{}", m_dst);
}
String NewBigInt::to_string() const
{
return String::formatted("NewBigInt dst:{}, bigint:\"{}\"", m_dst, m_bigint.to_base10());
return String::formatted("NewBigInt bigint:\"{}\"", m_bigint.to_base10());
}
String NewString::to_string() const
{
return String::formatted("NewString dst:{}, string:\"{}\"", m_dst, m_string);
return String::formatted("NewString string:\"{}\"", m_string);
}
String NewObject::to_string() const
{
return String::formatted("NewObject dst:{}", m_dst);
return "NewObject";
}
String ConcatString::to_string() const
{
return String::formatted("ConcatString lhs:{}", m_lhs);
}
String GetVariable::to_string() const
{
return String::formatted("GetVariable dst:{}, identifier:{}", m_dst, m_identifier);
return String::formatted("GetVariable identifier:{}", m_identifier);
}
String SetVariable::to_string() const
{
return String::formatted("SetVariable identifier:{}, src:{}", m_identifier, m_src);
return String::formatted("SetVariable identifier:{}", m_identifier);
}
String PutById::to_string() const
{
return String::formatted("PutById base:{}, property:{}, src:{}", m_base, m_property, m_src);
return String::formatted("PutById base:{}, property:{}", m_base, m_property);
}
String GetById::to_string() const
{
return String::formatted("GetById dst:{}, base:{}, property:{}", m_dst, m_base, m_property);
return String::formatted("GetById property:{}", m_property);
}
String Jump::to_string() const
@ -280,28 +301,28 @@ String Jump::to_string() const
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);
return String::formatted("JumpIfFalse target:{}", m_target.value());
return "JumpIfFalse target:<empty>";
}
String JumpIfTrue::to_string() const
{
if (m_target.has_value())
return String::formatted("JumpIfTrue result:{}, target:{}", m_result, m_target.value());
return String::formatted("JumpIfTrue result:{}, target:<empty>", m_result);
return String::formatted("JumpIfTrue target:{}", m_target.value());
return "JumpIfTrue result:{}, target:<empty>";
}
String JumpIfNullish::to_string() const
String JumpIfNotNullish::to_string() const
{
if (m_target.has_value())
return String::formatted("JumpIfNullish result:{}, target:{}", m_result, m_target.value());
return String::formatted("JumpIfNullish result:{}, target:<empty>", m_result);
return String::formatted("JumpIfNotNullish target:{}", m_target.value());
return "JumpIfNotNullish target:<empty>";
}
String Call::to_string() const
{
StringBuilder builder;
builder.appendff("Call dst:{}, callee:{}, this:{}", m_dst, m_callee, m_this_value);
builder.appendff("Call callee:{}, this:{}", m_callee, m_this_value);
if (m_argument_count != 0) {
builder.append(", arguments:[");
for (size_t i = 0; i < m_argument_count; ++i) {
@ -321,8 +342,6 @@ String EnterScope::to_string() const
String Return::to_string() const
{
if (m_argument.has_value())
return String::formatted("Return {}", m_argument.value());
return "Return";
}

View file

@ -19,26 +19,8 @@ namespace JS::Bytecode::Op {
class Load final : public Instruction {
public:
Load(Register dst, Value value)
Load(Register src)
: Instruction(Type::Load)
, m_dst(dst)
, m_value(value)
{
}
void execute(Bytecode::Interpreter&) const;
String to_string() const;
private:
Register m_dst;
Value m_value;
};
class LoadRegister final : public Instruction {
public:
LoadRegister(Register dst, Register src)
: Instruction(Type::LoadRegister)
, m_dst(dst)
, m_src(src)
{
}
@ -47,10 +29,39 @@ public:
String to_string() const;
private:
Register m_dst;
Register m_src;
};
class LoadImmediate final : public Instruction {
public:
LoadImmediate(Value value)
: Instruction(Type::LoadImmediate)
, m_value(value)
{
}
void execute(Bytecode::Interpreter&) const;
String to_string() const;
private:
Value m_value;
};
class Store final : public Instruction {
public:
Store(Register dst)
: Instruction(Type::Store)
, m_dst(dst)
{
}
void execute(Bytecode::Interpreter&) const;
String to_string() const;
private:
Register m_dst;
};
#define JS_ENUMERATE_COMMON_BINARY_OPS(O) \
O(Add, add) \
O(Sub, sub) \
@ -78,11 +89,9 @@ private:
#define JS_DECLARE_COMMON_BINARY_OP(OpTitleCase, op_snake_case) \
class OpTitleCase final : public Instruction { \
public: \
OpTitleCase(Register dst, Register src1, Register src2) \
OpTitleCase(Register lhs_reg) \
: Instruction(Type::OpTitleCase) \
, m_dst(dst) \
, m_src1(src1) \
, m_src2(src2) \
, m_lhs_reg(lhs_reg) \
{ \
} \
\
@ -90,9 +99,7 @@ private:
String to_string() const; \
\
private: \
Register m_dst; \
Register m_src1; \
Register m_src2; \
Register m_lhs_reg; \
};
JS_ENUMERATE_COMMON_BINARY_OPS(JS_DECLARE_COMMON_BINARY_OP)
@ -108,19 +115,13 @@ JS_ENUMERATE_COMMON_BINARY_OPS(JS_DECLARE_COMMON_BINARY_OP)
#define JS_DECLARE_COMMON_UNARY_OP(OpTitleCase, op_snake_case) \
class OpTitleCase final : public Instruction { \
public: \
OpTitleCase(Register dst, Register src) \
OpTitleCase() \
: Instruction(Type::OpTitleCase) \
, m_dst(dst) \
, m_src(src) \
{ \
} \
\
void execute(Bytecode::Interpreter&) const; \
String to_string() const; \
\
private: \
Register m_dst; \
Register m_src; \
};
JS_ENUMERATE_COMMON_UNARY_OPS(JS_DECLARE_COMMON_UNARY_OP)
@ -128,9 +129,8 @@ JS_ENUMERATE_COMMON_UNARY_OPS(JS_DECLARE_COMMON_UNARY_OP)
class NewString final : public Instruction {
public:
NewString(Register dst, String string)
NewString(String string)
: Instruction(Type::NewString)
, m_dst(dst)
, m_string(move(string))
{
}
@ -139,30 +139,24 @@ public:
String to_string() const;
private:
Register m_dst;
String m_string;
};
class NewObject final : public Instruction {
public:
explicit NewObject(Register dst)
NewObject()
: Instruction(Type::NewObject)
, m_dst(dst)
{
}
void execute(Bytecode::Interpreter&) const;
String to_string() const;
private:
Register m_dst;
};
class NewBigInt final : public Instruction {
public:
explicit NewBigInt(Register dst, Crypto::SignedBigInteger bigint)
explicit NewBigInt(Crypto::SignedBigInteger bigint)
: Instruction(Type::NewBigInt)
, m_dst(dst)
, m_bigint(move(bigint))
{
}
@ -171,16 +165,29 @@ public:
String to_string() const;
private:
Register m_dst;
Crypto::SignedBigInteger m_bigint;
};
class ConcatString final : public Instruction {
public:
ConcatString(Register lhs)
: Instruction(Type::ConcatString)
, m_lhs(lhs)
{
}
void execute(Bytecode::Interpreter&) const;
String to_string() const;
private:
Register m_lhs;
};
class SetVariable final : public Instruction {
public:
SetVariable(FlyString identifier, Register src)
SetVariable(FlyString identifier)
: Instruction(Type::SetVariable)
, m_identifier(move(identifier))
, m_src(src)
{
}
@ -189,14 +196,12 @@ public:
private:
FlyString m_identifier;
Register m_src;
};
class GetVariable final : public Instruction {
public:
GetVariable(Register dst, FlyString identifier)
GetVariable(FlyString identifier)
: Instruction(Type::GetVariable)
, m_dst(dst)
, m_identifier(move(identifier))
{
}
@ -205,16 +210,13 @@ public:
String to_string() const;
private:
Register m_dst;
FlyString m_identifier;
};
class GetById final : public Instruction {
public:
GetById(Register dst, Register base, FlyString property)
GetById(FlyString property)
: Instruction(Type::GetById)
, m_dst(dst)
, m_base(base)
, m_property(move(property))
{
}
@ -223,18 +225,15 @@ public:
String to_string() const;
private:
Register m_dst;
Register m_base;
FlyString m_property;
};
class PutById final : public Instruction {
public:
PutById(Register base, FlyString property, Register src)
PutById(Register base, FlyString property)
: Instruction(Type::PutById)
, m_base(base)
, m_property(move(property))
, m_src(src)
{
}
@ -244,7 +243,6 @@ public:
private:
Register m_base;
FlyString m_property;
Register m_src;
};
class Jump : public Instruction {
@ -272,55 +270,42 @@ protected:
class JumpIfFalse final : public Jump {
public:
explicit JumpIfFalse(Register result, Optional<Label> target = {})
explicit JumpIfFalse(Optional<Label> target = {})
: Jump(Type::JumpIfFalse, move(target))
, m_result(result)
{
}
void execute(Bytecode::Interpreter&) const;
String to_string() const;
private:
Register m_result;
};
class JumpIfTrue : public Jump {
public:
explicit JumpIfTrue(Register result, Optional<Label> target = {})
explicit JumpIfTrue(Optional<Label> target = {})
: Jump(Type::JumpIfTrue, move(target))
, m_result(result)
{
}
void execute(Bytecode::Interpreter&) const;
String to_string() const;
private:
Register m_result;
};
class JumpIfNullish final : public Jump {
class JumpIfNotNullish final : public Jump {
public:
explicit JumpIfNullish(Register result, Optional<Label> target = {})
: Jump(Type::JumpIfNullish, move(target))
, m_result(result)
explicit JumpIfNotNullish(Optional<Label> target = {})
: Jump(Type::JumpIfNotNullish, move(target))
{
}
void execute(Bytecode::Interpreter&) const;
String to_string() const;
private:
Register m_result;
};
// NOTE: This instruction is variable-width depending on the number of arguments!
class Call final : public Instruction {
public:
Call(Register dst, Register callee, Register this_value, Vector<Register> const& arguments)
Call(Register callee, Register this_value, Vector<Register> const& arguments)
: Instruction(Type::Call)
, m_dst(dst)
, m_callee(callee)
, m_this_value(this_value)
, m_argument_count(arguments.size())
@ -335,7 +320,6 @@ public:
size_t length() const { return sizeof(*this) + sizeof(Register) * m_argument_count; }
private:
Register m_dst;
Register m_callee;
Register m_this_value;
size_t m_argument_count { 0 };
@ -359,17 +343,13 @@ private:
class Return final : public Instruction {
public:
explicit Return(Optional<Register> argument)
Return()
: Instruction(Type::Return)
, m_argument(move(argument))
{
}
void execute(Bytecode::Interpreter&) const;
String to_string() const;
private:
Optional<Register> m_argument;
};
}

View file

@ -12,6 +12,14 @@ namespace JS::Bytecode {
class Register {
public:
constexpr static u32 accumulator_index = 0;
static Register accumulator()
{
static Register accumulator(accumulator_index);
return accumulator;
}
explicit Register(u32 index)
: m_index(index)
{
@ -20,7 +28,7 @@ public:
u32 index() const { return m_index; }
private:
u32 m_index { 0 };
u32 m_index;
};
}
@ -29,6 +37,8 @@ template<>
struct AK::Formatter<JS::Bytecode::Register> : AK::Formatter<FormatString> {
void format(FormatBuilder& builder, JS::Bytecode::Register const& value)
{
if (value.index() == JS::Bytecode::Register::accumulator_index)
return AK::Formatter<FormatString>::format(builder, "acc");
return AK::Formatter<FormatString>::format(builder, "${}", value.index());
}
};