From cea59b6642e9e8b83085d74ea3f126a5af2d80fa Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 7 May 2024 21:36:56 +0200 Subject: [PATCH] LibJS/Bytecode: Reuse bytecode registers This patch adds a register freelist to Bytecode::Generator and switches all operands inside the generator to a new ScopedOperand type that is ref-counted and automatically frees the register when nothing uses it. This dramatically reduces the size of bytecode executable register windows, which were often in the several thousands of registers for large functions. Most functions now use less than 100 registers. --- Userland/Libraries/LibJS/AST.h | 137 ++--- .../Libraries/LibJS/Bytecode/ASTCodegen.cpp | 526 +++++++++--------- .../Libraries/LibJS/Bytecode/BasicBlock.h | 6 +- .../Libraries/LibJS/Bytecode/Generator.cpp | 82 ++- Userland/Libraries/LibJS/Bytecode/Generator.h | 57 +- Userland/Libraries/LibJS/Bytecode/Op.h | 5 +- Userland/Libraries/LibJS/Bytecode/Operand.h | 2 + .../LibJS/Bytecode/ScopedOperand.cpp | 23 + .../Libraries/LibJS/Bytecode/ScopedOperand.h | 47 ++ Userland/Libraries/LibJS/CMakeLists.txt | 1 + 10 files changed, 505 insertions(+), 381 deletions(-) create mode 100644 Userland/Libraries/LibJS/Bytecode/ScopedOperand.cpp create mode 100644 Userland/Libraries/LibJS/Bytecode/ScopedOperand.h diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index 1c7b74a3804..2e06c10ed1e 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -55,7 +56,7 @@ public: // NOTE: This is here to stop ASAN complaining about mismatch between new/delete sizes in ASTNodeWithTailArray. void operator delete(void* ptr) { ::operator delete(ptr); } - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const; virtual void dump(int indent) const; [[nodiscard]] SourceRange source_range() const; @@ -175,8 +176,8 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_labelled_evaluation(Bytecode::Generator&, Vector const&, Optional preferred_dst = {}) const; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_labelled_evaluation(Bytecode::Generator&, Vector const&, Optional preferred_dst = {}) const; DeprecatedFlyString const& label() const { return m_label; } DeprecatedFlyString& label() { return m_label; } @@ -204,7 +205,7 @@ class IterationStatement : public Statement { public: using Statement::Statement; - virtual Bytecode::CodeGenerationErrorOr> generate_labelled_evaluation(Bytecode::Generator&, Vector const&, Optional preferred_dst = {}) const; + virtual Bytecode::CodeGenerationErrorOr> generate_labelled_evaluation(Bytecode::Generator&, Vector const&, Optional preferred_dst = {}) const; private: virtual bool is_iteration_statement() const final { return true; } @@ -216,7 +217,7 @@ public: : Statement(move(source_range)) { } - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; }; class ErrorStatement final : public Statement { @@ -236,7 +237,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; Expression const& expression() const { return m_expression; } @@ -299,7 +300,7 @@ public: Vector> const& children() const { return m_children; } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; void add_var_scoped_declaration(NonnullRefPtr variables); void add_lexical_declaration(NonnullRefPtr variables); @@ -387,7 +388,7 @@ public: virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; bool has_bound_name(DeprecatedFlyString const& name) const; Vector const& entries() const { return m_entries; } @@ -484,7 +485,7 @@ public: virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; bool has_export(DeprecatedFlyString const& export_name) const; @@ -671,7 +672,7 @@ public: void set_is_global() { m_is_global = true; } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: virtual bool is_identifier() const override { return true; } @@ -761,7 +762,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; ThrowCompletionOr for_each_bound_identifier(ThrowCompletionOrVoidCallback&&) const override; @@ -787,8 +788,8 @@ public: virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode_with_lhs_name(Bytecode::Generator&, Optional lhs_name, Optional preferred_dst = {}) const; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode_with_lhs_name(Bytecode::Generator&, Optional lhs_name, Optional preferred_dst = {}) const; bool has_name() const { return !name().is_empty(); } @@ -819,7 +820,7 @@ public: bool is_yield_from() const { return m_is_yield_from; } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: RefPtr m_argument; @@ -835,7 +836,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: NonnullRefPtr m_argument; @@ -852,7 +853,7 @@ public: Expression const* argument() const { return m_argument; } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: RefPtr m_argument; @@ -873,7 +874,7 @@ public: Statement const* alternate() const { return m_alternate; } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: NonnullRefPtr m_predicate; @@ -894,8 +895,8 @@ public: Statement const& body() const { return *m_body; } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_labelled_evaluation(Bytecode::Generator&, Vector const&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_labelled_evaluation(Bytecode::Generator&, Vector const&, Optional preferred_dst = {}) const override; private: NonnullRefPtr m_test; @@ -915,8 +916,8 @@ public: Statement const& body() const { return *m_body; } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_labelled_evaluation(Bytecode::Generator&, Vector const&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_labelled_evaluation(Bytecode::Generator&, Vector const&, Optional preferred_dst = {}) const override; private: NonnullRefPtr m_test; @@ -936,7 +937,7 @@ public: Statement const& body() const { return *m_body; } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: NonnullRefPtr m_object; @@ -960,8 +961,8 @@ public: Statement const& body() const { return *m_body; } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_labelled_evaluation(Bytecode::Generator&, Vector const&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_labelled_evaluation(Bytecode::Generator&, Vector const&, Optional preferred_dst = {}) const override; private: RefPtr m_init; @@ -984,8 +985,8 @@ public: Expression const& rhs() const { return *m_rhs; } Statement const& body() const { return *m_body; } - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_labelled_evaluation(Bytecode::Generator&, Vector const&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_labelled_evaluation(Bytecode::Generator&, Vector const&, Optional preferred_dst = {}) const override; virtual void dump(int indent) const override; private: @@ -1008,8 +1009,8 @@ public: Expression const& rhs() const { return *m_rhs; } Statement const& body() const { return *m_body; } - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_labelled_evaluation(Bytecode::Generator&, Vector const&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_labelled_evaluation(Bytecode::Generator&, Vector const&, Optional preferred_dst = {}) const override; virtual void dump(int indent) const override; private: @@ -1028,8 +1029,8 @@ public: { } - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_labelled_evaluation(Bytecode::Generator&, Vector const&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_labelled_evaluation(Bytecode::Generator&, Vector const&, Optional preferred_dst = {}) const override; virtual void dump(int indent) const override; private: @@ -1074,7 +1075,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: BinaryOp m_op; @@ -1099,7 +1100,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: LogicalOp m_op; @@ -1127,7 +1128,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: UnaryOp m_op; @@ -1144,7 +1145,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: Vector> m_expressions; @@ -1170,7 +1171,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; virtual Value value() const override { return Value(m_value); } @@ -1187,7 +1188,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; virtual Value value() const override { return m_value; } @@ -1206,7 +1207,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: ByteString m_value; @@ -1221,7 +1222,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; ByteString const& value() const { return m_value; } @@ -1239,7 +1240,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; virtual Value value() const override { return js_null(); } }; @@ -1257,7 +1258,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; regex::Parser::Result const& parsed_regex() const { return m_parsed_regex; } ByteString const& parsed_pattern() const { return m_parsed_pattern; } @@ -1402,7 +1403,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; virtual bool is_super_expression() const override { return true; } }; @@ -1425,8 +1426,8 @@ public: RefPtr constructor() const { return m_constructor; } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode_with_lhs_name(Bytecode::Generator&, Optional lhs_name, Optional preferred_dst = {}) const; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode_with_lhs_name(Bytecode::Generator&, Optional lhs_name, Optional preferred_dst = {}) const; bool has_name() const { return m_name; } @@ -1454,7 +1455,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; ThrowCompletionOr for_each_bound_identifier(ThrowCompletionOrVoidCallback&&) const override; @@ -1482,7 +1483,7 @@ public: } virtual void dump(int) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: NonnullRefPtr m_expression; @@ -1498,7 +1499,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: NonnullRefPtr m_target; @@ -1511,7 +1512,7 @@ public: { } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; }; struct CallExpressionArgument { @@ -1541,7 +1542,7 @@ public: static NonnullRefPtr create(SourceRange, NonnullRefPtr callee, ReadonlySpan arguments, InvocationStyleEnum invocation_style, InsideParenthesesEnum inside_parens); virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; Expression const& callee() const { return m_callee; } @@ -1609,7 +1610,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: Vector const m_arguments; @@ -1654,7 +1655,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: AssignmentOp m_op; @@ -1678,7 +1679,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: virtual bool is_update_expression() const override { return true; } @@ -1738,7 +1739,7 @@ public: DeclarationKind declaration_kind() const { return m_declaration_kind; } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; Vector> const& declarations() const { return m_declarations; } @@ -1824,7 +1825,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: virtual bool is_object_expression() const override { return true; } @@ -1843,7 +1844,7 @@ public: Vector> const& elements() const { return m_elements; } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: virtual bool is_array_expression() const override { return true; } @@ -1867,7 +1868,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; Vector> const& expressions() const { return m_expressions; } Vector> const& raw_strings() const { return m_raw_strings; } @@ -1887,7 +1888,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: NonnullRefPtr const m_tag; @@ -1905,7 +1906,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; bool is_computed() const { return m_computed; } Expression const& object() const { return *m_object; } @@ -1957,7 +1958,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; Expression const& base() const { return *m_base; } Vector const& references() const { return m_references; } @@ -1981,7 +1982,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: Type m_type; @@ -1997,7 +1998,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: virtual bool is_import_call() const override { return true; } @@ -2017,7 +2018,7 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: NonnullRefPtr m_test; @@ -2066,7 +2067,7 @@ public: BlockStatement const* finalizer() const { return m_finalizer; } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: NonnullRefPtr m_block; @@ -2085,7 +2086,7 @@ public: Expression const& argument() const { return m_argument; } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: NonnullRefPtr m_argument; @@ -2116,8 +2117,8 @@ public: } virtual void dump(int indent) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; - virtual Bytecode::CodeGenerationErrorOr> generate_labelled_evaluation(Bytecode::Generator&, Vector const&, Optional preferred_dst = {}) const; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_labelled_evaluation(Bytecode::Generator&, Vector const&, Optional preferred_dst = {}) const; void add_case(NonnullRefPtr switch_case) { m_cases.append(move(switch_case)); } @@ -2135,7 +2136,7 @@ public: } Optional const& target_label() const { return m_target_label; } - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; private: Optional m_target_label; @@ -2149,7 +2150,7 @@ public: { } - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; Optional const& target_label() const { return m_target_label; } @@ -2164,7 +2165,7 @@ public: { } - virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; + virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; }; class SyntheticReferenceExpression final : public Expression { diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 6d95b6940f7..175d7144967 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -19,14 +19,16 @@ namespace JS { -static Bytecode::Operand choose_dst(Bytecode::Generator& generator, Optional const& preferred_dst) +using namespace JS::Bytecode; + +static ScopedOperand choose_dst(Bytecode::Generator& generator, Optional const& preferred_dst) { if (preferred_dst.has_value()) return preferred_dst.value(); - return Bytecode::Operand(generator.allocate_register()); + return generator.allocate_register(); } -Bytecode::CodeGenerationErrorOr> ASTNode::generate_bytecode(Bytecode::Generator&, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ASTNode::generate_bytecode(Bytecode::Generator&, [[maybe_unused]] Optional preferred_dst) const { return Bytecode::CodeGenerationError { this, @@ -34,7 +36,7 @@ Bytecode::CodeGenerationErrorOr> ASTNode::generate_b }; } -Bytecode::CodeGenerationErrorOr> ScopeNode::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ScopeNode::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); bool did_create_lexical_environment = false; @@ -50,7 +52,7 @@ Bytecode::CodeGenerationErrorOr> ScopeNode::generate // FunctionDeclarationInstantiation is handled by the C++ AO. } - Optional last_result; + Optional last_result; for (auto& child : children()) { auto result = TRY(child->generate_bytecode(generator)); if (result.has_value()) @@ -65,18 +67,18 @@ Bytecode::CodeGenerationErrorOr> ScopeNode::generate return last_result; } -Bytecode::CodeGenerationErrorOr> EmptyStatement::generate_bytecode(Bytecode::Generator&, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> EmptyStatement::generate_bytecode(Bytecode::Generator&, [[maybe_unused]] Optional preferred_dst) const { - return Optional {}; + return Optional {}; } -Bytecode::CodeGenerationErrorOr> ExpressionStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ExpressionStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); return m_expression->generate_bytecode(generator); } -Bytecode::CodeGenerationErrorOr> BinaryExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> BinaryExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); if (m_op == BinaryOp::In && is(*m_lhs)) { @@ -164,7 +166,7 @@ Bytecode::CodeGenerationErrorOr> BinaryExpression::g return dst; } -Bytecode::CodeGenerationErrorOr> LogicalExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> LogicalExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); auto dst = choose_dst(generator, preferred_dst); @@ -213,13 +215,13 @@ Bytecode::CodeGenerationErrorOr> LogicalExpression:: return dst; } -Bytecode::CodeGenerationErrorOr> UnaryExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> UnaryExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); if (m_op == UnaryOp::Delete) return generator.emit_delete_reference(m_lhs); - Optional src; + Optional src; // Typeof needs some special handling for when the LHS is an Identifier. Namely, it shouldn't throw on unresolvable references, but instead return "undefined". if (m_op != UnaryOp::Typeof) src = TRY(m_lhs->generate_bytecode(generator)).value(); @@ -261,25 +263,25 @@ Bytecode::CodeGenerationErrorOr> UnaryExpression::ge return dst; } -Bytecode::CodeGenerationErrorOr> NumericLiteral::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> NumericLiteral::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); return generator.add_constant(Value(m_value), Bytecode::Generator::DeduplicateConstant::No); } -Bytecode::CodeGenerationErrorOr> BooleanLiteral::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> BooleanLiteral::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); return generator.add_constant(Value(m_value)); } -Bytecode::CodeGenerationErrorOr> NullLiteral::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> NullLiteral::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); return generator.add_constant(js_null()); } -Bytecode::CodeGenerationErrorOr> BigIntLiteral::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> BigIntLiteral::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); // 1. Return the NumericValue of NumericLiteral as defined in 12.8.3. @@ -296,13 +298,13 @@ Bytecode::CodeGenerationErrorOr> BigIntLiteral::gene return generator.add_constant(BigInt::create(generator.vm(), move(integer)), Bytecode::Generator::DeduplicateConstant::No); } -Bytecode::CodeGenerationErrorOr> StringLiteral::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> StringLiteral::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); return generator.add_constant(PrimitiveString::create(generator.vm(), m_value), Bytecode::Generator::DeduplicateConstant::No); } -Bytecode::CodeGenerationErrorOr> RegExpLiteral::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> RegExpLiteral::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); auto source_index = generator.intern_string(m_pattern); @@ -317,12 +319,12 @@ Bytecode::CodeGenerationErrorOr> RegExpLiteral::gene return dst; } -Bytecode::CodeGenerationErrorOr> Identifier::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> Identifier::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); if (is_local()) { - auto local = Bytecode::Operand(Bytecode::Operand::Type::Local, local_variable_index()); + auto local = generator.local(local_variable_index()); if (!generator.is_local_initialized(local_variable_index())) { generator.emit(local); } @@ -338,9 +340,9 @@ Bytecode::CodeGenerationErrorOr> Identifier::generat return dst; } -static Bytecode::CodeGenerationErrorOr> arguments_to_array_for_call(Bytecode::Generator& generator, ReadonlySpan arguments) +static Bytecode::CodeGenerationErrorOr> arguments_to_array_for_call(Bytecode::Generator& generator, ReadonlySpan arguments) { - auto dst = Bytecode::Operand(generator.allocate_register()); + auto dst = generator.allocate_register(); if (arguments.is_empty()) { generator.emit(dst); return dst; @@ -348,11 +350,13 @@ static Bytecode::CodeGenerationErrorOr> arguments_to auto first_spread = find_if(arguments.begin(), arguments.end(), [](auto el) { return el.is_spread; }); + Vector registers; Bytecode::Register args_start_reg { 0 }; for (auto it = arguments.begin(); it != first_spread; ++it) { - auto reg = generator.allocate_register(); + auto reg = generator.allocate_sequential_register(); + registers.append(reg); if (args_start_reg.index() == 0) - args_start_reg = reg; + args_start_reg = reg.operand().as_register(); } u32 i = 0; for (auto it = arguments.begin(); it != first_spread; ++it, ++i) { @@ -377,10 +381,10 @@ static Bytecode::CodeGenerationErrorOr> arguments_to return dst; } -Bytecode::CodeGenerationErrorOr> SuperCall::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> SuperCall::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - Optional arguments; + Optional arguments; if (m_is_synthetic == IsPartOfSyntheticConstructor::Yes) { // NOTE: This is the case where we have a fake constructor(...args) { super(...args); } which // shouldn't call @@iterator of %Array.prototype%. @@ -398,21 +402,21 @@ Bytecode::CodeGenerationErrorOr> SuperCall::generate return dst; } -static Bytecode::CodeGenerationErrorOr generate_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode, Bytecode::Operand const& input_value, bool create_variables); +static Bytecode::CodeGenerationErrorOr generate_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode, ScopedOperand const& input_value, bool create_variables); -Bytecode::CodeGenerationErrorOr> AssignmentExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> AssignmentExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); if (m_op == AssignmentOp::Assignment) { // AssignmentExpression : LeftHandSideExpression = AssignmentExpression return m_lhs.visit( // 1. If LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral, then - [&](NonnullRefPtr const& lhs) -> Bytecode::CodeGenerationErrorOr> { + [&](NonnullRefPtr const& lhs) -> Bytecode::CodeGenerationErrorOr> { // a. Let lref be the result of evaluating LeftHandSideExpression. // b. ReturnIfAbrupt(lref). - Optional base; - Optional computed_property; - Optional this_value; + Optional base; + Optional computed_property; + Optional this_value; bool lhs_is_super_expression = false; @@ -435,7 +439,7 @@ Bytecode::CodeGenerationErrorOr> AssignmentExpressio if (expression.is_computed()) { auto property = TRY(expression.property().generate_bytecode(generator)).value(); - computed_property = Bytecode::Operand(generator.allocate_register()); + computed_property = generator.allocate_register(); generator.emit(*computed_property, property); // To be continued later with PutByValue. @@ -458,7 +462,7 @@ Bytecode::CodeGenerationErrorOr> AssignmentExpressio // 2. Assert: env.HasSuperBinding() is true. // 3. Let baseValue be ? env.GetSuperBase(). // 4. Return the Reference Record { [[Base]]: baseValue, [[ReferencedName]]: propertyKey, [[Strict]]: strict, [[ThisValue]]: actualThis }. - base = Bytecode::Operand(generator.allocate_register()); + base = generator.allocate_register(); generator.emit(*base); } } else if (is(*lhs)) { @@ -474,7 +478,7 @@ Bytecode::CodeGenerationErrorOr> AssignmentExpressio // d. Else, // i. Let rref be the result of evaluating AssignmentExpression. // ii. Let rval be ? GetValue(rref). - auto rval = TRY([&]() -> Bytecode::CodeGenerationErrorOr { + auto rval = TRY([&]() -> Bytecode::CodeGenerationErrorOr { if (lhs->is_identifier()) { return TRY(generator.emit_named_evaluation_if_anonymous_function(*m_rhs, generator.intern_identifier(static_cast(*lhs).string()))).value(); } else { @@ -521,7 +525,7 @@ Bytecode::CodeGenerationErrorOr> AssignmentExpressio return rval; }, // 2. Let assignmentPattern be the AssignmentPattern that is covered by LeftHandSideExpression. - [&](NonnullRefPtr const& pattern) -> Bytecode::CodeGenerationErrorOr> { + [&](NonnullRefPtr const& pattern) -> Bytecode::CodeGenerationErrorOr> { // 3. Let rref be the result of evaluating AssignmentExpression. // 4. Let rval be ? GetValue(rref). auto rval = TRY(m_rhs->generate_bytecode(generator)).value(); @@ -577,7 +581,7 @@ Bytecode::CodeGenerationErrorOr> AssignmentExpressio if (rhs_block_ptr) generator.switch_to_basic_block(*rhs_block_ptr); - auto rhs = TRY([&]() -> Bytecode::CodeGenerationErrorOr { + auto rhs = TRY([&]() -> Bytecode::CodeGenerationErrorOr { if (lhs_expression->is_identifier()) { return TRY(generator.emit_named_evaluation_if_anonymous_function(*m_rhs, generator.intern_identifier(static_cast(*lhs_expression).string()))).value(); } @@ -659,7 +663,7 @@ Bytecode::CodeGenerationErrorOr> AssignmentExpressio // 14.13.3 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-labelled-statements-runtime-semantics-evaluation // LabelledStatement : LabelIdentifier : LabelledItem -Bytecode::CodeGenerationErrorOr> LabelledStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> LabelledStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); // Return ? LabelledEvaluation of this LabelledStatement with argument « ». @@ -668,7 +672,7 @@ Bytecode::CodeGenerationErrorOr> LabelledStatement:: // 14.13.4 Runtime Semantics: LabelledEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-labelledevaluation // LabelledStatement : LabelIdentifier : LabelledItem -Bytecode::CodeGenerationErrorOr> LabelledStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> LabelledStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); // Convert the m_labelled_item NNRP to a reference early so we don't have to do it every single time we want to use it. @@ -683,7 +687,7 @@ Bytecode::CodeGenerationErrorOr> LabelledStatement:: new_label_set.append(m_label); // 3. Let stmtResult be LabelledEvaluation of LabelledItem with argument newLabelSet. - Optional stmt_result; + Optional stmt_result; if (is(labelled_item)) { auto const& iteration_statement = static_cast(labelled_item); stmt_result = TRY(iteration_statement.generate_labelled_evaluation(generator, new_label_set)); @@ -716,7 +720,7 @@ Bytecode::CodeGenerationErrorOr> LabelledStatement:: return stmt_result; } -Bytecode::CodeGenerationErrorOr> IterationStatement::generate_labelled_evaluation(Bytecode::Generator&, Vector const&, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> IterationStatement::generate_labelled_evaluation(Bytecode::Generator&, Vector const&, [[maybe_unused]] Optional preferred_dst) const { return Bytecode::CodeGenerationError { this, @@ -724,13 +728,13 @@ Bytecode::CodeGenerationErrorOr> IterationStatement: }; } -Bytecode::CodeGenerationErrorOr> WhileStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> WhileStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); return generate_labelled_evaluation(generator, {}); } -Bytecode::CodeGenerationErrorOr> WhileStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> WhileStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); // test @@ -742,7 +746,7 @@ Bytecode::CodeGenerationErrorOr> WhileStatement::gen auto& body_block = generator.make_block(); auto& end_block = generator.make_block(); - auto result = Bytecode::Operand(generator.allocate_register()); + auto result = generator.allocate_register(); generator.emit(result, generator.add_constant(js_undefined())); generator.emit(Bytecode::Label { test_block }); @@ -771,13 +775,13 @@ Bytecode::CodeGenerationErrorOr> WhileStatement::gen return result; } -Bytecode::CodeGenerationErrorOr> DoWhileStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> DoWhileStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); return generate_labelled_evaluation(generator, {}); } -Bytecode::CodeGenerationErrorOr> DoWhileStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> DoWhileStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); // jump always (true) body @@ -791,7 +795,7 @@ Bytecode::CodeGenerationErrorOr> DoWhileStatement::g auto& load_result_and_jump_to_end_block = generator.make_block(); auto& end_block = generator.make_block(); - auto completion_value = Bytecode::Operand(generator.allocate_register()); + auto completion_value = generator.allocate_register(); generator.emit(completion_value, generator.add_constant(js_undefined())); // jump to the body block @@ -824,13 +828,13 @@ Bytecode::CodeGenerationErrorOr> DoWhileStatement::g return completion_value; } -Bytecode::CodeGenerationErrorOr> ForStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ForStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); return generate_labelled_evaluation(generator, {}); } -Bytecode::CodeGenerationErrorOr> ForStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ForStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); // init @@ -938,11 +942,11 @@ Bytecode::CodeGenerationErrorOr> ForStatement::gener return body_result; } -Bytecode::CodeGenerationErrorOr> ObjectExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ObjectExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - auto object = Bytecode::Operand(generator.allocate_register()); + auto object = generator.allocate_register(); generator.emit(object); if (m_properties.is_empty()) @@ -974,7 +978,7 @@ Bytecode::CodeGenerationErrorOr> ObjectExpression::g auto& string_literal = static_cast(property->key()); Bytecode::IdentifierTableIndex key_name = generator.intern_identifier(string_literal.value()); - Optional value; + Optional value; if (property_kind == Bytecode::Op::PropertyKind::ProtoSetter) { value = TRY(property->value().generate_bytecode(generator)).value(); } else if (property_kind != Bytecode::Op::PropertyKind::Spread) { @@ -993,7 +997,7 @@ Bytecode::CodeGenerationErrorOr> ObjectExpression::g generator.emit(object, key_name, *value, property_kind, generator.next_property_lookup_cache()); } else { auto property_name = TRY(property->key().generate_bytecode(generator)).value(); - Optional value; + Optional value; if (property_kind != Bytecode::Op::PropertyKind::Spread) value = TRY(property->value().generate_bytecode(generator)).value(); else @@ -1007,7 +1011,7 @@ Bytecode::CodeGenerationErrorOr> ObjectExpression::g return object; } -Bytecode::CodeGenerationErrorOr> ArrayExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ArrayExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); if (m_elements.is_empty()) { @@ -1033,15 +1037,17 @@ Bytecode::CodeGenerationErrorOr> ArrayExpression::ge auto first_spread = find_if(m_elements.begin(), m_elements.end(), [](auto el) { return el && is(*el); }); - Bytecode::Register args_start_reg { 0 }; + Vector registers; + Optional args_start_reg; for (auto it = m_elements.begin(); it != first_spread; ++it) { - auto reg = generator.allocate_register(); - if (args_start_reg.index() == 0) - args_start_reg = reg; + auto reg = generator.allocate_sequential_register(); + registers.append(reg); + if (!args_start_reg.has_value()) + args_start_reg = reg.operand().as_register(); } u32 i = 0; for (auto it = m_elements.begin(); it != first_spread; ++it, ++i) { - Bytecode::Register reg { args_start_reg.index() + i }; + Bytecode::Register reg { args_start_reg->index() + i }; if (*it) { auto value = TRY((*it)->generate_bytecode(generator)).value(); generator.emit(Bytecode::Operand(reg), value); @@ -1050,8 +1056,8 @@ Bytecode::CodeGenerationErrorOr> ArrayExpression::ge auto dst = choose_dst(generator, preferred_dst); if (first_spread.index() != 0) { - auto reg = Bytecode::Register { args_start_reg.index() + static_cast(first_spread.index() - 1) }; - generator.emit_with_extra_operand_slots(2u, dst, AK::Array { Bytecode::Operand(args_start_reg), Bytecode::Operand(reg) }); + auto reg = Bytecode::Register { args_start_reg->index() + static_cast(first_spread.index() - 1) }; + generator.emit_with_extra_operand_slots(2u, dst, AK::Array { Bytecode::Operand(*args_start_reg), Bytecode::Operand(reg) }); } else { generator.emit(dst); } @@ -1070,26 +1076,26 @@ Bytecode::CodeGenerationErrorOr> ArrayExpression::ge return dst; } -Bytecode::CodeGenerationErrorOr> MemberExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> MemberExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); auto reference = TRY(generator.emit_load_from_reference(*this, preferred_dst)); return reference.loaded_value; } -Bytecode::CodeGenerationErrorOr> FunctionDeclaration::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> FunctionDeclaration::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { if (m_is_hoisted) { Bytecode::Generator::SourceLocationScope scope(generator, *this); auto index = generator.intern_identifier(name()); - auto value = Bytecode::Operand(generator.allocate_register()); + auto value = generator.allocate_register(); generator.emit(value, index, generator.next_environment_variable_cache()); generator.emit(index, value, generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Set, Bytecode::Op::EnvironmentMode::Var); } - return Optional {}; + return Optional {}; } -Bytecode::CodeGenerationErrorOr> FunctionExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional lhs_name, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> FunctionExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional lhs_name, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); bool has_name = !name().is_empty(); @@ -1113,17 +1119,17 @@ Bytecode::CodeGenerationErrorOr> FunctionExpression: return new_function; } -Bytecode::CodeGenerationErrorOr> FunctionExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> FunctionExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); return generate_bytecode_with_lhs_name(generator, {}, preferred_dst); } -static Bytecode::CodeGenerationErrorOr generate_object_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode initialization_mode, Bytecode::Operand const& object, bool create_variables) +static Bytecode::CodeGenerationErrorOr generate_object_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode initialization_mode, ScopedOperand const& object, bool create_variables) { generator.emit(object); - Vector excluded_property_names; + Vector excluded_property_names; auto has_rest = false; if (pattern.entries.size() > 0) has_rest = pattern.entries[pattern.entries.size() - 1].is_rest; @@ -1135,7 +1141,7 @@ static Bytecode::CodeGenerationErrorOr generate_object_binding_pattern_byt auto identifier = name.get>(); auto interned_identifier = generator.intern_identifier(identifier->string()); - auto copy = Bytecode::Operand(generator.allocate_register()); + auto copy = generator.allocate_register(); generator.emit_with_extra_operand_slots( excluded_property_names.size(), copy, object, excluded_property_names); if (create_variables) { @@ -1147,7 +1153,7 @@ static Bytecode::CodeGenerationErrorOr generate_object_binding_pattern_byt return {}; } if (alias.has>()) { - auto copy = Bytecode::Operand(generator.allocate_register()); + auto copy = generator.allocate_register(); generator.emit_with_extra_operand_slots( excluded_property_names.size(), copy, object, excluded_property_names); (void)TRY(generator.emit_store_to_reference(alias.get>(), object)); @@ -1156,7 +1162,7 @@ static Bytecode::CodeGenerationErrorOr generate_object_binding_pattern_byt VERIFY_NOT_REACHED(); } - auto value = Bytecode::Operand(generator.allocate_register()); + auto value = generator.allocate_register(); if (name.has>()) { auto const& identifier = name.get>()->string(); @@ -1169,7 +1175,7 @@ static Bytecode::CodeGenerationErrorOr generate_object_binding_pattern_byt auto property_name = TRY(expression->generate_bytecode(generator)).value(); if (has_rest) { - auto excluded_name = Bytecode::Operand(generator.allocate_register()); + auto excluded_name = generator.allocate_register(); excluded_property_names.append(excluded_name); generator.emit(excluded_name, property_name); } @@ -1187,7 +1193,7 @@ static Bytecode::CodeGenerationErrorOr generate_object_binding_pattern_byt Bytecode::Label { if_not_undefined_block }); generator.switch_to_basic_block(if_undefined_block); - Optional default_value; + Optional default_value; if (auto const* alias_identifier = alias.get_pointer>()) { default_value = TRY(generator.emit_named_evaluation_if_anonymous_function(*initializer, generator.intern_identifier((*alias_identifier)->string()))).value(); } else if (auto const* lhs = name.get_pointer>()) { @@ -1203,7 +1209,7 @@ static Bytecode::CodeGenerationErrorOr generate_object_binding_pattern_byt if (alias.has>()) { auto& binding_pattern = *alias.get>(); - auto nested_value = Bytecode::Operand(generator.allocate_register()); + auto nested_value = generator.allocate_register(); generator.emit(nested_value, value); TRY(generate_binding_pattern_bytecode(generator, binding_pattern, initialization_mode, nested_value, create_variables)); } else if (alias.has()) { @@ -1233,7 +1239,7 @@ static Bytecode::CodeGenerationErrorOr generate_object_binding_pattern_byt return {}; } -static Bytecode::CodeGenerationErrorOr generate_array_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode initialization_mode, Bytecode::Operand const& input_array, bool create_variables, [[maybe_unused]] Optional preferred_dst = {}) +static Bytecode::CodeGenerationErrorOr generate_array_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode initialization_mode, ScopedOperand const& input_array, bool create_variables, [[maybe_unused]] Optional preferred_dst = {}) { /* * Consider the following destructuring assignment: @@ -1255,14 +1261,14 @@ static Bytecode::CodeGenerationErrorOr generate_array_binding_pattern_byte * unnecessary. */ - auto is_iterator_exhausted = Bytecode::Operand(generator.allocate_register()); + auto is_iterator_exhausted = generator.allocate_register(); generator.emit(is_iterator_exhausted, generator.add_constant(Value(false))); - auto iterator = Bytecode::Operand(generator.allocate_register()); + auto iterator = generator.allocate_register(); generator.emit(iterator, input_array); bool first = true; - auto assign_value_to_alias = [&](auto& alias, Bytecode::Operand value) { + auto assign_value_to_alias = [&](auto& alias, ScopedOperand value) { return alias.visit( [&](Empty) -> Bytecode::CodeGenerationErrorOr { // This element is an elision @@ -1284,7 +1290,7 @@ static Bytecode::CodeGenerationErrorOr generate_array_binding_pattern_byte }); }; - auto temp_iterator_result = Bytecode::Operand(generator.allocate_register()); + auto temp_iterator_result = generator.allocate_register(); for (auto& [name, alias, initializer, is_rest] : pattern.entries) { VERIFY(name.has()); @@ -1292,7 +1298,7 @@ static Bytecode::CodeGenerationErrorOr generate_array_binding_pattern_byte if (is_rest) { VERIFY(!initializer); - auto value = Bytecode::Operand(generator.allocate_register()); + auto value = generator.allocate_register(); if (first) { // The iterator has not been called, and is thus known to be not exhausted @@ -1307,7 +1313,7 @@ static Bytecode::CodeGenerationErrorOr generate_array_binding_pattern_byte Bytecode::Label { if_exhausted_block }, Bytecode::Label { if_not_exhausted_block }); - value = Bytecode::Operand(generator.allocate_register()); + value = generator.allocate_register(); generator.switch_to_basic_block(if_exhausted_block); generator.emit(value); @@ -1350,7 +1356,7 @@ static Bytecode::CodeGenerationErrorOr generate_array_binding_pattern_byte generator.switch_to_basic_block(no_bail_block); // Get the next value in the iterator - auto value = Bytecode::Operand { generator.allocate_register() }; + auto value = generator.allocate_register(); generator.emit_iterator_value(value, temp_iterator_result); auto& create_binding_block = generator.make_block(); @@ -1374,7 +1380,7 @@ static Bytecode::CodeGenerationErrorOr generate_array_binding_pattern_byte generator.switch_to_basic_block(value_is_undefined_block); - Optional default_value; + Optional default_value; if (auto const* alias_identifier = alias.get_pointer>()) { default_value = TRY(generator.emit_named_evaluation_if_anonymous_function(*initializer, generator.intern_identifier((*alias_identifier)->string()))).value(); } else if (auto const* name_identifier = name.get_pointer>()) { @@ -1409,7 +1415,7 @@ static Bytecode::CodeGenerationErrorOr generate_array_binding_pattern_byte return {}; } -static Bytecode::CodeGenerationErrorOr generate_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode initialization_mode, Bytecode::Operand const& input_value, bool create_variables) +static Bytecode::CodeGenerationErrorOr generate_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode initialization_mode, ScopedOperand const& input_value, bool create_variables) { if (pattern.kind == BindingPattern::Kind::Object) return generate_object_binding_pattern_bytecode(generator, pattern, initialization_mode, input_value, create_variables); @@ -1417,7 +1423,7 @@ static Bytecode::CodeGenerationErrorOr generate_binding_pattern_bytecode(B return generate_array_binding_pattern_bytecode(generator, pattern, initialization_mode, input_value, create_variables); } -static Bytecode::CodeGenerationErrorOr assign_value_to_variable_declarator(Bytecode::Generator& generator, VariableDeclarator const& declarator, VariableDeclaration const& declaration, Bytecode::Operand value) +static Bytecode::CodeGenerationErrorOr assign_value_to_variable_declarator(Bytecode::Generator& generator, VariableDeclarator const& declarator, VariableDeclaration const& declaration, ScopedOperand value) { auto initialization_mode = declaration.is_lexical_declaration() ? Bytecode::Op::SetVariable::InitializationMode::Initialize : Bytecode::Op::SetVariable::InitializationMode::Set; @@ -1431,7 +1437,7 @@ static Bytecode::CodeGenerationErrorOr assign_value_to_variable_declarator }); } -Bytecode::CodeGenerationErrorOr> VariableDeclaration::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> VariableDeclaration::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); @@ -1439,17 +1445,17 @@ Bytecode::CodeGenerationErrorOr> VariableDeclaration // NOTE: `var` declarations can have duplicates, but duplicate `let` or `const` bindings are a syntax error. // Because of this, we can sink `let` and `const` directly into the preferred_dst if available. // This is not safe for `var` since the preferred_dst may be used in the initializer. - Optional init_dst; + Optional init_dst; if (declaration_kind() != DeclarationKind::Var) { if (auto const* identifier = declarator->target().get_pointer>()) { if ((*identifier)->is_local()) { - init_dst = Bytecode::Operand(Bytecode::Operand::Type::Local, (*identifier)->local_variable_index()); + init_dst = generator.local((*identifier)->local_variable_index()); } } } if (declarator->init()) { - auto value = TRY([&]() -> Bytecode::CodeGenerationErrorOr { + auto value = TRY([&]() -> Bytecode::CodeGenerationErrorOr { if (auto const* lhs = declarator->target().get_pointer>()) { return TRY(generator.emit_named_evaluation_if_anonymous_function(*declarator->init(), generator.intern_identifier((*lhs)->string()), init_dst)).value(); } else { @@ -1471,12 +1477,12 @@ Bytecode::CodeGenerationErrorOr> VariableDeclaration } // NOTE: VariableDeclaration doesn't return a completion value. - return Optional {}; + return Optional {}; } struct BaseAndValue { - Bytecode::Operand base; - Bytecode::Operand value; + ScopedOperand base; + ScopedOperand value; }; static Bytecode::CodeGenerationErrorOr get_base_and_value_from_member_expression(Bytecode::Generator& generator, MemberExpression const& member_expression) @@ -1487,7 +1493,7 @@ static Bytecode::CodeGenerationErrorOr get_base_and_value_from_mem // 2. Let actualThis be ? env.GetThisBinding(). auto this_value = generator.get_this(); - Optional computed_property; + Optional computed_property; if (member_expression.is_computed()) { // SuperProperty : super [ Expression ] @@ -1502,10 +1508,10 @@ static Bytecode::CodeGenerationErrorOr get_base_and_value_from_mem // 1. Let env be GetThisEnvironment(). // 2. Assert: env.HasSuperBinding() is true. // 3. Let baseValue be ? env.GetSuperBase(). - auto super_base = Bytecode::Operand(generator.allocate_register()); + auto super_base = generator.allocate_register(); generator.emit(super_base); - auto value = Bytecode::Operand { generator.allocate_register() }; + auto value = generator.allocate_register(); // 4. Return the Reference Record { [[Base]]: baseValue, [[ReferencedName]]: propertyKey, [[Strict]]: strict, [[ThisValue]]: actualThis }. if (computed_property.has_value()) { @@ -1522,7 +1528,7 @@ static Bytecode::CodeGenerationErrorOr get_base_and_value_from_mem } auto base = TRY(member_expression.object().generate_bytecode(generator)).value(); - auto value = Bytecode::Operand { generator.allocate_register() }; + auto value = generator.allocate_register(); if (member_expression.is_computed()) { auto property = TRY(member_expression.property().generate_bytecode(generator)).value(); generator.emit(value, base, property); @@ -1539,15 +1545,15 @@ static Bytecode::CodeGenerationErrorOr get_base_and_value_from_mem return BaseAndValue { base, value }; } -static Bytecode::CodeGenerationErrorOr generate_optional_chain(Bytecode::Generator& generator, OptionalChain const& optional_chain, Bytecode::Operand current_value, Bytecode::Operand current_base, [[maybe_unused]] Optional preferred_dst = {}); +static Bytecode::CodeGenerationErrorOr generate_optional_chain(Bytecode::Generator& generator, OptionalChain const& optional_chain, ScopedOperand current_value, ScopedOperand current_base, [[maybe_unused]] Optional preferred_dst = {}); -Bytecode::CodeGenerationErrorOr> CallExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> CallExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); Optional builtin; - Optional original_callee; + Optional original_callee; auto this_value = generator.add_constant(js_undefined()); if (is(this)) { @@ -1560,8 +1566,8 @@ Bytecode::CodeGenerationErrorOr> CallExpression::gen builtin = Bytecode::get_builtin(member_expression); } else if (is(*m_callee)) { auto& optional_chain = static_cast(*m_callee); - original_callee = Bytecode::Operand(generator.allocate_register()); - this_value = Bytecode::Operand(generator.allocate_register()); + original_callee = generator.allocate_register(); + this_value = generator.allocate_register(); TRY(generate_optional_chain(generator, optional_chain, *original_callee, this_value)); } else if (is(*m_callee)) { // If the original_callee is an identifier, we may need to extract a `this` value. @@ -1571,16 +1577,16 @@ Bytecode::CodeGenerationErrorOr> CallExpression::gen // a `with` binding, so we can skip this. auto& identifier = static_cast(*m_callee); if (identifier.is_local()) { - auto local = Bytecode::Operand(Bytecode::Operand::Type::Local, identifier.local_variable_index()); - if (!generator.is_local_initialized(local.index())) { + auto local = generator.local(identifier.local_variable_index()); + if (!generator.is_local_initialized(local.operand().index())) { generator.emit(local); } original_callee = local; } else if (identifier.is_global()) { original_callee = m_callee->generate_bytecode(generator).value(); } else { - original_callee = Bytecode::Operand(generator.allocate_register()); - this_value = Bytecode::Operand(generator.allocate_register()); + original_callee = generator.allocate_register(); + this_value = generator.allocate_register(); generator.emit( *original_callee, this_value, @@ -1593,7 +1599,7 @@ Bytecode::CodeGenerationErrorOr> CallExpression::gen } // NOTE: We copy the callee to a new register to avoid overwriting it while evaluating arguments. - auto callee = Bytecode::Operand(generator.allocate_register()); + auto callee = generator.allocate_register(); generator.emit(callee, *original_callee); Bytecode::Op::CallType call_type; @@ -1616,10 +1622,10 @@ Bytecode::CodeGenerationErrorOr> CallExpression::gen auto arguments = TRY(arguments_to_array_for_call(generator, this->arguments())).value(); generator.emit(call_type, dst, callee, this_value, arguments, expression_string_index); } else { - Vector argument_operands; + Vector argument_operands; for (auto const& argument : arguments()) { auto argument_value = TRY(argument.value->generate_bytecode(generator)).value(); - auto temporary = Bytecode::Operand(generator.allocate_register()); + auto temporary = generator.allocate_register(); generator.emit(temporary, argument_value); argument_operands.append(temporary); } @@ -1637,21 +1643,21 @@ Bytecode::CodeGenerationErrorOr> CallExpression::gen return dst; } -static Bytecode::Operand generate_await( +static ScopedOperand generate_await( Bytecode::Generator& generator, - Bytecode::Operand argument, - Bytecode::Operand received_completion, - Bytecode::Operand received_completion_type, - Bytecode::Operand received_completion_value, + ScopedOperand argument, + ScopedOperand received_completion, + ScopedOperand received_completion_type, + ScopedOperand received_completion_value, Bytecode::IdentifierTableIndex type_identifier, Bytecode::IdentifierTableIndex value_identifier); // https://tc39.es/ecma262/#sec-return-statement-runtime-semantics-evaluation -Bytecode::CodeGenerationErrorOr> ReturnStatement::generate_bytecode(Bytecode::Generator& generator, Optional) const +Bytecode::CodeGenerationErrorOr> ReturnStatement::generate_bytecode(Bytecode::Generator& generator, Optional) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - Optional return_value; + Optional return_value; if (m_argument) { // ReturnStatement : return Expression ; @@ -1666,9 +1672,9 @@ Bytecode::CodeGenerationErrorOr> ReturnStatement::ge // See: https://tc39.es/ecma262/#sec-asyncblockstart // c. Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done. if (generator.is_in_async_function()) { - auto received_completion = Bytecode::Operand(generator.allocate_register()); - auto received_completion_type = Bytecode::Operand(generator.allocate_register()); - auto received_completion_value = Bytecode::Operand(generator.allocate_register()); + auto received_completion = generator.allocate_register(); + auto received_completion_type = generator.allocate_register(); + auto received_completion_value = generator.allocate_register(); auto type_identifier = generator.intern_identifier("type"); auto value_identifier = generator.intern_identifier("value"); @@ -1687,7 +1693,7 @@ Bytecode::CodeGenerationErrorOr> ReturnStatement::ge generator.emit(nullptr, *return_value); } else { generator.perform_needed_unwinds(); - generator.emit(return_value); + generator.emit(return_value.has_value() ? return_value->operand() : Optional {}); } return return_value; @@ -1695,9 +1701,9 @@ Bytecode::CodeGenerationErrorOr> ReturnStatement::ge static void get_received_completion_type_and_value( Bytecode::Generator& generator, - Bytecode::Operand received_completion, - Bytecode::Operand received_completion_type, - Bytecode::Operand received_completion_value, + ScopedOperand received_completion, + ScopedOperand received_completion_type, + ScopedOperand received_completion_value, Bytecode::IdentifierTableIndex type_identifier, Bytecode::IdentifierTableIndex value_identifier) { @@ -1712,10 +1718,10 @@ enum class AwaitBeforeYield { static void generate_yield(Bytecode::Generator& generator, Bytecode::Label continuation_label, - Bytecode::Operand argument, - Bytecode::Operand received_completion, - Bytecode::Operand received_completion_type, - Bytecode::Operand received_completion_value, + ScopedOperand argument, + ScopedOperand received_completion, + ScopedOperand received_completion_type, + ScopedOperand received_completion_value, Bytecode::IdentifierTableIndex type_identifier, Bytecode::IdentifierTableIndex value_identifier, AwaitBeforeYield await_before_yield) @@ -1732,13 +1738,13 @@ static void generate_yield(Bytecode::Generator& generator, generator.emit(Bytecode::Label { unwrap_yield_resumption_block }, argument); generator.switch_to_basic_block(unwrap_yield_resumption_block); - generator.emit(received_completion, Bytecode::Operand(Bytecode::Register(0))); + generator.emit(received_completion, generator.accumulator()); get_received_completion_type_and_value(generator, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); // 27.6.3.7 AsyncGeneratorUnwrapYieldResumption ( resumptionValue ), https://tc39.es/ecma262/#sec-asyncgeneratorunwrapyieldresumption // 1. If resumptionValue.[[Type]] is not return, return ? resumptionValue. auto& resumption_value_type_is_return_block = generator.make_block(); - auto resumption_value_type_is_not_return_result = Bytecode::Operand(generator.allocate_register()); + auto resumption_value_type_is_not_return_result = generator.allocate_register(); generator.emit( resumption_value_type_is_not_return_result, received_completion_type, @@ -1755,7 +1761,7 @@ static void generate_yield(Bytecode::Generator& generator, // 3. If awaited.[[Type]] is throw, return ? awaited. auto& awaited_type_is_normal_block = generator.make_block(); - auto awaited_type_is_throw_result = Bytecode::Operand(generator.allocate_register()); + auto awaited_type_is_throw_result = generator.allocate_register(); generator.emit( awaited_type_is_throw_result, received_completion_type, @@ -1778,20 +1784,20 @@ static void generate_yield(Bytecode::Generator& generator, generator.emit(continuation_label); } -Bytecode::CodeGenerationErrorOr> YieldExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> YieldExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { // Note: We need to catch any scheduled exceptions and reschedule them on re-entry // as the act of yielding would otherwise clear them out // This only applies when we are in a finalizer bool is_in_finalizer = generator.is_in_finalizer(); - Optional saved_exception; + Optional saved_exception; Bytecode::Generator::SourceLocationScope scope(generator, *this); VERIFY(generator.is_in_generator_function()); - auto received_completion = Bytecode::Operand(generator.allocate_register()); - auto received_completion_type = Bytecode::Operand(generator.allocate_register()); - auto received_completion_value = Bytecode::Operand(generator.allocate_register()); + auto received_completion = generator.allocate_register(); + auto received_completion_type = generator.allocate_register(); + auto received_completion_value = generator.allocate_register(); auto type_identifier = generator.intern_identifier("type"); auto value_identifier = generator.intern_identifier("value"); @@ -1807,16 +1813,16 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge auto value = TRY(m_argument->generate_bytecode(generator)).value(); // 4. Let iteratorRecord be ? GetIterator(value, generatorKind). - auto iterator_record = Bytecode::Operand(generator.allocate_register()); + auto iterator_record = generator.allocate_register(); auto iterator_hint = generator.is_in_async_generator_function() ? IteratorHint::Async : IteratorHint::Sync; generator.emit(iterator_record, value, iterator_hint); // 5. Let iterator be iteratorRecord.[[Iterator]]. - auto iterator = Bytecode::Operand(generator.allocate_register()); + auto iterator = generator.allocate_register(); generator.emit(iterator, iterator_record); // Cache iteratorRecord.[[NextMethod]] for use in step 7.a.i. - auto next_method = Bytecode::Operand(generator.allocate_register()); + auto next_method = generator.allocate_register(); generator.emit(next_method, iterator_record); // 6. Let received be NormalCompletion(undefined). @@ -1837,7 +1843,7 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge auto& type_is_normal_block = generator.make_block(); auto& is_type_throw_block = generator.make_block(); - auto received_completion_type_register_is_normal = Bytecode::Operand(generator.allocate_register()); + auto received_completion_type_register_is_normal = generator.allocate_register(); generator.emit( received_completion_type_register_is_normal, received_completion_type, @@ -1850,9 +1856,9 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge generator.switch_to_basic_block(type_is_normal_block); // i. Let innerResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]], « received.[[Value]] »). - auto array = Bytecode::Operand(generator.allocate_register()); - generator.emit_with_extra_operand_slots(2u, array, AK::Array { received_completion_value, received_completion_value }); - auto inner_result = Bytecode::Operand(generator.allocate_register()); + auto array = generator.allocate_register(); + generator.emit_with_extra_operand_slots(2u, array, AK::Array { received_completion_value.operand(), received_completion_value.operand() }); + auto inner_result = generator.allocate_register(); generator.emit(Bytecode::Op::CallType::Call, inner_result, next_method, iterator, array); // ii. If generatorKind is async, set innerResult to ? Await(innerResult). @@ -1865,7 +1871,7 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge generator.emit(inner_result); // iv. Let done be ? IteratorComplete(innerResult). - auto done = Bytecode::Operand(generator.allocate_register()); + auto done = generator.allocate_register(); generator.emit_iterator_complete(done, inner_result); // v. If done is true, then @@ -1879,7 +1885,7 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge generator.switch_to_basic_block(type_is_normal_done_block); // 1. Return ? IteratorValue(innerResult). - auto return_value = Bytecode::Operand(generator.allocate_register()); + auto return_value = generator.allocate_register(); generator.emit_iterator_value(return_value, inner_result); generator.emit(Bytecode::Label { loop_end_block }); @@ -1891,7 +1897,7 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge { // FIXME: Yield currently only accepts a Value, not an object conforming to the IteratorResult interface, so we have to do an observable lookup of `value` here. // This only matters for non-async generators. - auto current_value = Bytecode::Operand(generator.allocate_register()); + auto current_value = generator.allocate_register(); generator.emit_iterator_value(current_value, inner_result); if (is_in_finalizer) { @@ -1915,10 +1921,10 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge auto& type_is_throw_block = generator.make_block(); auto& type_is_return_block = generator.make_block(); - auto received_completion_type_register_is_throw = Bytecode::Operand(generator.allocate_register()); + auto received_completion_type_register_is_throw = generator.allocate_register(); generator.emit( received_completion_type_register_is_throw, - Bytecode::Operand(received_completion_type), + received_completion_type, generator.add_constant(Value(to_underlying(Completion::Type::Throw)))); generator.emit( received_completion_type_register_is_throw, @@ -1928,7 +1934,7 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge generator.switch_to_basic_block(type_is_throw_block); // i. Let throw be ? GetMethod(iterator, "throw"). - auto throw_method = Bytecode::Operand(generator.allocate_register()); + auto throw_method = generator.allocate_register(); generator.emit(throw_method, iterator, generator.intern_identifier("throw")); // ii. If throw is not undefined, then @@ -1942,8 +1948,8 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge generator.switch_to_basic_block(throw_method_is_defined_block); // 1. Let innerResult be ? Call(throw, iterator, « received.[[Value]] »). - auto received_value_array = Bytecode::Operand(generator.allocate_register()); - generator.emit_with_extra_operand_slots(2u, received_value_array, AK::Array { received_completion_value, received_completion_value }); + auto received_value_array = generator.allocate_register(); + generator.emit_with_extra_operand_slots(2u, received_value_array, AK::Array { received_completion_value.operand(), received_completion_value.operand() }); generator.emit(Bytecode::Op::CallType::Call, inner_result, throw_method, iterator, received_value_array); // 2. If generatorKind is async, set innerResult to ? Await(innerResult). @@ -1981,7 +1987,7 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge // FIXME: Yield currently only accepts a Value, not an object conforming to the IteratorResult interface, so we have to do an observable lookup of `value` here. // This only matters for non-async generators. - auto yield_value = Bytecode::Operand(generator.allocate_register()); + auto yield_value = generator.allocate_register(); generator.emit_iterator_value(yield_value, inner_result); generate_yield(generator, Bytecode::Label { continuation_block }, yield_value, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier, AwaitBeforeYield::No); } @@ -2003,7 +2009,7 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge // 5. NOTE: The next step throws a TypeError to indicate that there was a yield* protocol violation: iterator does not have a throw method. // 6. Throw a TypeError exception. - auto exception = Bytecode::Operand(generator.allocate_register()); + auto exception = generator.allocate_register(); generator.emit(exception, generator.intern_string(ErrorType::YieldFromIteratorMissingThrowMethod.message())); generator.perform_needed_unwinds(); generator.emit(exception); @@ -2013,7 +2019,7 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge generator.switch_to_basic_block(type_is_return_block); // ii. Let return be ? GetMethod(iterator, "return"). - auto return_method = Bytecode::Operand(generator.allocate_register()); + auto return_method = generator.allocate_register(); generator.emit(return_method, iterator, generator.intern_identifier("return")); // iii. If return is undefined, then @@ -2039,9 +2045,9 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge generator.switch_to_basic_block(return_is_defined_block); // iv. Let innerReturnResult be ? Call(return, iterator, « received.[[Value]] »). - auto call_array = Bytecode::Operand(generator.allocate_register()); - generator.emit_with_extra_operand_slots(2, call_array, AK::Array { received_completion_value, received_completion_value }); - auto inner_return_result = Bytecode::Operand(generator.allocate_register()); + auto call_array = generator.allocate_register(); + generator.emit_with_extra_operand_slots(2, call_array, AK::Array { received_completion_value.operand(), received_completion_value.operand() }); + auto inner_return_result = generator.allocate_register(); generator.emit(Bytecode::Op::CallType::Call, inner_return_result, return_method, iterator, call_array); // v. If generatorKind is async, set innerReturnResult to ? Await(innerReturnResult). @@ -2067,7 +2073,7 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge generator.switch_to_basic_block(type_is_return_done_block); // 1. Let value be ? IteratorValue(innerReturnResult). - auto inner_return_result_value = Bytecode::Operand(generator.allocate_register()); + auto inner_return_result_value = generator.allocate_register(); generator.emit_iterator_value(inner_return_result_value, inner_return_result); // 2. Return Completion Record { [[Type]]: return, [[Value]]: value, [[Target]]: empty }. @@ -2080,7 +2086,7 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge // x. Else, set received to Completion(GeneratorYield(innerReturnResult)). // FIXME: Yield currently only accepts a Value, not an object conforming to the IteratorResult interface, so we have to do an observable lookup of `value` here. // This only matters for non-async generators. - auto received = Bytecode::Operand(generator.allocate_register()); + auto received = generator.allocate_register(); generator.emit_iterator_value(received, inner_return_result); generate_yield(generator, Bytecode::Label { continuation_block }, received, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier, AwaitBeforeYield::No); @@ -2090,7 +2096,7 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge if (is_in_finalizer) generator.emit(Bytecode::Operand(Bytecode::Register::exception()), Bytecode::Operand(*saved_exception)); - generator.emit(received_completion, Bytecode::Operand(Bytecode::Register(0))); + generator.emit(received_completion, generator.accumulator()); get_received_completion_type_and_value(generator, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); generator.emit(Bytecode::Label { loop_block }); @@ -2098,7 +2104,7 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge return return_value; } - Optional argument; + Optional argument; if (m_argument) argument = TRY(m_argument->generate_bytecode(generator)).value(); else @@ -2117,13 +2123,14 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge if (is_in_finalizer) generator.emit(Bytecode::Operand(Bytecode::Register::exception()), Bytecode::Operand(*saved_exception)); - generator.emit(received_completion, Bytecode::Operand(Bytecode::Register(0))); + generator.emit(received_completion, generator.accumulator()); + get_received_completion_type_and_value(generator, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); auto& normal_completion_continuation_block = generator.make_block(); auto& throw_completion_continuation_block = generator.make_block(); - auto received_completion_type_is_normal = Bytecode::Operand(generator.allocate_register()); + auto received_completion_type_is_normal = generator.allocate_register(); generator.emit( received_completion_type_is_normal, received_completion_type, @@ -2137,7 +2144,7 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge auto& return_value_block = generator.make_block(); generator.switch_to_basic_block(throw_completion_continuation_block); - auto received_completion_type_is_throw = Bytecode::Operand(generator.allocate_register()); + auto received_completion_type_is_throw = generator.allocate_register(); generator.emit( received_completion_type_is_throw, received_completion_type, @@ -2161,7 +2168,7 @@ Bytecode::CodeGenerationErrorOr> YieldExpression::ge return received_completion_value; } -Bytecode::CodeGenerationErrorOr> IfStatement::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> IfStatement::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); // test @@ -2195,7 +2202,7 @@ Bytecode::CodeGenerationErrorOr> IfStatement::genera generator.switch_to_basic_block(false_block); - Optional alternate; + Optional alternate; if (m_alternate) { alternate = TRY(m_alternate->generate_bytecode(generator, dst)); } @@ -2210,24 +2217,24 @@ Bytecode::CodeGenerationErrorOr> IfStatement::genera return dst; } -Bytecode::CodeGenerationErrorOr> ContinueStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ContinueStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); if (!m_target_label.has_value()) { generator.generate_continue(); - return Optional {}; + return Optional {}; } generator.generate_continue(m_target_label.value()); - return Optional {}; + return Optional {}; } -Bytecode::CodeGenerationErrorOr> DebuggerStatement::generate_bytecode(Bytecode::Generator&, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> DebuggerStatement::generate_bytecode(Bytecode::Generator&, [[maybe_unused]] Optional preferred_dst) const { - return Optional {}; + return Optional {}; } -Bytecode::CodeGenerationErrorOr> ConditionalExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ConditionalExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); // test @@ -2265,10 +2272,10 @@ Bytecode::CodeGenerationErrorOr> ConditionalExpressi return dst; } -Bytecode::CodeGenerationErrorOr> SequenceExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> SequenceExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - Optional last_value; + Optional last_value; for (auto& expression : m_expressions) { last_value = TRY(expression->generate_bytecode(generator)); } @@ -2276,7 +2283,7 @@ Bytecode::CodeGenerationErrorOr> SequenceExpression: return last_value; } -Bytecode::CodeGenerationErrorOr> TemplateLiteral::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> TemplateLiteral::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); @@ -2294,7 +2301,7 @@ Bytecode::CodeGenerationErrorOr> TemplateLiteral::ge return dst; } -Bytecode::CodeGenerationErrorOr> TaggedTemplateLiteral::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> TaggedTemplateLiteral::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); auto tag = TRY(m_tag->generate_bytecode(generator)).value(); @@ -2307,12 +2314,12 @@ Bytecode::CodeGenerationErrorOr> TaggedTemplateLiter // * cache this somehow // * add a raw object accessor // * freeze array and raw member - Vector string_regs; + Vector string_regs; auto& expressions = m_template_literal->expressions(); for (size_t i = 0; i < expressions.size(); ++i) { if (i % 2 != 0) continue; - string_regs.append(Bytecode::Operand(generator.allocate_register())); + string_regs.append(generator.allocate_sequential_register()); } size_t reg_index = 0; @@ -2322,7 +2329,7 @@ Bytecode::CodeGenerationErrorOr> TaggedTemplateLiter // NOTE: If the string contains invalid escapes we get a null expression here, // which we then convert to the expected `undefined` TV. See // 12.9.6.1 Static Semantics: TV, https://tc39.es/ecma262/#sec-static-semantics-tv - auto string_reg = Bytecode::Operand(string_regs[reg_index++]); + auto string_reg = string_regs[reg_index++]; if (is(expressions[i])) { generator.emit(string_reg, generator.add_constant(js_undefined())); } else { @@ -2331,17 +2338,17 @@ Bytecode::CodeGenerationErrorOr> TaggedTemplateLiter } } - auto strings_array = Bytecode::Operand(generator.allocate_register()); + auto strings_array = generator.allocate_sequential_register(); if (string_regs.is_empty()) { generator.emit(strings_array); } else { - generator.emit_with_extra_operand_slots(2u, strings_array, AK::Array { string_regs.first(), string_regs.last() }); + generator.emit_with_extra_operand_slots(2u, strings_array, AK::Array { string_regs.first().operand(), string_regs.last().operand() }); } - Vector argument_regs; + Vector argument_regs; argument_regs.append(strings_array); for (size_t i = 1; i < expressions.size(); i += 2) - argument_regs.append(Bytecode::Operand(generator.allocate_register())); + argument_regs.append(generator.allocate_sequential_register()); for (size_t i = 1; i < expressions.size(); i += 2) { auto string_reg = argument_regs[1 + i / 2]; @@ -2349,9 +2356,9 @@ Bytecode::CodeGenerationErrorOr> TaggedTemplateLiter generator.emit(string_reg, string); } - Vector raw_string_regs; + Vector raw_string_regs; for ([[maybe_unused]] auto& raw_string : m_template_literal->raw_strings()) - string_regs.append(Bytecode::Operand(generator.allocate_register())); + string_regs.append(generator.allocate_sequential_register()); reg_index = 0; for (auto& raw_string : m_template_literal->raw_strings()) { @@ -2361,18 +2368,18 @@ Bytecode::CodeGenerationErrorOr> TaggedTemplateLiter raw_string_regs.append(raw_string_reg); } - auto raw_strings_array = Bytecode::Operand(generator.allocate_register()); + auto raw_strings_array = generator.allocate_register(); if (raw_string_regs.is_empty()) { generator.emit(raw_strings_array); } else { - generator.emit_with_extra_operand_slots(2u, raw_strings_array, AK::Array { raw_string_regs.first(), raw_string_regs.last() }); + generator.emit_with_extra_operand_slots(2u, raw_strings_array, AK::Array { raw_string_regs.first().operand(), raw_string_regs.last().operand() }); } generator.emit(strings_array, generator.intern_identifier("raw"), raw_strings_array, Bytecode::Op::PropertyKind::KeyValue, generator.next_property_lookup_cache()); - auto arguments = Bytecode::Operand(generator.allocate_register()); + auto arguments = generator.allocate_register(); if (!argument_regs.is_empty()) - generator.emit_with_extra_operand_slots(2, arguments, AK::Array { argument_regs.first(), argument_regs.last() }); + generator.emit_with_extra_operand_slots(2, arguments, AK::Array { argument_regs.first().operand(), argument_regs.last().operand() }); else generator.emit(arguments); @@ -2381,25 +2388,25 @@ Bytecode::CodeGenerationErrorOr> TaggedTemplateLiter return dst; } -Bytecode::CodeGenerationErrorOr> UpdateExpression::generate_bytecode(Bytecode::Generator& generator, Optional) const +Bytecode::CodeGenerationErrorOr> UpdateExpression::generate_bytecode(Bytecode::Generator& generator, Optional) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); auto reference = TRY(generator.emit_load_from_reference(*m_argument)); - Optional previous_value_for_postfix; + Optional previous_value_for_postfix; if (m_op == UpdateOp::Increment) { if (m_prefixed) { generator.emit(*reference.loaded_value); } else { - previous_value_for_postfix = Bytecode::Operand(generator.allocate_register()); + previous_value_for_postfix = generator.allocate_register(); generator.emit(*previous_value_for_postfix, *reference.loaded_value); } } else { if (m_prefixed) { generator.emit(*reference.loaded_value); } else { - previous_value_for_postfix = Bytecode::Operand(generator.allocate_register()); + previous_value_for_postfix = generator.allocate_register(); generator.emit(*previous_value_for_postfix, *reference.loaded_value); } } @@ -2414,16 +2421,16 @@ Bytecode::CodeGenerationErrorOr> UpdateExpression::g return *reference.loaded_value; } -Bytecode::CodeGenerationErrorOr> ThrowStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ThrowStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); auto argument = TRY(m_argument->generate_bytecode(generator)).value(); generator.perform_needed_unwinds(); generator.emit(argument); - return Optional {}; + return Optional {}; } -Bytecode::CodeGenerationErrorOr> BreakStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> BreakStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); // FIXME: Handle finally blocks in a graceful manner @@ -2431,14 +2438,14 @@ Bytecode::CodeGenerationErrorOr> BreakStatement::gen // execution at the designated block if (!m_target_label.has_value()) { generator.generate_break(); - return Optional {}; + return Optional {}; } generator.generate_break(m_target_label.value()); - return Optional {}; + return Optional {}; } -Bytecode::CodeGenerationErrorOr> TryStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> TryStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); auto& saved_block = generator.current_block(); @@ -2449,7 +2456,7 @@ Bytecode::CodeGenerationErrorOr> TryStatement::gener Bytecode::BasicBlock* next_block { nullptr }; - Optional dst; + Optional dst; if (m_finalizer) { // FIXME: See notes in Op.h->ScheduleJump @@ -2475,7 +2482,7 @@ Bytecode::CodeGenerationErrorOr> TryStatement::gener auto& handler_block = generator.make_block(); generator.switch_to_basic_block(handler_block); - auto caught_value = Bytecode::Operand { generator.allocate_register() }; + auto caught_value = generator.allocate_register(); generator.emit(caught_value); if (!m_finalizer) { @@ -2506,7 +2513,7 @@ Bytecode::CodeGenerationErrorOr> TryStatement::gener auto handler_result = TRY(m_handler->body().generate_bytecode(generator)); if (handler_result.has_value() && !generator.is_current_block_terminated()) { - dst = Bytecode::Operand(generator.allocate_register()); + dst = generator.allocate_register(); generator.emit(*dst, *handler_result); } handler_target = Bytecode::Label { handler_block }; @@ -2550,7 +2557,7 @@ Bytecode::CodeGenerationErrorOr> TryStatement::gener auto block_result = TRY(m_block->generate_bytecode(generator)); if (!generator.is_current_block_terminated()) { if (block_result.has_value()) { - dst = Bytecode::Operand(generator.allocate_register()); + dst = generator.allocate_register(); generator.emit(*dst, *block_result); } @@ -2576,15 +2583,19 @@ Bytecode::CodeGenerationErrorOr> TryStatement::gener return dst; } -Bytecode::CodeGenerationErrorOr> SwitchStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> SwitchStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); return generate_labelled_evaluation(generator, {}); } -Bytecode::CodeGenerationErrorOr> SwitchStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> SwitchStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); + + auto dst = generator.allocate_register(); + generator.emit(dst, generator.add_constant(js_undefined())); + auto discriminant = TRY(m_discriminant->generate_bytecode(generator)).value(); Vector case_blocks; Bytecode::BasicBlock* entry_block_for_default { nullptr }; @@ -2602,7 +2613,7 @@ Bytecode::CodeGenerationErrorOr> SwitchStatement::ge if (switch_case->test()) { generator.switch_to_basic_block(*next_test_block); auto test_value = TRY(switch_case->test()->generate_bytecode(generator)).value(); - auto result = Bytecode::Operand(generator.allocate_register()); + auto result = generator.allocate_register(); generator.emit(result, test_value, discriminant); next_test_block = &generator.make_block(); generator.emit( @@ -2630,7 +2641,6 @@ Bytecode::CodeGenerationErrorOr> SwitchStatement::ge } auto current_block = case_blocks.begin(); generator.begin_breakable_scope(Bytecode::Label { end_block }, label_set); - auto dst = Bytecode::Operand(generator.allocate_register()); for (auto& switch_case : m_cases) { generator.switch_to_basic_block(*current_block); for (auto& statement : switch_case->children()) { @@ -2663,23 +2673,23 @@ Bytecode::CodeGenerationErrorOr> SwitchStatement::ge return dst; } -Bytecode::CodeGenerationErrorOr> SuperExpression::generate_bytecode(Bytecode::Generator&, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> SuperExpression::generate_bytecode(Bytecode::Generator&, [[maybe_unused]] Optional preferred_dst) const { // The semantics for SuperExpression are handled in CallExpression and SuperCall. VERIFY_NOT_REACHED(); } -Bytecode::CodeGenerationErrorOr> ClassDeclaration::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ClassDeclaration::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); auto value = TRY(m_class_expression->generate_bytecode(generator)).value(); generator.emit_set_variable(*m_class_expression.ptr()->m_name, value, Bytecode::Op::SetVariable::InitializationMode::Initialize); // NOTE: ClassDeclaration does not produce a value. - return Optional {}; + return Optional {}; } // 15.7.14 Runtime Semantics: ClassDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation -Bytecode::CodeGenerationErrorOr> ClassExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional lhs_name, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ClassExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional lhs_name, Optional preferred_dst) const { // NOTE: Step 2 is not a part of NewClass instruction because it is assumed to be done before super class expression evaluation generator.emit(); @@ -2690,41 +2700,41 @@ Bytecode::CodeGenerationErrorOr> ClassExpression::ge generator.emit(interned_index, Bytecode::Op::EnvironmentMode::Lexical, true); } - Optional super_class; + Optional super_class; if (m_super_class) super_class = TRY(m_super_class->generate_bytecode(generator)).value(); auto dst = choose_dst(generator, preferred_dst); - generator.emit(dst, super_class, *this, lhs_name); + generator.emit(dst, super_class.has_value() ? super_class->operand() : Optional {}, *this, lhs_name); return dst; } -Bytecode::CodeGenerationErrorOr> ClassExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ClassExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); return generate_bytecode_with_lhs_name(generator, {}, preferred_dst); } -Bytecode::CodeGenerationErrorOr> SpreadExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> SpreadExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { // NOTE: All users of this should handle the behaviour of this on their own, // assuming it returns an Array-like object return m_target->generate_bytecode(generator); } -Bytecode::CodeGenerationErrorOr> ThisExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ThisExpression::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); return generator.get_this(preferred_dst); } -static Bytecode::Operand generate_await( +static ScopedOperand generate_await( Bytecode::Generator& generator, - Bytecode::Operand argument, - Bytecode::Operand received_completion, - Bytecode::Operand received_completion_type, - Bytecode::Operand received_completion_value, + ScopedOperand argument, + ScopedOperand received_completion, + ScopedOperand received_completion_type, + ScopedOperand received_completion_value, Bytecode::IdentifierTableIndex type_identifier, Bytecode::IdentifierTableIndex value_identifier) { @@ -2737,13 +2747,13 @@ static Bytecode::Operand generate_await( // FIXME: It's really magical that we can just assume that the completion value is in register 0. // It ends up there because we "return" from the Await instruction above via the synthetic // generator function that actually drives async execution. - generator.emit(received_completion, Bytecode::Operand(Bytecode::Register(0))); + generator.emit(received_completion, generator.accumulator()); get_received_completion_type_and_value(generator, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); auto& normal_completion_continuation_block = generator.make_block(); auto& throw_value_block = generator.make_block(); - auto received_completion_type_is_normal = Bytecode::Operand(generator.allocate_register()); + auto received_completion_type_is_normal = generator.allocate_register(); generator.emit( received_completion_type_is_normal, received_completion_type, @@ -2763,16 +2773,16 @@ static Bytecode::Operand generate_await( return received_completion_value; } -Bytecode::CodeGenerationErrorOr> AwaitExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> AwaitExpression::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); auto argument = TRY(m_argument->generate_bytecode(generator)).value(); - auto received_completion = Bytecode::Operand(generator.allocate_register()); - auto received_completion_type = Bytecode::Operand(generator.allocate_register()); - auto received_completion_value = Bytecode::Operand(generator.allocate_register()); + auto received_completion = generator.allocate_register(); + auto received_completion_type = generator.allocate_register(); + auto received_completion_value = generator.allocate_register(); - generator.emit(received_completion, Bytecode::Operand(Bytecode::Register(0))); + generator.emit(received_completion, generator.accumulator()); auto type_identifier = generator.intern_identifier("type"); auto value_identifier = generator.intern_identifier("value"); @@ -2780,7 +2790,7 @@ Bytecode::CodeGenerationErrorOr> AwaitExpression::ge return generate_await(generator, argument, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); } -Bytecode::CodeGenerationErrorOr> WithStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> WithStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); auto object = TRY(m_object->generate_bytecode(generator)).value(); @@ -2816,7 +2826,7 @@ enum class IterationKind { struct ForInOfHeadEvaluationResult { bool is_destructuring { false }; LHSKind lhs_kind { LHSKind::Assignment }; - Optional iterator; + Optional iterator; }; static Bytecode::CodeGenerationErrorOr for_in_of_head_evaluation(Bytecode::Generator& generator, IterationKind iteration_kind, Variant, NonnullRefPtr> const& lhs, NonnullRefPtr const& rhs) { @@ -2889,7 +2899,7 @@ static Bytecode::CodeGenerationErrorOr for_in_of_he // 5. Let exprValue be ? GetValue(exprRef). // NOTE: No need to store this anywhere. - auto iterator = Bytecode::Operand(generator.allocate_register()); + auto iterator = generator.allocate_register(); // 6. If iterationKind is enumerate, then if (iteration_kind == IterationKind::Enumerate) { @@ -2929,7 +2939,7 @@ static Bytecode::CodeGenerationErrorOr for_in_of_he } // 14.7.5.7 ForIn/OfBodyEvaluation ( lhs, stmt, iteratorRecord, iterationKind, lhsKind, labelSet [ , iteratorKind ] ), https://tc39.es/ecma262/#sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset -static Bytecode::CodeGenerationErrorOr> for_in_of_body_evaluation(Bytecode::Generator& generator, ASTNode const& node, Variant, NonnullRefPtr> const& lhs, ASTNode const& body, ForInOfHeadEvaluationResult const& head_result, Vector const& label_set, Bytecode::BasicBlock& loop_end, Bytecode::BasicBlock& loop_update, IteratorHint iterator_kind = IteratorHint::Sync, [[maybe_unused]] Optional preferred_dst = {}) +static Bytecode::CodeGenerationErrorOr> for_in_of_body_evaluation(Bytecode::Generator& generator, ASTNode const& node, Variant, NonnullRefPtr> const& lhs, ASTNode const& body, ForInOfHeadEvaluationResult const& head_result, Vector const& label_set, Bytecode::BasicBlock& loop_end, Bytecode::BasicBlock& loop_update, IteratorHint iterator_kind = IteratorHint::Sync, [[maybe_unused]] Optional preferred_dst = {}) { // 1. If iteratorKind is not present, set iteratorKind to sync. @@ -2937,7 +2947,7 @@ static Bytecode::CodeGenerationErrorOr> for_in_of_bo bool has_lexical_binding = false; // 3. Let V be undefined. - auto completion_value = Bytecode::Operand(generator.allocate_register()); + auto completion_value = generator.allocate_register(); generator.emit(completion_value, generator.add_constant(js_undefined())); // 4. Let destructuring be IsDestructuring of lhs. @@ -2959,19 +2969,19 @@ static Bytecode::CodeGenerationErrorOr> for_in_of_bo generator.begin_continuable_scope(Bytecode::Label { loop_update }, label_set); // a. Let nextResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]). - auto next_result = Bytecode::Operand(generator.allocate_register()); + auto next_result = generator.allocate_register(); generator.emit(next_result, *head_result.iterator); // b. If iteratorKind is async, set nextResult to ? Await(nextResult). if (iterator_kind == IteratorHint::Async) { - auto received_completion = Bytecode::Operand(generator.allocate_register()); - auto received_completion_type = Bytecode::Operand(generator.allocate_register()); - auto received_completion_value = Bytecode::Operand(generator.allocate_register()); + auto received_completion = generator.allocate_register(); + auto received_completion_type = generator.allocate_register(); + auto received_completion_value = generator.allocate_register(); auto type_identifier = generator.intern_identifier("type"); auto value_identifier = generator.intern_identifier("value"); - generator.emit(received_completion, Bytecode::Operand(Bytecode::Register(0))); + generator.emit(received_completion, generator.accumulator()); auto new_result = generate_await(generator, next_result, received_completion, received_completion_type, received_completion_value, type_identifier, value_identifier); generator.emit(next_result, new_result); } @@ -2980,7 +2990,7 @@ static Bytecode::CodeGenerationErrorOr> for_in_of_bo generator.emit(next_result); // d. Let done be ? IteratorComplete(nextResult). - auto done = Bytecode::Operand(generator.allocate_register()); + auto done = generator.allocate_register(); generator.emit_iterator_complete(done, next_result); // e. If done is true, return V. @@ -2992,7 +3002,7 @@ static Bytecode::CodeGenerationErrorOr> for_in_of_bo generator.switch_to_basic_block(loop_continue); // f. Let nextValue be ? IteratorValue(nextResult). - auto next_value = Bytecode::Operand(generator.allocate_register()); + auto next_value = generator.allocate_register(); generator.emit_iterator_value(next_value, next_result); // g. If lhsKind is either assignment or varBinding, then @@ -3151,14 +3161,14 @@ static Bytecode::CodeGenerationErrorOr> for_in_of_bo return completion_value; } -Bytecode::CodeGenerationErrorOr> ForInStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ForInStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); return generate_labelled_evaluation(generator, {}); } // 14.7.5.5 Runtime Semantics: ForInOfLoopEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-forinofloopevaluation -Bytecode::CodeGenerationErrorOr> ForInStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ForInStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set, [[maybe_unused]] Optional preferred_dst) const { auto& loop_end = generator.make_block(); auto& loop_update = generator.make_block(); @@ -3168,13 +3178,13 @@ Bytecode::CodeGenerationErrorOr> ForInStatement::gen return for_in_of_body_evaluation(generator, *this, m_lhs, body(), head_result, label_set, loop_end, loop_update); } -Bytecode::CodeGenerationErrorOr> ForOfStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ForOfStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); return generate_labelled_evaluation(generator, {}); } -Bytecode::CodeGenerationErrorOr> ForOfStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ForOfStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set, [[maybe_unused]] Optional preferred_dst) const { auto& loop_end = generator.make_block(); auto& loop_update = generator.make_block(); @@ -3184,13 +3194,13 @@ Bytecode::CodeGenerationErrorOr> ForOfStatement::gen return for_in_of_body_evaluation(generator, *this, m_lhs, body(), head_result, label_set, loop_end, loop_update); } -Bytecode::CodeGenerationErrorOr> ForAwaitOfStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ForAwaitOfStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); return generate_labelled_evaluation(generator, {}); } -Bytecode::CodeGenerationErrorOr> ForAwaitOfStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ForAwaitOfStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set, [[maybe_unused]] Optional preferred_dst) const { auto& loop_end = generator.make_block(); auto& loop_update = generator.make_block(); @@ -3201,7 +3211,7 @@ Bytecode::CodeGenerationErrorOr> ForAwaitOfStatement } // 13.3.12.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-meta-properties-runtime-semantics-evaluation -Bytecode::CodeGenerationErrorOr> MetaProperty::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> MetaProperty::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); // NewTarget : new . target @@ -3222,18 +3232,18 @@ Bytecode::CodeGenerationErrorOr> MetaProperty::gener VERIFY_NOT_REACHED(); } -Bytecode::CodeGenerationErrorOr> ClassFieldInitializerStatement::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ClassFieldInitializerStatement::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); auto value = TRY(generator.emit_named_evaluation_if_anonymous_function(*m_expression, generator.intern_identifier(m_class_field_identifier_name), preferred_dst)); generator.perform_needed_unwinds(); - generator.emit(value); + generator.emit(value.has_value() ? value->operand() : Optional {}); return value; } -static Bytecode::CodeGenerationErrorOr generate_optional_chain(Bytecode::Generator& generator, OptionalChain const& optional_chain, Bytecode::Operand current_value, Bytecode::Operand current_base, [[maybe_unused]] Optional preferred_dst) +static Bytecode::CodeGenerationErrorOr generate_optional_chain(Bytecode::Generator& generator, OptionalChain const& optional_chain, ScopedOperand current_value, ScopedOperand current_base, [[maybe_unused]] Optional preferred_dst) { - Optional new_current_value; + Optional new_current_value; if (is(optional_chain.base())) { auto& member_expression = static_cast(optional_chain.base()); auto base_and_value = TRY(get_base_and_value_from_member_expression(generator, member_expression)); @@ -3299,22 +3309,22 @@ static Bytecode::CodeGenerationErrorOr generate_optional_chain(Bytecode::G return {}; } -Bytecode::CodeGenerationErrorOr> OptionalChain::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> OptionalChain::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); - auto current_base = Bytecode::Operand(generator.allocate_register()); + auto current_base = generator.allocate_register(); auto current_value = choose_dst(generator, preferred_dst); generator.emit(current_base, generator.add_constant(js_undefined())); TRY(generate_optional_chain(generator, *this, current_value, current_base)); return current_value; } -Bytecode::CodeGenerationErrorOr> ImportCall::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ImportCall::generate_bytecode(Bytecode::Generator& generator, Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); auto specifier = TRY(m_specifier->generate_bytecode(generator)).value(); - Optional options; + Optional options; if (m_options) { options = TRY(m_options->generate_bytecode(generator)).value(); } else { @@ -3325,14 +3335,14 @@ Bytecode::CodeGenerationErrorOr> ImportCall::generat return dst; } -Bytecode::CodeGenerationErrorOr> ExportStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ExportStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const { Bytecode::Generator::SourceLocationScope scope(generator, *this); if (!is_default_export()) { if (m_statement) { return m_statement->generate_bytecode(generator); } - return Optional {}; + return Optional {}; } VERIFY(m_statement); @@ -3366,9 +3376,9 @@ Bytecode::CodeGenerationErrorOr> ExportStatement::ge return value; } -Bytecode::CodeGenerationErrorOr> ImportStatement::generate_bytecode(Bytecode::Generator&, [[maybe_unused]] Optional preferred_dst) const +Bytecode::CodeGenerationErrorOr> ImportStatement::generate_bytecode(Bytecode::Generator&, [[maybe_unused]] Optional preferred_dst) const { - return Optional {}; + return Optional {}; } } diff --git a/Userland/Libraries/LibJS/Bytecode/BasicBlock.h b/Userland/Libraries/LibJS/Bytecode/BasicBlock.h index 5f13ab95c0c..0596da01df2 100644 --- a/Userland/Libraries/LibJS/Bytecode/BasicBlock.h +++ b/Userland/Libraries/LibJS/Bytecode/BasicBlock.h @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include @@ -53,7 +53,7 @@ public: void add_source_map_entry(size_t bytecode_offset, SourceRecord const& source_record) { m_source_map.set(bytecode_offset, source_record); } auto const& this_() const { return m_this; } - void set_this(Operand operand) { m_this = operand; } + void set_this(ScopedOperand operand) { m_this = operand; } private: explicit BasicBlock(u32 index, String name); @@ -67,7 +67,7 @@ private: HashMap m_source_map; - Optional m_this; + Optional m_this; }; } diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.cpp b/Userland/Libraries/LibJS/Bytecode/Generator.cpp index abe884f4759..a324ec97806 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Generator.cpp @@ -22,6 +22,7 @@ Generator::Generator(VM& vm) , m_identifier_table(make()) , m_regex_table(make()) , m_constants(vm.heap()) + , m_accumulator(*this, Operand(Register::accumulator())) { } @@ -208,6 +209,8 @@ CodeGenerationErrorOr> Generator::generate(VM& vm, ASTN executable->basic_block_start_offsets = move(basic_block_start_offsets); executable->source_map = move(source_map); + generator.m_finished = true; + return executable; } @@ -217,10 +220,28 @@ void Generator::grow(size_t additional_size) m_current_basic_block->grow(additional_size); } -Register Generator::allocate_register() +ScopedOperand Generator::allocate_sequential_register() { VERIFY(m_next_register != NumericLimits::max()); - return Register { m_next_register++ }; + return ScopedOperand { *this, Operand { Register { m_next_register++ } } }; +} + +ScopedOperand Generator::allocate_register() +{ + if (!m_free_registers.is_empty()) { + return ScopedOperand { *this, Operand { m_free_registers.take_last() } }; + } + return allocate_sequential_register(); +} + +void Generator::free_register(Register reg) +{ + m_free_registers.append(reg); +} + +ScopedOperand Generator::local(u32 local_index) +{ + return ScopedOperand { *this, Operand { Operand::Type::Local, static_cast(local_index) } }; } Generator::SourceLocationScope::SourceLocationScope(Generator& generator, ASTNode const& node) @@ -311,10 +332,10 @@ CodeGenerationErrorOr Generator::emit_super_refere // https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation // 1. Let env be GetThisEnvironment(). // 2. Let actualThis be ? env.GetThisBinding(). - auto actual_this = Operand(allocate_register()); + auto actual_this = allocate_register(); emit(actual_this); - Optional computed_property_value; + Optional computed_property_value; if (expression.is_computed()) { // SuperProperty : super [ Expression ] @@ -329,7 +350,7 @@ CodeGenerationErrorOr Generator::emit_super_refere // 1. Let env be GetThisEnvironment(). // 2. Assert: env.HasSuperBinding() is true. // 3. Let baseValue be ? env.GetSuperBase(). - auto base_value = Operand(allocate_register()); + auto base_value = allocate_register(); emit(base_value); // 4. Return the Reference Record { [[Base]]: baseValue, [[ReferencedName]]: propertyKey, [[Strict]]: strict, [[ThisValue]]: actualThis }. @@ -340,7 +361,7 @@ CodeGenerationErrorOr Generator::emit_super_refere }; } -CodeGenerationErrorOr Generator::emit_load_from_reference(JS::ASTNode const& node, Optional preferred_dst) +CodeGenerationErrorOr Generator::emit_load_from_reference(JS::ASTNode const& node, Optional preferred_dst) { if (is(node)) { auto& identifier = static_cast(node); @@ -360,7 +381,7 @@ CodeGenerationErrorOr Generator::emit_load_from_re // https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation if (is(expression.object())) { auto super_reference = TRY(emit_super_reference(expression)); - auto dst = preferred_dst.has_value() ? preferred_dst.value() : Operand(allocate_register()); + auto dst = preferred_dst.has_value() ? preferred_dst.value() : allocate_register(); if (super_reference.referenced_name.has_value()) { // 5. Let propertyKey be ? ToPropertyKey(propertyNameValue). @@ -381,9 +402,9 @@ CodeGenerationErrorOr Generator::emit_load_from_re if (expression.is_computed()) { auto property = TRY(expression.property().generate_bytecode(*this)).value(); - auto saved_property = Operand(allocate_register()); + auto saved_property = allocate_register(); emit(saved_property, property); - auto dst = preferred_dst.has_value() ? preferred_dst.value() : Operand(allocate_register()); + auto dst = preferred_dst.has_value() ? preferred_dst.value() : allocate_register(); emit(dst, base, property, move(base_identifier)); return ReferenceOperands { .base = base, @@ -394,7 +415,7 @@ CodeGenerationErrorOr Generator::emit_load_from_re } if (expression.property().is_identifier()) { auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); - auto dst = preferred_dst.has_value() ? preferred_dst.value() : Operand(allocate_register()); + auto dst = preferred_dst.has_value() ? preferred_dst.value() : allocate_register(); emit_get_by_id(dst, base, identifier_table_ref, move(base_identifier)); return ReferenceOperands { .base = base, @@ -405,7 +426,7 @@ CodeGenerationErrorOr Generator::emit_load_from_re } if (expression.property().is_private_identifier()) { auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); - auto dst = preferred_dst.has_value() ? preferred_dst.value() : Operand(allocate_register()); + auto dst = preferred_dst.has_value() ? preferred_dst.value() : allocate_register(); emit(dst, base, identifier_table_ref); return ReferenceOperands { .base = base, @@ -420,7 +441,7 @@ CodeGenerationErrorOr Generator::emit_load_from_re }; } -CodeGenerationErrorOr Generator::emit_store_to_reference(JS::ASTNode const& node, Operand value) +CodeGenerationErrorOr Generator::emit_store_to_reference(JS::ASTNode const& node, ScopedOperand value) { if (is(node)) { auto& identifier = static_cast(node); @@ -473,7 +494,7 @@ CodeGenerationErrorOr Generator::emit_store_to_reference(JS::ASTNode const }; } -CodeGenerationErrorOr Generator::emit_store_to_reference(ReferenceOperands const& reference, Operand value) +CodeGenerationErrorOr Generator::emit_store_to_reference(ReferenceOperands const& reference, ScopedOperand value) { if (reference.referenced_private_identifier.has_value()) { emit(*reference.base, *reference.referenced_private_identifier, value); @@ -493,14 +514,14 @@ CodeGenerationErrorOr Generator::emit_store_to_reference(ReferenceOperands return {}; } -CodeGenerationErrorOr> Generator::emit_delete_reference(JS::ASTNode const& node) +CodeGenerationErrorOr> Generator::emit_delete_reference(JS::ASTNode const& node) { if (is(node)) { auto& identifier = static_cast(node); if (identifier.is_local()) { return add_constant(Value(false)); } - auto dst = Operand(allocate_register()); + auto dst = allocate_register(); emit(dst, intern_identifier(identifier.string())); return dst; } @@ -520,11 +541,11 @@ CodeGenerationErrorOr> Generator::emit_delete_reference(JS::AS emit(dst, *super_reference.base, *super_reference.this_value, identifier_table_ref); } - return Optional {}; + return Optional {}; } auto object = TRY(expression.object().generate_bytecode(*this)).value(); - auto dst = Operand(allocate_register()); + auto dst = allocate_register(); if (expression.is_computed()) { auto property = TRY(expression.property().generate_bytecode(*this)).value(); @@ -555,10 +576,10 @@ CodeGenerationErrorOr> Generator::emit_delete_reference(JS::AS return add_constant(Value(true)); } -void Generator::emit_set_variable(JS::Identifier const& identifier, Operand value, Bytecode::Op::SetVariable::InitializationMode initialization_mode, Bytecode::Op::EnvironmentMode mode) +void Generator::emit_set_variable(JS::Identifier const& identifier, ScopedOperand value, Bytecode::Op::SetVariable::InitializationMode initialization_mode, Bytecode::Op::EnvironmentMode mode) { if (identifier.is_local()) { - if (value.is_local() && value.index() == identifier.local_variable_index()) { + if (value.operand().is_local() && value.operand().index() == identifier.local_variable_index()) { // Moving a local to itself is a no-op. return; } @@ -730,7 +751,7 @@ void Generator::generate_continue(DeprecatedFlyString const& continue_label) generate_labelled_jump(JumpType::Continue, continue_label); } -void Generator::push_home_object(Operand object) +void Generator::push_home_object(ScopedOperand object) { m_home_objects.append(object); } @@ -740,7 +761,7 @@ void Generator::pop_home_object() m_home_objects.take_last(); } -void Generator::emit_new_function(Operand dst, FunctionExpression const& function_node, Optional lhs_name) +void Generator::emit_new_function(ScopedOperand dst, FunctionExpression const& function_node, Optional lhs_name) { if (m_home_objects.is_empty()) { emit(dst, function_node, lhs_name); @@ -749,7 +770,7 @@ void Generator::emit_new_function(Operand dst, FunctionExpression const& functio } } -CodeGenerationErrorOr> Generator::emit_named_evaluation_if_anonymous_function(Expression const& expression, Optional lhs_name, Optional preferred_dst) +CodeGenerationErrorOr> Generator::emit_named_evaluation_if_anonymous_function(Expression const& expression, Optional lhs_name, Optional preferred_dst) { if (is(expression)) { auto const& function_expression = static_cast(expression); @@ -768,22 +789,22 @@ CodeGenerationErrorOr> Generator::emit_named_evaluation_if_ano return expression.generate_bytecode(*this, preferred_dst); } -void Generator::emit_get_by_id(Operand dst, Operand base, IdentifierTableIndex property_identifier, Optional base_identifier) +void Generator::emit_get_by_id(ScopedOperand dst, ScopedOperand base, IdentifierTableIndex property_identifier, Optional base_identifier) { emit(dst, base, property_identifier, move(base_identifier), m_next_property_lookup_cache++); } -void Generator::emit_get_by_id_with_this(Operand dst, Operand base, IdentifierTableIndex id, Operand this_value) +void Generator::emit_get_by_id_with_this(ScopedOperand dst, ScopedOperand base, IdentifierTableIndex id, ScopedOperand this_value) { emit(dst, base, id, this_value, m_next_property_lookup_cache++); } -void Generator::emit_iterator_value(Operand dst, Operand result) +void Generator::emit_iterator_value(ScopedOperand dst, ScopedOperand result) { emit_get_by_id(dst, result, intern_identifier("value"sv)); } -void Generator::emit_iterator_complete(Operand dst, Operand result) +void Generator::emit_iterator_complete(ScopedOperand dst, ScopedOperand result) { emit_get_by_id(dst, result, intern_identifier("done"sv)); } @@ -798,7 +819,7 @@ void Generator::set_local_initialized(u32 local_index) m_initialized_locals.set(local_index); } -Operand Generator::get_this(Optional preferred_dst) +ScopedOperand Generator::get_this(Optional preferred_dst) { if (m_current_basic_block->this_().has_value()) return m_current_basic_block->this_().value(); @@ -806,10 +827,15 @@ Operand Generator::get_this(Optional preferred_dst) m_current_basic_block->set_this(m_root_basic_blocks[0]->this_().value()); return m_root_basic_blocks[0]->this_().value(); } - auto dst = preferred_dst.has_value() ? preferred_dst.value() : Operand(allocate_register()); + auto dst = preferred_dst.has_value() ? preferred_dst.value() : allocate_register(); emit(dst); m_current_basic_block->set_this(dst); return dst; } +ScopedOperand Generator::accumulator() +{ + return m_accumulator; +} + } diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.h b/Userland/Libraries/LibJS/Bytecode/Generator.h index 28eefc1c026..7da2e117f82 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.h +++ b/Userland/Libraries/LibJS/Bytecode/Generator.h @@ -34,7 +34,13 @@ public: }; static CodeGenerationErrorOr> generate(VM&, ASTNode const&, ReadonlySpan parameters, FunctionKind = FunctionKind::Normal); - Register allocate_register(); + [[nodiscard]] ScopedOperand allocate_sequential_register(); + + [[nodiscard]] ScopedOperand allocate_register(); + [[nodiscard]] ScopedOperand local(u32 local_index); + [[nodiscard]] ScopedOperand accumulator(); + + void free_register(Register); void set_local_initialized(u32 local_index); [[nodiscard]] bool is_local_initialized(u32 local_index) const; @@ -112,28 +118,28 @@ public: } struct ReferenceOperands { - Optional base {}; // [[Base]] - Optional referenced_name {}; // [[ReferencedName]] as an operand + Optional base {}; // [[Base]] + Optional referenced_name {}; // [[ReferencedName]] as an operand Optional referenced_identifier {}; // [[ReferencedName]] as an identifier Optional referenced_private_identifier {}; // [[ReferencedName]] as a private identifier - Optional this_value {}; // [[ThisValue]] - Optional loaded_value {}; // Loaded value, if we've performed a load. + Optional this_value {}; // [[ThisValue]] + Optional loaded_value {}; // Loaded value, if we've performed a load. }; - CodeGenerationErrorOr emit_load_from_reference(JS::ASTNode const&, Optional preferred_dst = {}); - CodeGenerationErrorOr emit_store_to_reference(JS::ASTNode const&, Operand value); - CodeGenerationErrorOr emit_store_to_reference(ReferenceOperands const&, Operand value); - CodeGenerationErrorOr> emit_delete_reference(JS::ASTNode const&); + CodeGenerationErrorOr emit_load_from_reference(JS::ASTNode const&, Optional preferred_dst = {}); + CodeGenerationErrorOr emit_store_to_reference(JS::ASTNode const&, ScopedOperand value); + CodeGenerationErrorOr emit_store_to_reference(ReferenceOperands const&, ScopedOperand value); + CodeGenerationErrorOr> emit_delete_reference(JS::ASTNode const&); CodeGenerationErrorOr emit_super_reference(MemberExpression const&); - void emit_set_variable(JS::Identifier const& identifier, Operand value, Bytecode::Op::SetVariable::InitializationMode initialization_mode = Bytecode::Op::SetVariable::InitializationMode::Set, Bytecode::Op::EnvironmentMode mode = Bytecode::Op::EnvironmentMode::Lexical); + void emit_set_variable(JS::Identifier const& identifier, ScopedOperand value, Bytecode::Op::SetVariable::InitializationMode initialization_mode = Bytecode::Op::SetVariable::InitializationMode::Set, Bytecode::Op::EnvironmentMode mode = Bytecode::Op::EnvironmentMode::Lexical); - void push_home_object(Operand); + void push_home_object(ScopedOperand); void pop_home_object(); - void emit_new_function(Operand dst, JS::FunctionExpression const&, Optional lhs_name); + void emit_new_function(ScopedOperand dst, JS::FunctionExpression const&, Optional lhs_name); - CodeGenerationErrorOr> emit_named_evaluation_if_anonymous_function(Expression const&, Optional lhs_name, Optional preferred_dst = {}); + CodeGenerationErrorOr> emit_named_evaluation_if_anonymous_function(Expression const&, Optional lhs_name, Optional preferred_dst = {}); void begin_continuable_scope(Label continue_target, Vector const& language_label_set); void end_continuable_scope(); @@ -257,14 +263,14 @@ public: m_boundaries.take_last(); } - [[nodiscard]] Operand get_this(Optional preferred_dst = {}); + [[nodiscard]] ScopedOperand get_this(Optional preferred_dst = {}); - void emit_get_by_id(Operand dst, Operand base, IdentifierTableIndex property_identifier, Optional base_identifier = {}); + void emit_get_by_id(ScopedOperand dst, ScopedOperand base, IdentifierTableIndex property_identifier, Optional base_identifier = {}); - void emit_get_by_id_with_this(Operand dst, Operand base, IdentifierTableIndex, Operand this_value); + void emit_get_by_id_with_this(ScopedOperand dst, ScopedOperand base, IdentifierTableIndex, ScopedOperand this_value); - void emit_iterator_value(Operand dst, Operand result); - void emit_iterator_complete(Operand dst, Operand result); + void emit_iterator_value(ScopedOperand dst, ScopedOperand result); + void emit_iterator_complete(ScopedOperand dst, ScopedOperand result); [[nodiscard]] size_t next_global_variable_cache() { return m_next_global_variable_cache++; } [[nodiscard]] size_t next_environment_variable_cache() { return m_next_environment_variable_cache++; } @@ -274,20 +280,22 @@ public: Yes, No, }; - [[nodiscard]] Operand add_constant(Value value, DeduplicateConstant deduplicate_constant = DeduplicateConstant::Yes) + [[nodiscard]] ScopedOperand add_constant(Value value, DeduplicateConstant deduplicate_constant = DeduplicateConstant::Yes) { if (deduplicate_constant == DeduplicateConstant::Yes) { for (size_t i = 0; i < m_constants.size(); ++i) { if (m_constants[i] == value) - return Operand(Operand::Type::Constant, i); + return ScopedOperand(*this, Operand(Operand::Type::Constant, i)); } } m_constants.append(value); - return Operand(Operand::Type::Constant, m_constants.size() - 1); + return ScopedOperand(*this, Operand(Operand::Type::Constant, m_constants.size() - 1)); } UnwindContext const* current_unwind_context() const { return m_current_unwind_context; } + [[nodiscard]] bool is_finished() const { return m_finished; } + private: VM& m_vm; @@ -318,6 +326,9 @@ private: NonnullOwnPtr m_regex_table; MarkedVector m_constants; + ScopedOperand m_accumulator; + Vector m_free_registers; + u32 m_next_register { Register::reserved_register_count }; u32 m_next_block { 1 }; u32 m_next_property_lookup_cache { 0 }; @@ -327,9 +338,11 @@ private: Vector m_continuable_scopes; Vector m_breakable_scopes; Vector m_boundaries; - Vector m_home_objects; + Vector m_home_objects; HashTable m_initialized_locals; + + bool m_finished { false }; }; } diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index 42935f14649..671983d5545 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -211,7 +212,7 @@ class CopyObjectExcludingProperties final : public Instruction { public: static constexpr bool IsVariableLength = true; - CopyObjectExcludingProperties(Operand dst, Operand from_object, Vector const& excluded_names) + CopyObjectExcludingProperties(Operand dst, Operand from_object, Vector const& excluded_names) : Instruction(Type::CopyObjectExcludingProperties) , m_dst(dst) , m_from_object(from_object) @@ -1238,7 +1239,7 @@ class Call final : public Instruction { public: static constexpr bool IsVariableLength = true; - Call(CallType type, Operand dst, Operand callee, Operand this_value, ReadonlySpan arguments, Optional expression_string = {}, Optional builtin = {}) + Call(CallType type, Operand dst, Operand callee, Operand this_value, ReadonlySpan arguments, Optional expression_string = {}, Optional builtin = {}) : Instruction(Type::Call) , m_dst(dst) , m_callee(callee) diff --git a/Userland/Libraries/LibJS/Bytecode/Operand.h b/Userland/Libraries/LibJS/Bytecode/Operand.h index 560111af25d..89482af46c5 100644 --- a/Userland/Libraries/LibJS/Bytecode/Operand.h +++ b/Userland/Libraries/LibJS/Bytecode/Operand.h @@ -36,6 +36,8 @@ public: [[nodiscard]] Type type() const { return m_type; } [[nodiscard]] u32 index() const { return m_index; } + [[nodiscard]] Register as_register() const; + private: Type m_type {}; u32 m_index { 0 }; diff --git a/Userland/Libraries/LibJS/Bytecode/ScopedOperand.cpp b/Userland/Libraries/LibJS/Bytecode/ScopedOperand.cpp new file mode 100644 index 00000000000..50aa61d3368 --- /dev/null +++ b/Userland/Libraries/LibJS/Bytecode/ScopedOperand.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace JS::Bytecode { + +ScopedOperandImpl::~ScopedOperandImpl() +{ + if (!m_generator.is_finished() && m_operand.is_register() && m_operand.as_register().index() != 0) + m_generator.free_register(m_operand.as_register()); +} + +Register Operand::as_register() const +{ + return Register { m_index }; +} + +} diff --git a/Userland/Libraries/LibJS/Bytecode/ScopedOperand.h b/Userland/Libraries/LibJS/Bytecode/ScopedOperand.h new file mode 100644 index 00000000000..a2f42a42a43 --- /dev/null +++ b/Userland/Libraries/LibJS/Bytecode/ScopedOperand.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace JS::Bytecode { + +class ScopedOperandImpl : public RefCounted { +public: + ScopedOperandImpl(Generator& generator, Operand operand) + : m_generator(generator) + , m_operand(operand) + { + } + + ~ScopedOperandImpl(); + + [[nodiscard]] Operand operand() const { return m_operand; } + +private: + Generator& m_generator; + Operand m_operand; +}; + +class ScopedOperand { +public: + explicit ScopedOperand(Generator& generator, Operand operand) + : m_impl(adopt_ref(*new ScopedOperandImpl(generator, operand))) + { + } + + [[nodiscard]] Operand operand() const { return m_impl->operand(); } + operator Operand() const { return operand(); } + + [[nodiscard]] bool operator==(ScopedOperand const& other) const { return operand() == other.operand(); } + +private: + NonnullRefPtr m_impl; +}; + +} diff --git a/Userland/Libraries/LibJS/CMakeLists.txt b/Userland/Libraries/LibJS/CMakeLists.txt index c60ac43685f..adff8fbe896 100644 --- a/Userland/Libraries/LibJS/CMakeLists.txt +++ b/Userland/Libraries/LibJS/CMakeLists.txt @@ -11,6 +11,7 @@ set(SOURCES Bytecode/Interpreter.cpp Bytecode/Label.cpp Bytecode/RegexTable.cpp + Bytecode/ScopedOperand.cpp Bytecode/StringTable.cpp Console.cpp Contrib/Test262/262Object.cpp