ladybird/Libraries/LibJS/Bytecode/Op.h

2860 lines
86 KiB
C
Raw Normal View History

/*
* Copyright (c) 2021-2023, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/FixedArray.h>
#include <AK/StdLibExtras.h>
#include <LibCrypto/BigInt/SignedBigInteger.h>
#include <LibJS/Bytecode/Builtins.h>
#include <LibJS/Bytecode/IdentifierTable.h>
#include <LibJS/Bytecode/Instruction.h>
#include <LibJS/Bytecode/Label.h>
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
#include <LibJS/Bytecode/Operand.h>
#include <LibJS/Bytecode/RegexTable.h>
#include <LibJS/Bytecode/Register.h>
#include <LibJS/Bytecode/ScopedOperand.h>
#include <LibJS/Bytecode/StringTable.h>
#include <LibJS/Heap/Cell.h>
#include <LibJS/Runtime/Environment.h>
#include <LibJS/Runtime/Iterator.h>
#include <LibJS/Runtime/Value.h>
#include <LibJS/Runtime/ValueTraits.h>
namespace JS {
class FunctionExpression;
}
namespace JS::Bytecode::Op {
class CreateRestParams final : public Instruction {
public:
CreateRestParams(Operand dst, u32 rest_index)
: Instruction(Type::CreateRestParams)
, m_dst(dst)
, m_rest_index(rest_index)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
}
private:
Operand m_dst;
u32 m_rest_index;
};
class CreateArguments final : public Instruction {
public:
enum class Kind {
Mapped,
Unmapped,
};
CreateArguments(Optional<Operand> dst, Kind kind, bool is_immutable)
: Instruction(Type::CreateArguments)
, m_dst(dst)
, m_kind(kind)
, m_is_immutable(is_immutable)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
if (m_dst.has_value())
visitor(m_dst.value());
}
private:
Optional<Operand> m_dst;
Kind m_kind;
bool m_is_immutable { false };
};
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
class Mov final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Mov(Operand dst, Operand src)
: Instruction(Type::Mov)
, m_dst(dst)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_src(src)
{
}
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_src);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand src() const { return m_src; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_src;
};
#define JS_ENUMERATE_COMMON_BINARY_OPS_WITH_FAST_PATH(O) \
O(Add, add) \
O(BitwiseAnd, bitwise_and) \
O(BitwiseOr, bitwise_or) \
O(BitwiseXor, bitwise_xor) \
O(GreaterThan, greater_than) \
O(GreaterThanEquals, greater_than_equals) \
O(LeftShift, left_shift) \
O(LessThan, less_than) \
O(LessThanEquals, less_than_equals) \
O(Mul, mul) \
O(RightShift, right_shift) \
O(Sub, sub) \
O(UnsignedRightShift, unsigned_right_shift)
#define JS_ENUMERATE_COMMON_BINARY_OPS_WITHOUT_FAST_PATH(O) \
O(Div, div) \
O(Exp, exp) \
O(Mod, mod) \
O(In, in) \
O(InstanceOf, instance_of) \
O(LooselyInequals, loosely_inequals) \
O(LooselyEquals, loosely_equals) \
O(StrictlyInequals, strict_inequals) \
O(StrictlyEquals, strict_equals)
#define JS_DECLARE_COMMON_BINARY_OP(OpTitleCase, op_snake_case) \
class OpTitleCase final : public Instruction { \
public: \
explicit OpTitleCase(Operand dst, Operand lhs, Operand rhs) \
: Instruction(Type::OpTitleCase) \
, m_dst(dst) \
, m_lhs(lhs) \
, m_rhs(rhs) \
{ \
} \
\
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const; \
ByteString to_byte_string_impl(Bytecode::Executable const&) const; \
void visit_operands_impl(Function<void(Operand&)> visitor) \
{ \
visitor(m_dst); \
visitor(m_lhs); \
visitor(m_rhs); \
} \
\
Operand dst() const { return m_dst; } \
Operand lhs() const { return m_lhs; } \
Operand rhs() const { return m_rhs; } \
\
private: \
Operand m_dst; \
Operand m_lhs; \
Operand m_rhs; \
};
JS_ENUMERATE_COMMON_BINARY_OPS_WITHOUT_FAST_PATH(JS_DECLARE_COMMON_BINARY_OP)
JS_ENUMERATE_COMMON_BINARY_OPS_WITH_FAST_PATH(JS_DECLARE_COMMON_BINARY_OP)
#undef JS_DECLARE_COMMON_BINARY_OP
#define JS_ENUMERATE_COMMON_UNARY_OPS(O) \
O(BitwiseNot, bitwise_not) \
O(Not, not_) \
O(UnaryPlus, unary_plus) \
O(UnaryMinus, unary_minus) \
O(Typeof, typeof_)
#define JS_DECLARE_COMMON_UNARY_OP(OpTitleCase, op_snake_case) \
class OpTitleCase final : public Instruction { \
public: \
OpTitleCase(Operand dst, Operand src) \
: Instruction(Type::OpTitleCase) \
, m_dst(dst) \
, m_src(src) \
{ \
} \
\
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const; \
ByteString to_byte_string_impl(Bytecode::Executable const&) const; \
void visit_operands_impl(Function<void(Operand&)> visitor) \
{ \
visitor(m_dst); \
visitor(m_src); \
} \
\
Operand dst() const { return m_dst; } \
Operand src() const { return m_src; } \
\
private: \
Operand m_dst; \
Operand m_src; \
};
JS_ENUMERATE_COMMON_UNARY_OPS(JS_DECLARE_COMMON_UNARY_OP)
#undef JS_DECLARE_COMMON_UNARY_OP
class NewObject final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit NewObject(Operand dst)
: Instruction(Type::NewObject)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
private:
Operand m_dst;
};
class NewRegExp final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
NewRegExp(Operand dst, StringTableIndex source_index, StringTableIndex flags_index, RegexTableIndex regex_index)
: Instruction(Type::NewRegExp)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_source_index(source_index)
, m_flags_index(flags_index)
, m_regex_index(regex_index)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
StringTableIndex source_index() const { return m_source_index; }
StringTableIndex flags_index() const { return m_flags_index; }
RegexTableIndex regex_index() const { return m_regex_index; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
StringTableIndex m_source_index;
StringTableIndex m_flags_index;
RegexTableIndex m_regex_index;
};
2022-12-09 18:48:57 +00:00
#define JS_ENUMERATE_NEW_BUILTIN_ERROR_OPS(O) \
O(TypeError)
#define JS_DECLARE_NEW_BUILTIN_ERROR_OP(ErrorName) \
class New##ErrorName final : public Instruction { \
public: \
New##ErrorName(Operand dst, StringTableIndex error_string) \
: Instruction(Type::New##ErrorName) \
, m_dst(dst) \
, m_error_string(error_string) \
{ \
} \
\
void execute_impl(Bytecode::Interpreter&) const; \
ByteString to_byte_string_impl(Bytecode::Executable const&) const; \
void visit_operands_impl(Function<void(Operand&)> visitor) \
{ \
visitor(m_dst); \
} \
\
Operand dst() const { return m_dst; } \
StringTableIndex error_string() const { return m_error_string; } \
\
private: \
Operand m_dst; \
StringTableIndex m_error_string; \
2022-12-09 18:48:57 +00:00
};
JS_ENUMERATE_NEW_BUILTIN_ERROR_OPS(JS_DECLARE_NEW_BUILTIN_ERROR_OP)
#undef JS_DECLARE_NEW_BUILTIN_ERROR_OP
// NOTE: This instruction is variable-width depending on the number of excluded names
class CopyObjectExcludingProperties final : public Instruction {
public:
static constexpr bool IsVariableLength = true;
CopyObjectExcludingProperties(Operand dst, Operand from_object, Vector<ScopedOperand> const& excluded_names)
: Instruction(Type::CopyObjectExcludingProperties)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_from_object(from_object)
, m_excluded_names_count(excluded_names.size())
{
for (size_t i = 0; i < m_excluded_names_count; i++)
m_excluded_names[i] = excluded_names[i];
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_from_object);
for (size_t i = 0; i < m_excluded_names_count; i++)
visitor(m_excluded_names[i]);
}
size_t length_impl() const
{
return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Operand) * m_excluded_names_count);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand from_object() const { return m_from_object; }
size_t excluded_names_count() const { return m_excluded_names_count; }
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand const* excluded_names() const { return m_excluded_names; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_from_object;
size_t m_excluded_names_count { 0 };
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_excluded_names[];
};
// NOTE: This instruction is variable-width depending on the number of elements!
class NewArray final : public Instruction {
public:
static constexpr bool IsVariableLength = true;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit NewArray(Operand dst)
: Instruction(Type::NewArray)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_element_count(0)
{
}
NewArray(Operand dst, ReadonlySpan<ScopedOperand> elements)
: Instruction(Type::NewArray)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_element_count(elements.size())
{
for (size_t i = 0; i < m_element_count; ++i)
m_elements[i] = elements[i];
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
for (size_t i = 0; i < m_element_count; i++)
visitor(m_elements[i]);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
size_t length_impl() const
{
return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Operand) * m_element_count);
}
size_t element_count() const { return m_element_count; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
size_t m_element_count { 0 };
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_elements[];
};
class NewPrimitiveArray final : public Instruction {
public:
static constexpr bool IsVariableLength = true;
NewPrimitiveArray(Operand dst, ReadonlySpan<Value> elements)
: Instruction(Type::NewPrimitiveArray)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_element_count(elements.size())
{
for (size_t i = 0; i < m_element_count; ++i)
m_elements[i] = elements[i];
}
size_t length_impl() const
{
return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Value) * m_element_count);
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
ReadonlySpan<Value> elements() const { return { m_elements, m_element_count }; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
size_t m_element_count { 0 };
Value m_elements[];
};
class AddPrivateName final : public Instruction {
public:
explicit AddPrivateName(IdentifierTableIndex name)
: Instruction(Type::AddPrivateName)
, m_name(name)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
private:
IdentifierTableIndex m_name;
};
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
class ArrayAppend final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
ArrayAppend(Operand dst, Operand src, bool is_spread)
: Instruction(Type::ArrayAppend)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_src(src)
, m_is_spread(is_spread)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_src);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand src() const { return m_src; }
bool is_spread() const { return m_is_spread; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_src;
bool m_is_spread = false;
};
class ImportCall final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
ImportCall(Operand dst, Operand specifier, Operand options)
: Instruction(Type::ImportCall)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_specifier(specifier)
, m_options(options)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_specifier);
visitor(m_options);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand specifier() const { return m_specifier; }
Operand options() const { return m_options; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_specifier;
Operand m_options;
};
class IteratorToArray final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit IteratorToArray(Operand dst, Operand iterator)
: Instruction(Type::IteratorToArray)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_iterator(iterator)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand iterator() const { return m_iterator; }
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_iterator);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
private:
Operand m_dst;
Operand m_iterator;
};
class ConcatString final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit ConcatString(Operand dst, Operand src)
: Instruction(Type::ConcatString)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_src(src)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand src() const { return m_src; }
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_src);
}
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_src;
};
enum class EnvironmentMode {
Lexical,
Var,
};
enum class BindingInitializationMode {
Initialize,
Set,
};
class CreateLexicalEnvironment final : public Instruction {
public:
explicit CreateLexicalEnvironment(u32 capacity = 0)
: Instruction(Type::CreateLexicalEnvironment)
, m_capacity(capacity)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
private:
u32 m_capacity { 0 };
};
class CreateVariableEnvironment final : public Instruction {
public:
explicit CreateVariableEnvironment(u32 capacity = 0)
: Instruction(Type::CreateVariableEnvironment)
, m_capacity(capacity)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
private:
u32 m_capacity { 0 };
};
class CreatePrivateEnvironment final : public Instruction {
public:
explicit CreatePrivateEnvironment()
: Instruction(Type::CreatePrivateEnvironment)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
};
class EnterObjectEnvironment final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit EnterObjectEnvironment(Operand object)
: Instruction(Type::EnterObjectEnvironment)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_object(object)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand object() const { return m_object; }
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_object);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
private:
Operand m_object;
};
class Catch final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit Catch(Operand dst)
: Instruction(Type::Catch)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
private:
Operand m_dst;
};
class LeaveFinally final : public Instruction {
public:
explicit LeaveFinally()
: Instruction(Type::LeaveFinally)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
};
class RestoreScheduledJump final : public Instruction {
public:
explicit RestoreScheduledJump()
: Instruction(Type::RestoreScheduledJump)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
};
class CreateVariable final : public Instruction {
public:
explicit CreateVariable(IdentifierTableIndex identifier, EnvironmentMode mode, bool is_immutable, bool is_global = false, bool is_strict = false)
: Instruction(Type::CreateVariable)
, m_identifier(identifier)
, m_mode(mode)
, m_is_immutable(is_immutable)
, m_is_global(is_global)
, m_is_strict(is_strict)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
IdentifierTableIndex identifier() const { return m_identifier; }
EnvironmentMode mode() const { return m_mode; }
bool is_immutable() const { return m_is_immutable; }
bool is_global() const { return m_is_global; }
bool is_strict() const { return m_is_strict; }
private:
IdentifierTableIndex m_identifier;
EnvironmentMode m_mode;
bool m_is_immutable : 4 { false };
bool m_is_global : 4 { false };
bool m_is_strict { false };
};
class InitializeLexicalBinding final : public Instruction {
public:
explicit InitializeLexicalBinding(IdentifierTableIndex identifier, Operand src)
: Instruction(Type::InitializeLexicalBinding)
, m_identifier(identifier)
, m_src(src)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_src);
}
IdentifierTableIndex identifier() const { return m_identifier; }
Operand src() const { return m_src; }
private:
IdentifierTableIndex m_identifier;
Operand m_src;
mutable EnvironmentCoordinate m_cache;
};
class InitializeVariableBinding final : public Instruction {
public:
explicit InitializeVariableBinding(IdentifierTableIndex identifier, Operand src)
: Instruction(Type::InitializeVariableBinding)
, m_identifier(identifier)
, m_src(src)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_src);
}
IdentifierTableIndex identifier() const { return m_identifier; }
Operand src() const { return m_src; }
private:
IdentifierTableIndex m_identifier;
Operand m_src;
mutable EnvironmentCoordinate m_cache;
};
class SetLexicalBinding final : public Instruction {
public:
explicit SetLexicalBinding(IdentifierTableIndex identifier, Operand src)
: Instruction(Type::SetLexicalBinding)
, m_identifier(identifier)
, m_src(src)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_src);
}
IdentifierTableIndex identifier() const { return m_identifier; }
Operand src() const { return m_src; }
private:
IdentifierTableIndex m_identifier;
Operand m_src;
mutable EnvironmentCoordinate m_cache;
};
class SetVariableBinding final : public Instruction {
public:
explicit SetVariableBinding(IdentifierTableIndex identifier, Operand src)
: Instruction(Type::SetVariableBinding)
, m_identifier(identifier)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_src(src)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_src);
}
IdentifierTableIndex identifier() const { return m_identifier; }
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand src() const { return m_src; }
private:
IdentifierTableIndex m_identifier;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_src;
mutable EnvironmentCoordinate m_cache;
};
class SetArgument final : public Instruction {
public:
SetArgument(size_t index, Operand src)
: Instruction(Type::SetArgument)
, m_index(index)
, m_src(src)
{
}
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_src);
}
size_t index() const { return m_index; }
Operand src() const { return m_src; }
private:
u32 m_index;
Operand m_src;
};
class GetArgument final : public Instruction {
public:
GetArgument(Operand dst, size_t index)
: Instruction(Type::GetArgument)
, m_index(index)
, m_dst(dst)
{
}
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
}
u32 index() const { return m_index; }
Operand dst() const { return m_dst; }
private:
u32 m_index;
Operand m_dst;
};
class GetCalleeAndThisFromEnvironment final : public Instruction {
public:
explicit GetCalleeAndThisFromEnvironment(Operand callee, Operand this_value, IdentifierTableIndex identifier)
: Instruction(Type::GetCalleeAndThisFromEnvironment)
, m_identifier(identifier)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_callee(callee)
, m_this_value(this_value)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_callee);
visitor(m_this_value);
}
IdentifierTableIndex identifier() const { return m_identifier; }
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand callee() const { return m_callee; }
Operand this_() const { return m_this_value; }
private:
IdentifierTableIndex m_identifier;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_callee;
Operand m_this_value;
mutable EnvironmentCoordinate m_cache;
};
class GetBinding final : public Instruction {
public:
explicit GetBinding(Operand dst, IdentifierTableIndex identifier)
: Instruction(Type::GetBinding)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_identifier(identifier)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
IdentifierTableIndex identifier() const { return m_identifier; }
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
}
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
IdentifierTableIndex m_identifier;
mutable EnvironmentCoordinate m_cache;
};
class GetGlobal final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
GetGlobal(Operand dst, IdentifierTableIndex identifier, u32 cache_index)
: Instruction(Type::GetGlobal)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_identifier(identifier)
, m_cache_index(cache_index)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
IdentifierTableIndex identifier() const { return m_identifier; }
u32 cache_index() const { return m_cache_index; }
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
}
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
IdentifierTableIndex m_identifier;
u32 m_cache_index { 0 };
};
class DeleteVariable final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit DeleteVariable(Operand dst, IdentifierTableIndex identifier)
: Instruction(Type::DeleteVariable)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_identifier(identifier)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
IdentifierTableIndex identifier() const { return m_identifier; }
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
}
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
IdentifierTableIndex m_identifier;
};
class GetById final : public Instruction {
public:
GetById(Operand dst, Operand base, IdentifierTableIndex property, Optional<IdentifierTableIndex> base_identifier, u32 cache_index)
: Instruction(Type::GetById)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_base(base)
, m_property(property)
, m_base_identifier(move(base_identifier))
, m_cache_index(cache_index)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_base);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand base() const { return m_base; }
IdentifierTableIndex property() const { return m_property; }
u32 cache_index() const { return m_cache_index; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_base;
IdentifierTableIndex m_property;
Optional<IdentifierTableIndex> m_base_identifier;
u32 m_cache_index { 0 };
};
class GetByIdWithThis final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
GetByIdWithThis(Operand dst, Operand base, IdentifierTableIndex property, Operand this_value, u32 cache_index)
: Instruction(Type::GetByIdWithThis)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_base(base)
, m_property(property)
, m_this_value(this_value)
, m_cache_index(cache_index)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_base);
visitor(m_this_value);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand base() const { return m_base; }
IdentifierTableIndex property() const { return m_property; }
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand this_value() const { return m_this_value; }
u32 cache_index() const { return m_cache_index; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_base;
IdentifierTableIndex m_property;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_this_value;
u32 m_cache_index { 0 };
};
class GetLength final : public Instruction {
public:
GetLength(Operand dst, Operand base, Optional<IdentifierTableIndex> base_identifier, u32 cache_index)
: Instruction(Type::GetLength)
, m_dst(dst)
, m_base(base)
, m_base_identifier(move(base_identifier))
, m_cache_index(cache_index)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_base);
}
Operand dst() const { return m_dst; }
Operand base() const { return m_base; }
u32 cache_index() const { return m_cache_index; }
private:
Operand m_dst;
Operand m_base;
Optional<IdentifierTableIndex> m_base_identifier;
u32 m_cache_index { 0 };
};
class GetLengthWithThis final : public Instruction {
public:
GetLengthWithThis(Operand dst, Operand base, Operand this_value, u32 cache_index)
: Instruction(Type::GetLengthWithThis)
, m_dst(dst)
, m_base(base)
, m_this_value(this_value)
, m_cache_index(cache_index)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_base);
visitor(m_this_value);
}
Operand dst() const { return m_dst; }
Operand base() const { return m_base; }
Operand this_value() const { return m_this_value; }
u32 cache_index() const { return m_cache_index; }
private:
Operand m_dst;
Operand m_base;
Operand m_this_value;
u32 m_cache_index { 0 };
};
class GetPrivateById final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit GetPrivateById(Operand dst, Operand base, IdentifierTableIndex property)
: Instruction(Type::GetPrivateById)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_base(base)
, m_property(property)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_base);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand base() const { return m_base; }
IdentifierTableIndex property() const { return m_property; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_base;
IdentifierTableIndex m_property;
};
class HasPrivateId final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
HasPrivateId(Operand dst, Operand base, IdentifierTableIndex property)
: Instruction(Type::HasPrivateId)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_base(base)
, m_property(property)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_base);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand base() const { return m_base; }
IdentifierTableIndex property() const { return m_property; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_base;
IdentifierTableIndex m_property;
};
enum class PropertyKind {
Getter,
Setter,
KeyValue,
DirectKeyValue, // Used for Object expressions. Always sets an own property, never calls a setter.
ProtoSetter,
};
class PutBySpread final : public Instruction {
public:
PutBySpread(Operand base, Operand src)
: Instruction(Type::PutBySpread)
, m_base(base)
, m_src(src)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_base);
visitor(m_src);
}
Operand base() const { return m_base; }
Operand src() const { return m_src; }
private:
Operand m_base;
Operand m_src;
};
class PutById final : public Instruction {
public:
explicit PutById(Operand base, IdentifierTableIndex property, Operand src, PropertyKind kind, u32 cache_index, Optional<IdentifierTableIndex> base_identifier = {})
: Instruction(Type::PutById)
, m_base(base)
, m_property(property)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_src(src)
, m_kind(kind)
, m_cache_index(cache_index)
, m_base_identifier(move(base_identifier))
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_base);
visitor(m_src);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand base() const { return m_base; }
IdentifierTableIndex property() const { return m_property; }
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand src() const { return m_src; }
PropertyKind kind() const { return m_kind; }
u32 cache_index() const { return m_cache_index; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_base;
IdentifierTableIndex m_property;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_src;
PropertyKind m_kind;
u32 m_cache_index { 0 };
Optional<IdentifierTableIndex> m_base_identifier {};
};
class PutByIdWithThis final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
PutByIdWithThis(Operand base, Operand this_value, IdentifierTableIndex property, Operand src, PropertyKind kind, u32 cache_index)
: Instruction(Type::PutByIdWithThis)
, m_base(base)
, m_this_value(this_value)
, m_property(property)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_src(src)
, m_kind(kind)
, m_cache_index(cache_index)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_base);
visitor(m_this_value);
visitor(m_src);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand base() const { return m_base; }
Operand this_value() const { return m_this_value; }
IdentifierTableIndex property() const { return m_property; }
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand src() const { return m_src; }
PropertyKind kind() const { return m_kind; }
u32 cache_index() const { return m_cache_index; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_base;
Operand m_this_value;
IdentifierTableIndex m_property;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_src;
PropertyKind m_kind;
u32 m_cache_index { 0 };
};
class PutPrivateById final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit PutPrivateById(Operand base, IdentifierTableIndex property, Operand src, PropertyKind kind = PropertyKind::KeyValue)
: Instruction(Type::PutPrivateById)
, m_base(base)
, m_property(property)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_src(src)
, m_kind(kind)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_base);
visitor(m_src);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand base() const { return m_base; }
IdentifierTableIndex property() const { return m_property; }
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand src() const { return m_src; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_base;
IdentifierTableIndex m_property;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_src;
PropertyKind m_kind;
};
class DeleteById final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit DeleteById(Operand dst, Operand base, IdentifierTableIndex property)
: Instruction(Type::DeleteById)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_base(base)
, m_property(property)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_base);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand base() const { return m_base; }
IdentifierTableIndex property() const { return m_property; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_base;
IdentifierTableIndex m_property;
};
class DeleteByIdWithThis final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
DeleteByIdWithThis(Operand dst, Operand base, Operand this_value, IdentifierTableIndex property)
: Instruction(Type::DeleteByIdWithThis)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_base(base)
, m_this_value(this_value)
, m_property(property)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_base);
visitor(m_this_value);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand base() const { return m_base; }
Operand this_value() const { return m_this_value; }
IdentifierTableIndex property() const { return m_property; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_base;
Operand m_this_value;
IdentifierTableIndex m_property;
};
class GetByValue final : public Instruction {
public:
GetByValue(Operand dst, Operand base, Operand property, Optional<IdentifierTableIndex> base_identifier = {})
: Instruction(Type::GetByValue)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_base(base)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_property(property)
, m_base_identifier(move(base_identifier))
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_base);
visitor(m_property);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand base() const { return m_base; }
Operand property() const { return m_property; }
Optional<DeprecatedFlyString const&> base_identifier(Bytecode::Interpreter const&) const;
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_base;
Operand m_property;
Optional<IdentifierTableIndex> m_base_identifier;
};
class GetByValueWithThis final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
GetByValueWithThis(Operand dst, Operand base, Operand property, Operand this_value)
: Instruction(Type::GetByValueWithThis)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_base(base)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_property(property)
, m_this_value(this_value)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_base);
visitor(m_property);
visitor(m_this_value);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand base() const { return m_base; }
Operand property() const { return m_property; }
Operand this_value() const { return m_this_value; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_base;
Operand m_property;
Operand m_this_value;
};
class PutByValue final : public Instruction {
public:
PutByValue(Operand base, Operand property, Operand src, PropertyKind kind = PropertyKind::KeyValue, Optional<IdentifierTableIndex> base_identifier = {})
: Instruction(Type::PutByValue)
, m_base(base)
, m_property(property)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_src(src)
, m_kind(kind)
, m_base_identifier(move(base_identifier))
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_base);
visitor(m_property);
visitor(m_src);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand base() const { return m_base; }
Operand property() const { return m_property; }
Operand src() const { return m_src; }
PropertyKind kind() const { return m_kind; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_base;
Operand m_property;
Operand m_src;
PropertyKind m_kind;
Optional<IdentifierTableIndex> m_base_identifier;
};
class PutByValueWithThis final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
PutByValueWithThis(Operand base, Operand property, Operand this_value, Operand src, PropertyKind kind = PropertyKind::KeyValue)
: Instruction(Type::PutByValueWithThis)
, m_base(base)
, m_property(property)
, m_this_value(this_value)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_src(src)
, m_kind(kind)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_base);
visitor(m_property);
visitor(m_this_value);
visitor(m_src);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand base() const { return m_base; }
Operand property() const { return m_property; }
Operand this_value() const { return m_this_value; }
Operand src() const { return m_src; }
PropertyKind kind() const { return m_kind; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_base;
Operand m_property;
Operand m_this_value;
Operand m_src;
PropertyKind m_kind;
};
class DeleteByValue final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
DeleteByValue(Operand dst, Operand base, Operand property)
: Instruction(Type::DeleteByValue)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_base(base)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_property(property)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_base);
visitor(m_property);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand base() const { return m_base; }
Operand property() const { return m_property; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_base;
Operand m_property;
};
class DeleteByValueWithThis final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
DeleteByValueWithThis(Operand dst, Operand base, Operand this_value, Operand property)
: Instruction(Type::DeleteByValueWithThis)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_base(base)
, m_this_value(this_value)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_property(property)
{
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand base() const { return m_base; }
Operand this_value() const { return m_this_value; }
Operand property() const { return m_property; }
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_base);
visitor(m_this_value);
visitor(m_property);
}
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_base;
Operand m_this_value;
Operand m_property;
};
class Jump final : public Instruction {
public:
constexpr static bool IsTerminator = true;
explicit Jump(Label target)
: Instruction(Type::Jump)
, m_target(target)
{
}
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_labels_impl(Function<void(Label&)> visitor)
{
visitor(m_target);
}
auto& target() const { return m_target; }
protected:
Label m_target;
};
class JumpIf final : public Instruction {
public:
constexpr static bool IsTerminator = true;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit JumpIf(Operand condition, Label true_target, Label false_target)
: Instruction(Type::JumpIf)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_condition(condition)
, m_true_target(true_target)
, m_false_target(false_target)
{
}
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_labels_impl(Function<void(Label&)> visitor)
{
visitor(m_true_target);
visitor(m_false_target);
}
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_condition);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand condition() const { return m_condition; }
auto& true_target() const { return m_true_target; }
auto& false_target() const { return m_false_target; }
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
private:
Operand m_condition;
Label m_true_target;
Label m_false_target;
};
class JumpTrue final : public Instruction {
public:
constexpr static bool IsTerminator = true;
explicit JumpTrue(Operand condition, Label target)
: Instruction(Type::JumpTrue)
, m_condition(condition)
, m_target(target)
{
}
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_labels_impl(Function<void(Label&)> visitor)
{
visitor(m_target);
}
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_condition);
}
Operand condition() const { return m_condition; }
auto& target() const { return m_target; }
private:
Operand m_condition;
Label m_target;
};
class JumpFalse final : public Instruction {
public:
constexpr static bool IsTerminator = true;
explicit JumpFalse(Operand condition, Label target)
: Instruction(Type::JumpFalse)
, m_condition(condition)
, m_target(target)
{
}
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_labels_impl(Function<void(Label&)> visitor)
{
visitor(m_target);
}
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_condition);
}
Operand condition() const { return m_condition; }
auto& target() const { return m_target; }
private:
Operand m_condition;
Label m_target;
};
#define JS_ENUMERATE_COMPARISON_OPS(X) \
X(LessThan, less_than, <) \
X(LessThanEquals, less_than_equals, <=) \
X(GreaterThan, greater_than, >) \
X(GreaterThanEquals, greater_than_equals, >=) \
X(LooselyEquals, loosely_equals, ==) \
X(LooselyInequals, loosely_inequals, !=) \
X(StrictlyEquals, strict_equals, ==) \
X(StrictlyInequals, strict_inequals, !=)
#define DECLARE_COMPARISON_OP(op_TitleCase, op_snake_case, numeric_operator) \
class Jump##op_TitleCase final : public Instruction { \
public: \
constexpr static bool IsTerminator = true; \
\
explicit Jump##op_TitleCase(Operand lhs, Operand rhs, Label true_target, Label false_target) \
: Instruction(Type::Jump##op_TitleCase) \
, m_lhs(lhs) \
, m_rhs(rhs) \
, m_true_target(true_target) \
, m_false_target(false_target) \
{ \
} \
\
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const; \
ByteString to_byte_string_impl(Bytecode::Executable const&) const; \
void visit_labels_impl(Function<void(Label&)> visitor) \
{ \
visitor(m_true_target); \
visitor(m_false_target); \
} \
void visit_operands_impl(Function<void(Operand&)> visitor) \
{ \
visitor(m_lhs); \
visitor(m_rhs); \
} \
\
Operand lhs() const { return m_lhs; } \
Operand rhs() const { return m_rhs; } \
auto& true_target() const { return m_true_target; } \
auto& false_target() const { return m_false_target; } \
\
private: \
Operand m_lhs; \
Operand m_rhs; \
Label m_true_target; \
Label m_false_target; \
};
JS_ENUMERATE_COMPARISON_OPS(DECLARE_COMPARISON_OP)
class JumpNullish final : public Instruction {
public:
constexpr static bool IsTerminator = true;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit JumpNullish(Operand condition, Label true_target, Label false_target)
: Instruction(Type::JumpNullish)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_condition(condition)
, m_true_target(true_target)
, m_false_target(false_target)
{
}
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_labels_impl(Function<void(Label&)> visitor)
{
visitor(m_true_target);
visitor(m_false_target);
}
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_condition);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand condition() const { return m_condition; }
auto& true_target() const { return m_true_target; }
auto& false_target() const { return m_false_target; }
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
private:
Operand m_condition;
Label m_true_target;
Label m_false_target;
};
class JumpUndefined final : public Instruction {
2021-06-13 19:24:40 +00:00
public:
constexpr static bool IsTerminator = true;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit JumpUndefined(Operand condition, Label true_target, Label false_target)
: Instruction(Type::JumpUndefined)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_condition(condition)
, m_true_target(true_target)
, m_false_target(false_target)
2021-06-13 19:24:40 +00:00
{
}
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_labels_impl(Function<void(Label&)> visitor)
{
visitor(m_true_target);
visitor(m_false_target);
}
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_condition);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand condition() const { return m_condition; }
auto& true_target() const { return m_true_target; }
auto& false_target() const { return m_false_target; }
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
private:
Operand m_condition;
Label m_true_target;
Label m_false_target;
2021-06-13 19:24:40 +00:00
};
enum class CallType : u8 {
Call,
Construct,
DirectEval,
};
class Call final : public Instruction {
public:
static constexpr bool IsVariableLength = true;
Call(Operand dst, Operand callee, Operand this_value, ReadonlySpan<ScopedOperand> arguments, Optional<StringTableIndex> expression_string = {})
: Instruction(Type::Call)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_callee(callee)
, m_this_value(this_value)
, m_argument_count(arguments.size())
, m_expression_string(expression_string)
{
for (size_t i = 0; i < arguments.size(); ++i)
m_arguments[i] = arguments[i];
}
size_t length_impl() const
{
return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Operand) * m_argument_count);
}
Operand dst() const { return m_dst; }
Operand callee() const { return m_callee; }
Operand this_value() const { return m_this_value; }
Optional<StringTableIndex> const& expression_string() const { return m_expression_string; }
u32 argument_count() const { return m_argument_count; }
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_callee);
visitor(m_this_value);
for (size_t i = 0; i < m_argument_count; i++)
visitor(m_arguments[i]);
}
private:
Operand m_dst;
Operand m_callee;
Operand m_this_value;
u32 m_argument_count { 0 };
Optional<StringTableIndex> m_expression_string;
Operand m_arguments[];
};
class CallBuiltin final : public Instruction {
public:
static constexpr bool IsVariableLength = true;
CallBuiltin(Operand dst, Operand callee, Operand this_value, ReadonlySpan<ScopedOperand> arguments, Builtin builtin, Optional<StringTableIndex> expression_string = {})
: Instruction(Type::CallBuiltin)
, m_dst(dst)
, m_callee(callee)
, m_this_value(this_value)
, m_argument_count(arguments.size())
, m_builtin(builtin)
, m_expression_string(expression_string)
{
for (size_t i = 0; i < arguments.size(); ++i)
m_arguments[i] = arguments[i];
}
size_t length_impl() const
{
return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Operand) * m_argument_count);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand callee() const { return m_callee; }
Operand this_value() const { return m_this_value; }
Optional<StringTableIndex> const& expression_string() const { return m_expression_string; }
u32 argument_count() const { return m_argument_count; }
Builtin const& builtin() const { return m_builtin; }
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_callee);
visitor(m_this_value);
for (size_t i = 0; i < m_argument_count; i++)
visitor(m_arguments[i]);
}
private:
Operand m_dst;
Operand m_callee;
Operand m_this_value;
u32 m_argument_count { 0 };
Builtin m_builtin;
Optional<StringTableIndex> m_expression_string;
Operand m_arguments[];
};
class CallConstruct final : public Instruction {
public:
static constexpr bool IsVariableLength = true;
CallConstruct(Operand dst, Operand callee, Operand this_value, ReadonlySpan<ScopedOperand> arguments, Optional<StringTableIndex> expression_string = {})
: Instruction(Type::CallConstruct)
, m_dst(dst)
, m_callee(callee)
, m_this_value(this_value)
, m_argument_count(arguments.size())
, m_expression_string(expression_string)
{
for (size_t i = 0; i < arguments.size(); ++i)
m_arguments[i] = arguments[i];
}
size_t length_impl() const
{
return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Operand) * m_argument_count);
}
Operand dst() const { return m_dst; }
Operand callee() const { return m_callee; }
Operand this_value() const { return m_this_value; }
Optional<StringTableIndex> const& expression_string() const { return m_expression_string; }
u32 argument_count() const { return m_argument_count; }
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_callee);
visitor(m_this_value);
for (size_t i = 0; i < m_argument_count; i++)
visitor(m_arguments[i]);
}
private:
Operand m_dst;
Operand m_callee;
Operand m_this_value;
u32 m_argument_count { 0 };
Optional<StringTableIndex> m_expression_string;
Operand m_arguments[];
};
class CallDirectEval final : public Instruction {
public:
static constexpr bool IsVariableLength = true;
CallDirectEval(Operand dst, Operand callee, Operand this_value, ReadonlySpan<ScopedOperand> arguments, Optional<StringTableIndex> expression_string = {})
: Instruction(Type::CallDirectEval)
, m_dst(dst)
, m_callee(callee)
, m_this_value(this_value)
, m_argument_count(arguments.size())
, m_expression_string(expression_string)
{
for (size_t i = 0; i < arguments.size(); ++i)
m_arguments[i] = arguments[i];
}
size_t length_impl() const
{
return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Operand) * m_argument_count);
}
Operand dst() const { return m_dst; }
Operand callee() const { return m_callee; }
Operand this_value() const { return m_this_value; }
Optional<StringTableIndex> const& expression_string() const { return m_expression_string; }
u32 argument_count() const { return m_argument_count; }
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_callee);
visitor(m_this_value);
for (size_t i = 0; i < m_argument_count; i++)
visitor(m_arguments[i]);
}
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_callee;
Operand m_this_value;
u32 m_argument_count { 0 };
Optional<StringTableIndex> m_expression_string;
Operand m_arguments[];
};
class CallWithArgumentArray final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
CallWithArgumentArray(CallType type, Operand dst, Operand callee, Operand this_value, Operand arguments, Optional<StringTableIndex> expression_string = {})
: Instruction(Type::CallWithArgumentArray)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_callee(callee)
, m_this_value(this_value)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_arguments(arguments)
, m_type(type)
, m_expression_string(expression_string)
{
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
CallType call_type() const { return m_type; }
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand callee() const { return m_callee; }
Operand this_value() const { return m_this_value; }
Operand arguments() const { return m_arguments; }
Optional<StringTableIndex> const& expression_string() const { return m_expression_string; }
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_callee);
visitor(m_this_value);
visitor(m_arguments);
}
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_callee;
Operand m_this_value;
Operand m_arguments;
CallType m_type;
Optional<StringTableIndex> m_expression_string;
};
class SuperCallWithArgumentArray : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit SuperCallWithArgumentArray(Operand dst, Operand arguments, bool is_synthetic)
: Instruction(Type::SuperCallWithArgumentArray)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_arguments(arguments)
, m_is_synthetic(is_synthetic)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_arguments);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand arguments() const { return m_arguments; }
bool is_synthetic() const { return m_is_synthetic; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_arguments;
bool m_is_synthetic;
};
class NewClass final : public Instruction {
public:
static constexpr bool IsVariableLength = true;
explicit NewClass(Operand dst, Optional<Operand> super_class, ClassExpression const& class_expression, Optional<IdentifierTableIndex> lhs_name, ReadonlySpan<Optional<ScopedOperand>> elements_keys)
: Instruction(Type::NewClass)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_super_class(super_class)
, m_class_expression(class_expression)
, m_lhs_name(lhs_name)
, m_element_keys_count(elements_keys.size())
{
for (size_t i = 0; i < m_element_keys_count; i++) {
if (elements_keys[i].has_value())
m_element_keys[i] = elements_keys[i]->operand();
}
}
size_t length_impl() const
{
return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Optional<Operand>) * m_element_keys_count);
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
if (m_super_class.has_value())
visitor(m_super_class.value());
for (size_t i = 0; i < m_element_keys_count; i++) {
if (m_element_keys[i].has_value())
visitor(m_element_keys[i].value());
}
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Optional<Operand> const& super_class() const { return m_super_class; }
ClassExpression const& class_expression() const { return m_class_expression; }
Optional<IdentifierTableIndex> const& lhs_name() const { return m_lhs_name; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Optional<Operand> m_super_class;
ClassExpression const& m_class_expression;
Optional<IdentifierTableIndex> m_lhs_name;
size_t m_element_keys_count { 0 };
Optional<Operand> m_element_keys[];
};
class NewFunction final : public Instruction {
public:
explicit NewFunction(Operand dst, FunctionNode const& function_node, Optional<IdentifierTableIndex> lhs_name, Optional<Operand> home_object = {})
: Instruction(Type::NewFunction)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_function_node(function_node)
, m_lhs_name(lhs_name)
, m_home_object(move(home_object))
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
if (m_home_object.has_value())
visitor(m_home_object.value());
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
FunctionNode const& function_node() const { return m_function_node; }
Optional<IdentifierTableIndex> const& lhs_name() const { return m_lhs_name; }
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Optional<Operand> const& home_object() const { return m_home_object; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
FunctionNode const& m_function_node;
Optional<IdentifierTableIndex> m_lhs_name;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Optional<Operand> m_home_object;
};
class BlockDeclarationInstantiation final : public Instruction {
public:
explicit BlockDeclarationInstantiation(ScopeNode const& scope_node)
: Instruction(Type::BlockDeclarationInstantiation)
, m_scope_node(scope_node)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
ScopeNode const& scope_node() const { return m_scope_node; }
private:
ScopeNode const& m_scope_node;
};
class Return final : public Instruction {
public:
constexpr static bool IsTerminator = true;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit Return(Optional<Operand> value = {})
: Instruction(Type::Return)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_value(value)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
if (m_value.has_value())
visitor(m_value.value());
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Optional<Operand> const& value() const { return m_value; }
private:
Optional<Operand> m_value;
};
class Increment final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit Increment(Operand dst)
: Instruction(Type::Increment)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
private:
Operand m_dst;
};
class PostfixIncrement final : public Instruction {
public:
explicit PostfixIncrement(Operand dst, Operand src)
: Instruction(Type::PostfixIncrement)
, m_dst(dst)
, m_src(src)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_src);
}
Operand dst() const { return m_dst; }
Operand src() const { return m_src; }
private:
Operand m_dst;
Operand m_src;
};
class Decrement final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit Decrement(Operand dst)
: Instruction(Type::Decrement)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
private:
Operand m_dst;
};
class PostfixDecrement final : public Instruction {
public:
explicit PostfixDecrement(Operand dst, Operand src)
: Instruction(Type::PostfixDecrement)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_src(src)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_src);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand src() const { return m_src; }
private:
Operand m_dst;
Operand m_src;
};
class Throw final : public Instruction {
public:
constexpr static bool IsTerminator = true;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit Throw(Operand src)
: Instruction(Type::Throw)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_src(src)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_src);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand src() const { return m_src; }
private:
Operand m_src;
};
2022-12-09 18:48:57 +00:00
class ThrowIfNotObject final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
ThrowIfNotObject(Operand src)
: Instruction(Type::ThrowIfNotObject)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_src(src)
2022-12-09 18:48:57 +00:00
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_src);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand src() const { return m_src; }
private:
Operand m_src;
2022-12-09 18:48:57 +00:00
};
class ThrowIfNullish final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit ThrowIfNullish(Operand src)
: Instruction(Type::ThrowIfNullish)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_src(src)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_src);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand src() const { return m_src; }
private:
Operand m_src;
};
class ThrowIfTDZ final : public Instruction {
public:
explicit ThrowIfTDZ(Operand src)
: Instruction(Type::ThrowIfTDZ)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_src(src)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_src);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand src() const { return m_src; }
private:
Operand m_src;
};
LibJS: Implement bytecode generation for try..catch..finally EnterUnwindContext pushes an unwind context (exception handler and/or finalizer) onto a stack. LeaveUnwindContext pops the unwind context from that stack. Upon return to the interpreter loop we check whether the VM has an exception pending. If no unwind context is available we return from the loop. If an exception handler is available we clear the VM's exception, put the exception value into the accumulator register, clear the unwind context's handler and jump to the handler. If no handler is available but a finalizer is available we save the exception value + metadata (for later use by ContinuePendingUnwind), clear the VM's exception, pop the unwind context and jump to the finalizer. ContinuePendingUnwind checks whether a saved exception is available. If no saved exception is available it jumps to the resume label. Otherwise it stores the exception into the VM. The Jump after LeaveUnwindContext could be integrated into the LeaveUnwindContext instruction. I've kept them separate for now to make the bytecode more readable. > try { 1; throw "x" } catch (e) { 2 } finally { 3 }; 4 1: [ 0] EnterScope [ 10] EnterUnwindContext handler:@4 finalizer:@3 [ 38] EnterScope [ 48] LoadImmediate 1 [ 60] NewString 1 ("x") [ 70] Throw <for non-terminated blocks: insert LeaveUnwindContext + Jump @3 here> 2: [ 0] LoadImmediate 4 3: [ 0] EnterScope [ 10] LoadImmediate 3 [ 28] ContinuePendingUnwind resume:@2 4: [ 0] SetVariable 0 (e) [ 10] EnterScope [ 20] LoadImmediate 2 [ 38] LeaveUnwindContext [ 3c] Jump @3 String Table: 0: e 1: x
2021-06-10 13:04:38 +00:00
class EnterUnwindContext final : public Instruction {
public:
constexpr static bool IsTerminator = true;
EnterUnwindContext(Label entry_point)
: Instruction(Type::EnterUnwindContext)
, m_entry_point(move(entry_point))
LibJS: Implement bytecode generation for try..catch..finally EnterUnwindContext pushes an unwind context (exception handler and/or finalizer) onto a stack. LeaveUnwindContext pops the unwind context from that stack. Upon return to the interpreter loop we check whether the VM has an exception pending. If no unwind context is available we return from the loop. If an exception handler is available we clear the VM's exception, put the exception value into the accumulator register, clear the unwind context's handler and jump to the handler. If no handler is available but a finalizer is available we save the exception value + metadata (for later use by ContinuePendingUnwind), clear the VM's exception, pop the unwind context and jump to the finalizer. ContinuePendingUnwind checks whether a saved exception is available. If no saved exception is available it jumps to the resume label. Otherwise it stores the exception into the VM. The Jump after LeaveUnwindContext could be integrated into the LeaveUnwindContext instruction. I've kept them separate for now to make the bytecode more readable. > try { 1; throw "x" } catch (e) { 2 } finally { 3 }; 4 1: [ 0] EnterScope [ 10] EnterUnwindContext handler:@4 finalizer:@3 [ 38] EnterScope [ 48] LoadImmediate 1 [ 60] NewString 1 ("x") [ 70] Throw <for non-terminated blocks: insert LeaveUnwindContext + Jump @3 here> 2: [ 0] LoadImmediate 4 3: [ 0] EnterScope [ 10] LoadImmediate 3 [ 28] ContinuePendingUnwind resume:@2 4: [ 0] SetVariable 0 (e) [ 10] EnterScope [ 20] LoadImmediate 2 [ 38] LeaveUnwindContext [ 3c] Jump @3 String Table: 0: e 1: x
2021-06-10 13:04:38 +00:00
{
}
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_labels_impl(Function<void(Label&)> visitor)
{
visitor(m_entry_point);
}
auto& entry_point() const { return m_entry_point; }
LibJS: Implement bytecode generation for try..catch..finally EnterUnwindContext pushes an unwind context (exception handler and/or finalizer) onto a stack. LeaveUnwindContext pops the unwind context from that stack. Upon return to the interpreter loop we check whether the VM has an exception pending. If no unwind context is available we return from the loop. If an exception handler is available we clear the VM's exception, put the exception value into the accumulator register, clear the unwind context's handler and jump to the handler. If no handler is available but a finalizer is available we save the exception value + metadata (for later use by ContinuePendingUnwind), clear the VM's exception, pop the unwind context and jump to the finalizer. ContinuePendingUnwind checks whether a saved exception is available. If no saved exception is available it jumps to the resume label. Otherwise it stores the exception into the VM. The Jump after LeaveUnwindContext could be integrated into the LeaveUnwindContext instruction. I've kept them separate for now to make the bytecode more readable. > try { 1; throw "x" } catch (e) { 2 } finally { 3 }; 4 1: [ 0] EnterScope [ 10] EnterUnwindContext handler:@4 finalizer:@3 [ 38] EnterScope [ 48] LoadImmediate 1 [ 60] NewString 1 ("x") [ 70] Throw <for non-terminated blocks: insert LeaveUnwindContext + Jump @3 here> 2: [ 0] LoadImmediate 4 3: [ 0] EnterScope [ 10] LoadImmediate 3 [ 28] ContinuePendingUnwind resume:@2 4: [ 0] SetVariable 0 (e) [ 10] EnterScope [ 20] LoadImmediate 2 [ 38] LeaveUnwindContext [ 3c] Jump @3 String Table: 0: e 1: x
2021-06-10 13:04:38 +00:00
private:
Label m_entry_point;
LibJS: Implement bytecode generation for try..catch..finally EnterUnwindContext pushes an unwind context (exception handler and/or finalizer) onto a stack. LeaveUnwindContext pops the unwind context from that stack. Upon return to the interpreter loop we check whether the VM has an exception pending. If no unwind context is available we return from the loop. If an exception handler is available we clear the VM's exception, put the exception value into the accumulator register, clear the unwind context's handler and jump to the handler. If no handler is available but a finalizer is available we save the exception value + metadata (for later use by ContinuePendingUnwind), clear the VM's exception, pop the unwind context and jump to the finalizer. ContinuePendingUnwind checks whether a saved exception is available. If no saved exception is available it jumps to the resume label. Otherwise it stores the exception into the VM. The Jump after LeaveUnwindContext could be integrated into the LeaveUnwindContext instruction. I've kept them separate for now to make the bytecode more readable. > try { 1; throw "x" } catch (e) { 2 } finally { 3 }; 4 1: [ 0] EnterScope [ 10] EnterUnwindContext handler:@4 finalizer:@3 [ 38] EnterScope [ 48] LoadImmediate 1 [ 60] NewString 1 ("x") [ 70] Throw <for non-terminated blocks: insert LeaveUnwindContext + Jump @3 here> 2: [ 0] LoadImmediate 4 3: [ 0] EnterScope [ 10] LoadImmediate 3 [ 28] ContinuePendingUnwind resume:@2 4: [ 0] SetVariable 0 (e) [ 10] EnterScope [ 20] LoadImmediate 2 [ 38] LeaveUnwindContext [ 3c] Jump @3 String Table: 0: e 1: x
2021-06-10 13:04:38 +00:00
};
class ScheduleJump final : public Instruction {
public:
// Note: We use this instruction to tell the next `finally` block to
// continue execution with a specific break/continue target;
constexpr static bool IsTerminator = true;
ScheduleJump(Label target)
: Instruction(Type::ScheduleJump)
, m_target(target)
{
}
Label target() const { return m_target; }
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_labels_impl(Function<void(Label&)> visitor)
{
visitor(m_target);
}
private:
Label m_target;
};
class LeaveLexicalEnvironment final : public Instruction {
public:
LeaveLexicalEnvironment()
: Instruction(Type::LeaveLexicalEnvironment)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
};
class LeavePrivateEnvironment final : public Instruction {
public:
LeavePrivateEnvironment()
: Instruction(Type::LeavePrivateEnvironment)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
};
LibJS: Implement bytecode generation for try..catch..finally EnterUnwindContext pushes an unwind context (exception handler and/or finalizer) onto a stack. LeaveUnwindContext pops the unwind context from that stack. Upon return to the interpreter loop we check whether the VM has an exception pending. If no unwind context is available we return from the loop. If an exception handler is available we clear the VM's exception, put the exception value into the accumulator register, clear the unwind context's handler and jump to the handler. If no handler is available but a finalizer is available we save the exception value + metadata (for later use by ContinuePendingUnwind), clear the VM's exception, pop the unwind context and jump to the finalizer. ContinuePendingUnwind checks whether a saved exception is available. If no saved exception is available it jumps to the resume label. Otherwise it stores the exception into the VM. The Jump after LeaveUnwindContext could be integrated into the LeaveUnwindContext instruction. I've kept them separate for now to make the bytecode more readable. > try { 1; throw "x" } catch (e) { 2 } finally { 3 }; 4 1: [ 0] EnterScope [ 10] EnterUnwindContext handler:@4 finalizer:@3 [ 38] EnterScope [ 48] LoadImmediate 1 [ 60] NewString 1 ("x") [ 70] Throw <for non-terminated blocks: insert LeaveUnwindContext + Jump @3 here> 2: [ 0] LoadImmediate 4 3: [ 0] EnterScope [ 10] LoadImmediate 3 [ 28] ContinuePendingUnwind resume:@2 4: [ 0] SetVariable 0 (e) [ 10] EnterScope [ 20] LoadImmediate 2 [ 38] LeaveUnwindContext [ 3c] Jump @3 String Table: 0: e 1: x
2021-06-10 13:04:38 +00:00
class LeaveUnwindContext final : public Instruction {
public:
LeaveUnwindContext()
: Instruction(Type::LeaveUnwindContext)
LibJS: Implement bytecode generation for try..catch..finally EnterUnwindContext pushes an unwind context (exception handler and/or finalizer) onto a stack. LeaveUnwindContext pops the unwind context from that stack. Upon return to the interpreter loop we check whether the VM has an exception pending. If no unwind context is available we return from the loop. If an exception handler is available we clear the VM's exception, put the exception value into the accumulator register, clear the unwind context's handler and jump to the handler. If no handler is available but a finalizer is available we save the exception value + metadata (for later use by ContinuePendingUnwind), clear the VM's exception, pop the unwind context and jump to the finalizer. ContinuePendingUnwind checks whether a saved exception is available. If no saved exception is available it jumps to the resume label. Otherwise it stores the exception into the VM. The Jump after LeaveUnwindContext could be integrated into the LeaveUnwindContext instruction. I've kept them separate for now to make the bytecode more readable. > try { 1; throw "x" } catch (e) { 2 } finally { 3 }; 4 1: [ 0] EnterScope [ 10] EnterUnwindContext handler:@4 finalizer:@3 [ 38] EnterScope [ 48] LoadImmediate 1 [ 60] NewString 1 ("x") [ 70] Throw <for non-terminated blocks: insert LeaveUnwindContext + Jump @3 here> 2: [ 0] LoadImmediate 4 3: [ 0] EnterScope [ 10] LoadImmediate 3 [ 28] ContinuePendingUnwind resume:@2 4: [ 0] SetVariable 0 (e) [ 10] EnterScope [ 20] LoadImmediate 2 [ 38] LeaveUnwindContext [ 3c] Jump @3 String Table: 0: e 1: x
2021-06-10 13:04:38 +00:00
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
LibJS: Implement bytecode generation for try..catch..finally EnterUnwindContext pushes an unwind context (exception handler and/or finalizer) onto a stack. LeaveUnwindContext pops the unwind context from that stack. Upon return to the interpreter loop we check whether the VM has an exception pending. If no unwind context is available we return from the loop. If an exception handler is available we clear the VM's exception, put the exception value into the accumulator register, clear the unwind context's handler and jump to the handler. If no handler is available but a finalizer is available we save the exception value + metadata (for later use by ContinuePendingUnwind), clear the VM's exception, pop the unwind context and jump to the finalizer. ContinuePendingUnwind checks whether a saved exception is available. If no saved exception is available it jumps to the resume label. Otherwise it stores the exception into the VM. The Jump after LeaveUnwindContext could be integrated into the LeaveUnwindContext instruction. I've kept them separate for now to make the bytecode more readable. > try { 1; throw "x" } catch (e) { 2 } finally { 3 }; 4 1: [ 0] EnterScope [ 10] EnterUnwindContext handler:@4 finalizer:@3 [ 38] EnterScope [ 48] LoadImmediate 1 [ 60] NewString 1 ("x") [ 70] Throw <for non-terminated blocks: insert LeaveUnwindContext + Jump @3 here> 2: [ 0] LoadImmediate 4 3: [ 0] EnterScope [ 10] LoadImmediate 3 [ 28] ContinuePendingUnwind resume:@2 4: [ 0] SetVariable 0 (e) [ 10] EnterScope [ 20] LoadImmediate 2 [ 38] LeaveUnwindContext [ 3c] Jump @3 String Table: 0: e 1: x
2021-06-10 13:04:38 +00:00
};
class ContinuePendingUnwind final : public Instruction {
public:
constexpr static bool IsTerminator = true;
explicit ContinuePendingUnwind(Label resume_target)
: Instruction(Type::ContinuePendingUnwind)
LibJS: Implement bytecode generation for try..catch..finally EnterUnwindContext pushes an unwind context (exception handler and/or finalizer) onto a stack. LeaveUnwindContext pops the unwind context from that stack. Upon return to the interpreter loop we check whether the VM has an exception pending. If no unwind context is available we return from the loop. If an exception handler is available we clear the VM's exception, put the exception value into the accumulator register, clear the unwind context's handler and jump to the handler. If no handler is available but a finalizer is available we save the exception value + metadata (for later use by ContinuePendingUnwind), clear the VM's exception, pop the unwind context and jump to the finalizer. ContinuePendingUnwind checks whether a saved exception is available. If no saved exception is available it jumps to the resume label. Otherwise it stores the exception into the VM. The Jump after LeaveUnwindContext could be integrated into the LeaveUnwindContext instruction. I've kept them separate for now to make the bytecode more readable. > try { 1; throw "x" } catch (e) { 2 } finally { 3 }; 4 1: [ 0] EnterScope [ 10] EnterUnwindContext handler:@4 finalizer:@3 [ 38] EnterScope [ 48] LoadImmediate 1 [ 60] NewString 1 ("x") [ 70] Throw <for non-terminated blocks: insert LeaveUnwindContext + Jump @3 here> 2: [ 0] LoadImmediate 4 3: [ 0] EnterScope [ 10] LoadImmediate 3 [ 28] ContinuePendingUnwind resume:@2 4: [ 0] SetVariable 0 (e) [ 10] EnterScope [ 20] LoadImmediate 2 [ 38] LeaveUnwindContext [ 3c] Jump @3 String Table: 0: e 1: x
2021-06-10 13:04:38 +00:00
, m_resume_target(resume_target)
{
}
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_labels_impl(Function<void(Label&)> visitor)
{
visitor(m_resume_target);
}
auto& resume_target() const { return m_resume_target; }
LibJS: Implement bytecode generation for try..catch..finally EnterUnwindContext pushes an unwind context (exception handler and/or finalizer) onto a stack. LeaveUnwindContext pops the unwind context from that stack. Upon return to the interpreter loop we check whether the VM has an exception pending. If no unwind context is available we return from the loop. If an exception handler is available we clear the VM's exception, put the exception value into the accumulator register, clear the unwind context's handler and jump to the handler. If no handler is available but a finalizer is available we save the exception value + metadata (for later use by ContinuePendingUnwind), clear the VM's exception, pop the unwind context and jump to the finalizer. ContinuePendingUnwind checks whether a saved exception is available. If no saved exception is available it jumps to the resume label. Otherwise it stores the exception into the VM. The Jump after LeaveUnwindContext could be integrated into the LeaveUnwindContext instruction. I've kept them separate for now to make the bytecode more readable. > try { 1; throw "x" } catch (e) { 2 } finally { 3 }; 4 1: [ 0] EnterScope [ 10] EnterUnwindContext handler:@4 finalizer:@3 [ 38] EnterScope [ 48] LoadImmediate 1 [ 60] NewString 1 ("x") [ 70] Throw <for non-terminated blocks: insert LeaveUnwindContext + Jump @3 here> 2: [ 0] LoadImmediate 4 3: [ 0] EnterScope [ 10] LoadImmediate 3 [ 28] ContinuePendingUnwind resume:@2 4: [ 0] SetVariable 0 (e) [ 10] EnterScope [ 20] LoadImmediate 2 [ 38] LeaveUnwindContext [ 3c] Jump @3 String Table: 0: e 1: x
2021-06-10 13:04:38 +00:00
private:
Label m_resume_target;
};
class Yield final : public Instruction {
public:
constexpr static bool IsTerminator = true;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit Yield(Label continuation_label, Operand value)
: Instruction(Type::Yield)
, m_continuation_label(continuation_label)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_value(value)
{
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit Yield(nullptr_t, Operand value)
: Instruction(Type::Yield)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_value(value)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_labels_impl(Function<void(Label&)> visitor)
{
if (m_continuation_label.has_value())
visitor(m_continuation_label.value());
}
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_value);
}
auto& continuation() const { return m_continuation_label; }
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand value() const { return m_value; }
private:
Optional<Label> m_continuation_label;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_value;
};
class PrepareYield final : public Instruction {
public:
explicit PrepareYield(Operand dest, Operand value)
: Instruction(Type::PrepareYield)
, m_dest(dest)
, m_value(value)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dest);
visitor(m_value);
}
Operand destination() const { return m_dest; }
Operand value() const { return m_value; }
private:
Operand m_dest;
Operand m_value;
};
class Await final : public Instruction {
public:
constexpr static bool IsTerminator = true;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit Await(Label continuation_label, Operand argument)
: Instruction(Type::Await)
, m_continuation_label(continuation_label)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_argument(argument)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_labels_impl(Function<void(Label&)> visitor)
{
visitor(m_continuation_label);
}
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_argument);
}
auto& continuation() const { return m_continuation_label; }
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand argument() const { return m_argument; }
private:
Label m_continuation_label;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_argument;
};
class GetIterator final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
GetIterator(Operand dst, Operand iterable, IteratorHint hint = IteratorHint::Sync)
: Instruction(Type::GetIterator)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_iterable(iterable)
, m_hint(hint)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_iterable);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand iterable() const { return m_iterable; }
IteratorHint hint() const { return m_hint; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_iterable;
IteratorHint m_hint { IteratorHint::Sync };
};
class GetObjectFromIteratorRecord final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
GetObjectFromIteratorRecord(Operand object, Operand iterator_record)
: Instruction(Type::GetObjectFromIteratorRecord)
, m_object(object)
, m_iterator_record(iterator_record)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_object);
visitor(m_iterator_record);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand object() const { return m_object; }
Operand iterator_record() const { return m_iterator_record; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_object;
Operand m_iterator_record;
};
class GetNextMethodFromIteratorRecord final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
GetNextMethodFromIteratorRecord(Operand next_method, Operand iterator_record)
: Instruction(Type::GetNextMethodFromIteratorRecord)
, m_next_method(next_method)
, m_iterator_record(iterator_record)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_next_method);
visitor(m_iterator_record);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand next_method() const { return m_next_method; }
Operand iterator_record() const { return m_iterator_record; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_next_method;
Operand m_iterator_record;
};
2022-12-09 18:48:57 +00:00
class GetMethod final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
GetMethod(Operand dst, Operand object, IdentifierTableIndex property)
: Instruction(Type::GetMethod)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_object(object)
2022-12-09 18:48:57 +00:00
, m_property(property)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_object);
}
2022-12-09 18:48:57 +00:00
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand object() const { return m_object; }
IdentifierTableIndex property() const { return m_property; }
2022-12-09 18:48:57 +00:00
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
Operand m_object;
2022-12-09 18:48:57 +00:00
IdentifierTableIndex m_property;
};
class GetObjectPropertyIterator final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
GetObjectPropertyIterator(Operand dst, Operand object)
: Instruction(Type::GetObjectPropertyIterator)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_object(object)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_object);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand object() const { return m_object; }
private:
Operand m_dst;
Operand m_object;
};
2022-12-09 18:48:57 +00:00
class IteratorClose final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
IteratorClose(Operand iterator_record, Completion::Type completion_type, Optional<Value> completion_value)
: Instruction(Type::IteratorClose)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_iterator_record(iterator_record)
2022-12-09 18:48:57 +00:00
, m_completion_type(completion_type)
, m_completion_value(completion_value)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_iterator_record);
}
2022-12-09 18:48:57 +00:00
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand iterator_record() const { return m_iterator_record; }
Completion::Type completion_type() const { return m_completion_type; }
Optional<Value> const& completion_value() const { return m_completion_value; }
2022-12-09 18:48:57 +00:00
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_iterator_record;
2022-12-09 18:48:57 +00:00
Completion::Type m_completion_type { Completion::Type::Normal };
Optional<Value> m_completion_value;
};
class AsyncIteratorClose final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
AsyncIteratorClose(Operand iterator_record, Completion::Type completion_type, Optional<Value> completion_value)
: Instruction(Type::AsyncIteratorClose)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_iterator_record(iterator_record)
, m_completion_type(completion_type)
, m_completion_value(completion_value)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_iterator_record);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand iterator_record() const { return m_iterator_record; }
Completion::Type completion_type() const { return m_completion_type; }
Optional<Value> const& completion_value() const { return m_completion_value; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_iterator_record;
Completion::Type m_completion_type { Completion::Type::Normal };
Optional<Value> m_completion_value;
};
class IteratorNext final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
IteratorNext(Operand dst, Operand iterator_record)
: Instruction(Type::IteratorNext)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_iterator_record(iterator_record)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
visitor(m_iterator_record);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
Operand iterator_record() const { return m_iterator_record; }
private:
Operand m_dst;
Operand m_iterator_record;
};
class ResolveThisBinding final : public Instruction {
public:
ResolveThisBinding()
: Instruction(Type::ResolveThisBinding)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)>) { }
};
class ResolveSuperBase final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit ResolveSuperBase(Operand dst)
: Instruction(Type::ResolveSuperBase)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
private:
Operand m_dst;
};
class GetNewTarget final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit GetNewTarget(Operand dst)
: Instruction(Type::GetNewTarget)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
private:
Operand m_dst;
};
class GetImportMeta final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
explicit GetImportMeta(Operand dst)
: Instruction(Type::GetImportMeta)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
{
}
void execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
private:
Operand m_dst;
};
class TypeofBinding final : public Instruction {
public:
TypeofBinding(Operand dst, IdentifierTableIndex identifier)
: Instruction(Type::TypeofBinding)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_dst(dst)
, m_identifier(identifier)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand dst() const { return m_dst; }
IdentifierTableIndex identifier() const { return m_identifier; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_dst;
IdentifierTableIndex m_identifier;
mutable EnvironmentCoordinate m_cache;
};
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
class End final : public Instruction {
public:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
constexpr static bool IsTerminator = true;
explicit End(Operand value)
: Instruction(Type::End)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_value(value)
{
}
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_value);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand value() const { return m_value; }
private:
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
Operand m_value;
};
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
class Dump final : public Instruction {
public:
explicit Dump(StringView text, Operand value)
: Instruction(Type::Dump)
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
, m_text(text)
, m_value(value)
{
}
void execute_impl(Bytecode::Interpreter&) const;
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_value);
}
LibJS/Bytecode: Move to a new bytecode format This patch moves us away from the accumulator-based bytecode format to one with explicit source and destination registers. The new format has multiple benefits: - ~25% faster on the Kraken and Octane benchmarks :^) - Fewer instructions to accomplish the same thing - Much easier for humans to read(!) Because this change requires a fundamental shift in how bytecode is generated, it is quite comprehensive. Main implementation mechanism: generate_bytecode() virtual function now takes an optional "preferred dst" operand, which allows callers to communicate when they have an operand that would be optimal for the result to go into. It also returns an optional "actual dst" operand, which is where the completion value (if any) of the AST node is stored after the node has "executed". One thing of note that's new: because instructions can now take locals as operands, this means we got rid of the GetLocal instruction. A side-effect of that is we have to think about the temporal deadzone (TDZ) a bit differently for locals (GetLocal would previously check for empty values and interpret that as a TDZ access and throw). We now insert special ThrowIfTDZ instructions in places where a local access may be in the TDZ, to maintain the correct behavior. There are a number of progressions and regressions from this test: A number of async generator tests have been accidentally fixed while converting the implementation to the new bytecode format. It didn't seem useful to preserve bugs in the original code when converting it. Some "does eval() return the correct completion value" tests have regressed, in particular ones related to propagating the appropriate completion after control flow statements like continue and break. These are all fairly obscure issues, and I believe we can continue working on them separately. The net test262 result is a progression though. :^)
2024-02-04 07:00:54 +00:00
private:
StringView m_text;
Operand m_value;
};
}