Browse Source

LibJS: Join locals, constants and registers into single vector

Merging registers, constants and locals into single vector means:
- Better data locality
- No need to check type in Interpreter::get() and Interpreter::set()
  which are very hot functions

Performance improvement is visible in almost all Octane and Kraken
tests.
Aliaksandr Kalenik 1 year ago
parent
commit
d79438a2a6

+ 4 - 1
Userland/Libraries/LibJS/AST.cpp

@@ -1624,7 +1624,10 @@ void ScopeNode::block_declaration_instantiation(VM& vm, Environment* environment
 
             // iii. Perform ! env.InitializeBinding(fn, fo). NOTE: This step is replaced in section B.3.2.6.
             if (function_declaration.name_identifier()->is_local()) {
-                vm.running_execution_context().local(function_declaration.name_identifier()->local_variable_index()) = function;
+                auto& running_execution_context = vm.running_execution_context();
+                auto number_of_registers = running_execution_context.executable->number_of_registers;
+                auto number_of_constants = running_execution_context.executable->constants.size();
+                running_execution_context.local(function_declaration.name_identifier()->local_variable_index() + number_of_registers + number_of_constants) = function;
             } else {
                 VERIFY(is<DeclarativeEnvironment>(*environment));
                 static_cast<DeclarativeEnvironment&>(*environment).initialize_or_set_mutable_binding({}, vm, function_declaration.name(), function);

+ 42 - 0
Userland/Libraries/LibJS/Bytecode/Generator.cpp

@@ -266,6 +266,19 @@ CodeGenerationErrorOr<NonnullGCPtr<Executable>> Generator::emit_function_body_by
 
     HashMap<size_t, SourceRecord> source_map;
 
+    for (auto& block : generator.m_root_basic_blocks) {
+        if (!block->is_terminated()) {
+            // NOTE: We must ensure that the "undefined" constant, which will be used by the not yet
+            // emitted End instruction, is taken into account while shifting local operands by the
+            // number of constants.
+            (void)generator.add_constant(js_undefined());
+            break;
+        }
+    }
+
+    auto number_of_registers = generator.m_next_register;
+    auto number_of_constants = generator.m_constants.size();
+
     for (auto& block : generator.m_root_basic_blocks) {
         basic_block_start_offsets.append(bytecode.size());
         if (block->handler() || block->finalizer()) {
@@ -287,6 +300,21 @@ CodeGenerationErrorOr<NonnullGCPtr<Executable>> Generator::emit_function_body_by
         while (!it.at_end()) {
             auto& instruction = const_cast<Instruction&>(*it);
 
+            instruction.visit_operands([number_of_registers, number_of_constants](Operand& operand) {
+                switch (operand.type()) {
+                case Operand::Type::Register:
+                    break;
+                case Operand::Type::Local:
+                    operand.offset_index_by(number_of_registers + number_of_constants);
+                    break;
+                case Operand::Type::Constant:
+                    operand.offset_index_by(number_of_registers);
+                    break;
+                default:
+                    VERIFY_NOT_REACHED();
+                }
+            });
+
             // OPTIMIZATION: Don't emit jumps that just jump to the next block.
             if (instruction.type() == Instruction::Type::Jump) {
                 auto& jump = static_cast<Bytecode::Op::Jump&>(instruction);
@@ -333,6 +361,20 @@ CodeGenerationErrorOr<NonnullGCPtr<Executable>> Generator::emit_function_body_by
         }
         if (!block->is_terminated()) {
             Op::End end(generator.add_constant(js_undefined()));
+            end.visit_operands([number_of_registers, number_of_constants](Operand& operand) {
+                switch (operand.type()) {
+                case Operand::Type::Register:
+                    break;
+                case Operand::Type::Local:
+                    operand.offset_index_by(number_of_registers + number_of_constants);
+                    break;
+                case Operand::Type::Constant:
+                    operand.offset_index_by(number_of_registers);
+                    break;
+                default:
+                    VERIFY_NOT_REACHED();
+                }
+            });
             bytecode.append(reinterpret_cast<u8 const*>(&end), end.length());
         }
         if (block->handler() || block->finalizer()) {

+ 16 - 0
Userland/Libraries/LibJS/Bytecode/Instruction.cpp

@@ -42,6 +42,22 @@ void Instruction::visit_labels(Function<void(JS::Bytecode::Label&)> visitor)
 #undef __BYTECODE_OP
 }
 
+void Instruction::visit_operands(Function<void(JS::Bytecode::Operand&)> visitor)
+{
+#define __BYTECODE_OP(op)                                               \
+    case Type::op:                                                      \
+        static_cast<Op::op&>(*this).visit_operands_impl(move(visitor)); \
+        return;
+
+    switch (type()) {
+        ENUMERATE_BYTECODE_OPS(__BYTECODE_OP)
+    default:
+        VERIFY_NOT_REACHED();
+    }
+
+#undef __BYTECODE_OP
+}
+
 template<typename Op>
 concept HasVariableLength = Op::IsVariableLength;
 

+ 2 - 0
Userland/Libraries/LibJS/Bytecode/Instruction.h

@@ -157,6 +157,7 @@ public:
     size_t length() const;
     ByteString to_byte_string(Bytecode::Executable const&) const;
     void visit_labels(Function<void(Label&)> visitor);
+    void visit_operands(Function<void(Operand&)> visitor);
     static void destroy(Instruction&);
 
 protected:
@@ -166,6 +167,7 @@ protected:
     }
 
     void visit_labels_impl(Function<void(Label&)>) { }
+    void visit_operands_impl(Function<void(Operand&)>) { }
 
 private:
     Type m_type {};

+ 19 - 33
Userland/Libraries/LibJS/Bytecode/Interpreter.cpp

@@ -54,7 +54,7 @@ static ByteString format_operand(StringView name, Operand operand, Bytecode::Exe
         break;
     case Operand::Type::Constant: {
         builder.append("\033[36m"sv);
-        auto value = executable.constants[operand.index()];
+        auto value = executable.constants[operand.index() - executable.number_of_registers];
         if (value.is_empty())
             builder.append("<Empty>"sv);
         else if (value.is_boolean())
@@ -153,30 +153,12 @@ Interpreter::~Interpreter()
 
 ALWAYS_INLINE Value Interpreter::get(Operand op) const
 {
-    switch (op.type()) {
-    case Operand::Type::Register:
-        return m_registers.data()[op.index()];
-    case Operand::Type::Local:
-        return m_locals.data()[op.index()];
-    case Operand::Type::Constant:
-        return m_constants.data()[op.index()];
-    }
-    __builtin_unreachable();
+    return m_registers_and_constants_and_locals.data()[op.index()];
 }
 
 ALWAYS_INLINE void Interpreter::set(Operand op, Value value)
 {
-    switch (op.type()) {
-    case Operand::Type::Register:
-        m_registers.data()[op.index()] = value;
-        return;
-    case Operand::Type::Local:
-        m_locals.data()[op.index()] = value;
-        return;
-    case Operand::Type::Constant:
-        break;
-    }
-    __builtin_unreachable();
+    m_registers_and_constants_and_locals.data()[op.index()] = value;
 }
 
 // 16.1.6 ScriptEvaluation ( scriptRecord ), https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation
@@ -346,7 +328,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
 
     auto& running_execution_context = this->running_execution_context();
     auto* arguments = running_execution_context.arguments.data();
-    auto* locals = running_execution_context.locals.data();
+    auto* registers_and_constants_and_locals = running_execution_context.registers_and_constants_and_locals.data();
     auto& accumulator = this->accumulator();
     auto& executable = current_executable();
     auto const* bytecode = executable.bytecode.data();
@@ -384,7 +366,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
 
         handle_SetLocal: {
             auto& instruction = *reinterpret_cast<Op::SetLocal const*>(&bytecode[program_counter]);
-            locals[instruction.index()] = get(instruction.src());
+            registers_and_constants_and_locals[instruction.index()] = get(instruction.src());
             DISPATCH_NEXT(SetLocal);
         }
 
@@ -715,31 +697,35 @@ Interpreter::ResultAndReturnRegister Interpreter::run_executable(Executable& exe
     VERIFY(!vm().execution_context_stack().is_empty());
 
     auto& running_execution_context = vm().running_execution_context();
-    if (running_execution_context.registers.size() < executable.number_of_registers)
-        running_execution_context.registers.resize(executable.number_of_registers);
+    u32 registers_and_contants_count = executable.number_of_registers + executable.constants.size();
+    if (running_execution_context.registers_and_constants_and_locals.size() < registers_and_contants_count)
+        running_execution_context.registers_and_constants_and_locals.resize(registers_and_contants_count);
 
     TemporaryChange restore_running_execution_context { m_running_execution_context, &running_execution_context };
     TemporaryChange restore_arguments { m_arguments, running_execution_context.arguments.span() };
-    TemporaryChange restore_registers { m_registers, running_execution_context.registers.span() };
-    TemporaryChange restore_locals { m_locals, running_execution_context.locals.span() };
-    TemporaryChange restore_constants { m_constants, executable.constants.span() };
+    TemporaryChange restore_registers_and_constants_and_locals { m_registers_and_constants_and_locals, running_execution_context.registers_and_constants_and_locals.span() };
 
     reg(Register::accumulator()) = initial_accumulator_value;
     reg(Register::return_value()) = {};
 
     running_execution_context.executable = &executable;
 
+    for (size_t i = 0; i < executable.constants.size(); ++i) {
+        running_execution_context.registers_and_constants_and_locals[executable.number_of_registers + i] = executable.constants[i];
+    }
+
     run_bytecode(entry_point.value_or(0));
 
     dbgln_if(JS_BYTECODE_DEBUG, "Bytecode::Interpreter did run unit {:p}", &executable);
 
     if constexpr (JS_BYTECODE_DEBUG) {
-        for (size_t i = 0; i < registers().size(); ++i) {
+        auto const& registers_and_constants_and_locals = running_execution_context.registers_and_constants_and_locals;
+        for (size_t i = 0; i < executable.number_of_registers; ++i) {
             String value_string;
-            if (registers()[i].is_empty())
+            if (registers_and_constants_and_locals[i].is_empty())
                 value_string = "(empty)"_string;
             else
-                value_string = registers()[i].to_string_without_side_effects();
+                value_string = registers_and_constants_and_locals[i].to_string_without_side_effects();
             dbgln("[{:3}] {}", i, value_string);
         }
     }
@@ -758,8 +744,8 @@ Interpreter::ResultAndReturnRegister Interpreter::run_executable(Executable& exe
     vm().finish_execution_generation();
 
     if (!exception.is_empty())
-        return { throw_completion(exception), running_execution_context.registers[0] };
-    return { return_value, running_execution_context.registers[0] };
+        return { throw_completion(exception), running_execution_context.registers_and_constants_and_locals[0] };
+    return { return_value, running_execution_context.registers_and_constants_and_locals[0] };
 }
 
 void Interpreter::enter_unwind_context()

+ 3 - 8
Userland/Libraries/LibJS/Bytecode/Interpreter.h

@@ -49,11 +49,11 @@ public:
     ALWAYS_INLINE Value& saved_return_value() { return reg(Register::saved_return_value()); }
     Value& reg(Register const& r)
     {
-        return m_registers.data()[r.index()];
+        return m_registers_and_constants_and_locals.data()[r.index()];
     }
     Value reg(Register const& r) const
     {
-        return m_registers.data()[r.index()];
+        return m_registers_and_constants_and_locals.data()[r.index()];
     }
 
     [[nodiscard]] Value get(Operand) const;
@@ -77,9 +77,6 @@ public:
     Executable const& current_executable() const { return *m_current_executable; }
     Optional<size_t> program_counter() const { return m_program_counter; }
 
-    Vector<Value>& registers() { return vm().running_execution_context().registers; }
-    Vector<Value> const& registers() const { return vm().running_execution_context().registers; }
-
     ExecutionContext& running_execution_context() { return *m_running_execution_context; }
 
 private:
@@ -99,9 +96,7 @@ private:
     GCPtr<DeclarativeEnvironment> m_global_declarative_environment { nullptr };
     Optional<size_t&> m_program_counter;
     Span<Value> m_arguments;
-    Span<Value> m_registers;
-    Span<Value> m_locals;
-    Span<Value> m_constants;
+    Span<Value> m_registers_and_constants_and_locals;
     ExecutionContext* m_running_execution_context { nullptr };
 };
 

+ 384 - 4
Userland/Libraries/LibJS/Bytecode/Op.h

@@ -43,6 +43,10 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+    }
 
 private:
     Operand m_dst;
@@ -82,6 +86,11 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_src);
+    }
 
     Operand dst() const { return m_dst; }
     Operand src() const { return m_src; }
@@ -130,6 +139,12 @@ private:
                                                                             \
         ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const; \
         ByteString to_byte_string_impl(Bytecode::Executable const&) const;  \
+        void visit_operands_impl(Function<void(Operand&)> visitor)          \
+        {                                                                   \
+            visitor(m_dst);                                                 \
+            visitor(m_lhs);                                                 \
+            visitor(m_rhs);                                                 \
+        }                                                                   \
                                                                             \
         Operand dst() const { return m_dst; }                               \
         Operand lhs() const { return m_lhs; }                               \
@@ -164,6 +179,11 @@ JS_ENUMERATE_COMMON_BINARY_OPS_WITH_FAST_PATH(JS_DECLARE_COMMON_BINARY_OP)
                                                                             \
         ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const; \
         ByteString to_byte_string_impl(Bytecode::Executable const&) const;  \
+        void visit_operands_impl(Function<void(Operand&)> visitor)          \
+        {                                                                   \
+            visitor(m_dst);                                                 \
+            visitor(m_src);                                                 \
+        }                                                                   \
                                                                             \
         Operand dst() const { return m_dst; }                               \
         Operand src() const { return m_src; }                               \
@@ -186,6 +206,10 @@ public:
 
     void execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+    }
 
     Operand dst() const { return m_dst; }
 
@@ -206,6 +230,10 @@ public:
 
     void execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+    }
 
     Operand dst() const { return m_dst; }
     StringTableIndex source_index() const { return m_source_index; }
@@ -234,6 +262,10 @@ private:
                                                                            \
         void execute_impl(Bytecode::Interpreter&) const;                   \
         ByteString to_byte_string_impl(Bytecode::Executable const&) const; \
+        void visit_operands_impl(Function<void(Operand&)> visitor)         \
+        {                                                                  \
+            visitor(m_dst);                                                \
+        }                                                                  \
                                                                            \
         Operand dst() const { return m_dst; }                              \
         StringTableIndex error_string() const { return m_error_string; }   \
@@ -263,6 +295,13 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_from_object);
+        for (size_t i = 0; i < m_excluded_names_count; i++)
+            visitor(m_excluded_names[i]);
+    }
 
     size_t length_impl() const
     {
@@ -304,6 +343,12 @@ public:
 
     void execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        for (size_t i = 0; i < m_element_count; i++)
+            visitor(m_elements[i]);
+    }
 
     Operand dst() const { return m_dst; }
 
@@ -340,6 +385,10 @@ public:
 
     void execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+    }
 
     Operand dst() const { return m_dst; }
     ReadonlySpan<Value> elements() const { return { m_elements, m_element_count }; }
@@ -377,6 +426,11 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_src);
+    }
 
     Operand dst() const { return m_dst; }
     Operand src() const { return m_src; }
@@ -400,6 +454,12 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_specifier);
+        visitor(m_options);
+    }
 
     Operand dst() const { return m_dst; }
     Operand specifier() const { return m_specifier; }
@@ -426,6 +486,12 @@ public:
     Operand dst() const { return m_dst; }
     Operand iterator() const { return m_iterator; }
 
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_iterator);
+    }
+
 private:
     Operand m_dst;
     Operand m_iterator;
@@ -446,6 +512,12 @@ public:
     Operand dst() const { return m_dst; }
     Operand src() const { return m_src; }
 
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_src);
+    }
+
 private:
     Operand m_dst;
     Operand m_src;
@@ -510,6 +582,11 @@ public:
 
     Operand object() const { return m_object; }
 
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_object);
+    }
+
 private:
     Operand m_object;
 };
@@ -527,6 +604,11 @@ public:
 
     Operand dst() const { return m_dst; }
 
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+    }
+
 private:
     Operand m_dst;
 };
@@ -599,6 +681,10 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_src);
+    }
 
     IdentifierTableIndex identifier() const { return m_identifier; }
     Operand src() const { return m_src; }
@@ -617,20 +703,25 @@ class SetLocal final : public Instruction {
 public:
     SetLocal(size_t index, Operand src)
         : Instruction(Type::SetLocal)
-        , m_index(index)
+        , m_dst(Operand(Operand::Type::Local, index))
         , m_src(src)
     {
     }
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_src);
+    }
 
-    u32 index() const { return m_index; }
-    Operand dst() const { return Operand(Operand::Type::Local, m_index); }
+    u32 index() const { return m_dst.index(); }
+    Operand dst() const { return m_dst; }
     Operand src() const { return m_src; }
 
 private:
-    u32 m_index;
+    Operand m_dst;
     Operand m_src;
 };
 
@@ -645,6 +736,10 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_src);
+    }
 
     size_t index() const { return m_index; }
     Operand src() const { return m_src; }
@@ -665,6 +760,10 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+    }
 
     u32 index() const { return m_index; }
     Operand dst() const { return m_dst; }
@@ -686,6 +785,11 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_callee);
+        visitor(m_this_value);
+    }
 
     IdentifierTableIndex identifier() const { return m_identifier; }
     Operand callee() const { return m_callee; }
@@ -713,6 +817,11 @@ public:
     Operand dst() const { return m_dst; }
     IdentifierTableIndex identifier() const { return m_identifier; }
 
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+    }
+
 private:
     Operand m_dst;
     IdentifierTableIndex m_identifier;
@@ -736,6 +845,11 @@ public:
     IdentifierTableIndex identifier() const { return m_identifier; }
     u32 cache_index() const { return m_cache_index; }
 
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+    }
+
 private:
     Operand m_dst;
     IdentifierTableIndex m_identifier;
@@ -757,6 +871,11 @@ public:
     Operand dst() const { return m_dst; }
     IdentifierTableIndex identifier() const { return m_identifier; }
 
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+    }
+
 private:
     Operand m_dst;
     IdentifierTableIndex m_identifier;
@@ -776,6 +895,11 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_base);
+    }
 
     Operand dst() const { return m_dst; }
     Operand base() const { return m_base; }
@@ -804,6 +928,12 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_base);
+        visitor(m_this_value);
+    }
 
     Operand dst() const { return m_dst; }
     Operand base() const { return m_base; }
@@ -831,6 +961,11 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_base);
+    }
 
     Operand dst() const { return m_dst; }
     Operand base() const { return m_base; }
@@ -854,6 +989,11 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_base);
+    }
 
     Operand dst() const { return m_dst; }
     Operand base() const { return m_base; }
@@ -889,6 +1029,11 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_base);
+        visitor(m_src);
+    }
 
     Operand base() const { return m_base; }
     IdentifierTableIndex property() const { return m_property; }
@@ -920,6 +1065,12 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_base);
+        visitor(m_this_value);
+        visitor(m_src);
+    }
 
     Operand base() const { return m_base; }
     Operand this_value() const { return m_this_value; }
@@ -950,6 +1101,11 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_base);
+        visitor(m_src);
+    }
 
     Operand base() const { return m_base; }
     IdentifierTableIndex property() const { return m_property; }
@@ -974,6 +1130,11 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_base);
+    }
 
     Operand dst() const { return m_dst; }
     Operand base() const { return m_base; }
@@ -998,6 +1159,12 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_base);
+        visitor(m_this_value);
+    }
 
     Operand dst() const { return m_dst; }
     Operand base() const { return m_base; }
@@ -1024,6 +1191,12 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_base);
+        visitor(m_property);
+    }
 
     Operand dst() const { return m_dst; }
     Operand base() const { return m_base; }
@@ -1051,6 +1224,13 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_base);
+        visitor(m_property);
+        visitor(m_this_value);
+    }
 
     Operand dst() const { return m_dst; }
     Operand base() const { return m_base; }
@@ -1078,6 +1258,12 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_base);
+        visitor(m_property);
+        visitor(m_src);
+    }
 
     Operand base() const { return m_base; }
     Operand property() const { return m_property; }
@@ -1106,6 +1292,13 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_base);
+        visitor(m_property);
+        visitor(m_this_value);
+        visitor(m_src);
+    }
 
     Operand base() const { return m_base; }
     Operand property() const { return m_property; }
@@ -1133,6 +1326,12 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_base);
+        visitor(m_property);
+    }
 
     Operand dst() const { return m_dst; }
     Operand base() const { return m_base; }
@@ -1162,6 +1361,13 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_base);
+        visitor(m_this_value);
+        visitor(m_property);
+    }
 
 private:
     Operand m_dst;
@@ -1212,6 +1418,10 @@ public:
         visitor(m_true_target);
         visitor(m_false_target);
     }
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_condition);
+    }
 
     Operand condition() const { return m_condition; }
     auto& true_target() const { return m_true_target; }
@@ -1240,6 +1450,10 @@ public:
     {
         visitor(m_target);
     }
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_condition);
+    }
 
     Operand condition() const { return m_condition; }
     auto& target() const { return m_target; }
@@ -1266,6 +1480,10 @@ public:
     {
         visitor(m_target);
     }
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_condition);
+    }
 
     Operand condition() const { return m_condition; }
     auto& target() const { return m_target; }
@@ -1305,6 +1523,11 @@ private:
         {                                                                                            \
             visitor(m_true_target);                                                                  \
             visitor(m_false_target);                                                                 \
+        }                                                                                            \
+        void visit_operands_impl(Function<void(Operand&)> visitor)                                   \
+        {                                                                                            \
+            visitor(m_lhs);                                                                          \
+            visitor(m_rhs);                                                                          \
         }                                                                                            \
                                                                                                      \
         Operand lhs() const { return m_lhs; }                                                        \
@@ -1340,6 +1563,10 @@ public:
         visitor(m_true_target);
         visitor(m_false_target);
     }
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_condition);
+    }
 
     Operand condition() const { return m_condition; }
     auto& true_target() const { return m_true_target; }
@@ -1370,6 +1597,10 @@ public:
         visitor(m_true_target);
         visitor(m_false_target);
     }
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_condition);
+    }
 
     Operand condition() const { return m_condition; }
     auto& true_target() const { return m_true_target; }
@@ -1422,6 +1653,14 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_callee);
+        visitor(m_this_value);
+        for (size_t i = 0; i < m_argument_count; i++)
+            visitor(m_arguments[i]);
+    }
 
 private:
     Operand m_dst;
@@ -1456,6 +1695,13 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_callee);
+        visitor(m_this_value);
+        visitor(m_arguments);
+    }
 
 private:
     Operand m_dst;
@@ -1478,6 +1724,11 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_arguments);
+    }
 
     Operand dst() const { return m_dst; }
     Operand arguments() const { return m_arguments; }
@@ -1514,6 +1765,16 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        if (m_super_class.has_value())
+            visitor(m_super_class.value());
+        for (size_t i = 0; i < m_element_keys_count; i++) {
+            if (m_element_keys[i].has_value())
+                visitor(m_element_keys[i].value());
+        }
+    }
 
     Operand dst() const { return m_dst; }
     Optional<Operand> const& super_class() const { return m_super_class; }
@@ -1542,6 +1803,12 @@ public:
 
     void execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        if (m_home_object.has_value())
+            visitor(m_home_object.value());
+    }
 
     Operand dst() const { return m_dst; }
     FunctionNode const& function_node() const { return m_function_node; }
@@ -1584,6 +1851,11 @@ public:
 
     void execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        if (m_value.has_value())
+            visitor(m_value.value());
+    }
 
     Optional<Operand> const& value() const { return m_value; }
 
@@ -1601,6 +1873,10 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+    }
 
     Operand dst() const { return m_dst; }
 
@@ -1619,6 +1895,11 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_src);
+    }
 
     Operand dst() const { return m_dst; }
     Operand src() const { return m_src; }
@@ -1638,6 +1919,10 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+    }
 
     Operand dst() const { return m_dst; }
 
@@ -1656,6 +1941,11 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_src);
+    }
 
     Operand dst() const { return m_dst; }
     Operand src() const { return m_src; }
@@ -1677,6 +1967,10 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_src);
+    }
 
     Operand src() const { return m_src; }
 
@@ -1694,6 +1988,10 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_src);
+    }
 
     Operand src() const { return m_src; }
 
@@ -1711,6 +2009,10 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_src);
+    }
 
     Operand src() const { return m_src; }
 
@@ -1728,6 +2030,10 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_src);
+    }
 
     Operand src() const { return m_src; }
 
@@ -1863,6 +2169,10 @@ public:
         if (m_continuation_label.has_value())
             visitor(m_continuation_label.value());
     }
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_value);
+    }
 
     auto& continuation() const { return m_continuation_label; }
     Operand value() const { return m_value; }
@@ -1889,6 +2199,10 @@ public:
     {
         visitor(m_continuation_label);
     }
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_argument);
+    }
 
     auto& continuation() const { return m_continuation_label; }
     Operand argument() const { return m_argument; }
@@ -1910,6 +2224,11 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_iterable);
+    }
 
     Operand dst() const { return m_dst; }
     Operand iterable() const { return m_iterable; }
@@ -1932,6 +2251,11 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_object);
+        visitor(m_iterator_record);
+    }
 
     Operand object() const { return m_object; }
     Operand iterator_record() const { return m_iterator_record; }
@@ -1952,6 +2276,11 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_next_method);
+        visitor(m_iterator_record);
+    }
 
     Operand next_method() const { return m_next_method; }
     Operand iterator_record() const { return m_iterator_record; }
@@ -1973,6 +2302,11 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_object);
+    }
 
     Operand dst() const { return m_dst; }
     Operand object() const { return m_object; }
@@ -1995,6 +2329,11 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_object);
+    }
 
     Operand dst() const { return m_dst; }
     Operand object() const { return m_object; }
@@ -2016,6 +2355,10 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_iterator_record);
+    }
 
     Operand iterator_record() const { return m_iterator_record; }
     Completion::Type completion_type() const { return m_completion_type; }
@@ -2039,6 +2382,10 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_iterator_record);
+    }
 
     Operand iterator_record() const { return m_iterator_record; }
     Completion::Type completion_type() const { return m_completion_type; }
@@ -2061,6 +2408,11 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+        visitor(m_iterator_record);
+    }
 
     Operand dst() const { return m_dst; }
     Operand iterator_record() const { return m_iterator_record; }
@@ -2080,6 +2432,10 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+    }
 
     Operand dst() const { return m_dst; }
 
@@ -2097,6 +2453,10 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+    }
 
     Operand dst() const { return m_dst; }
 
@@ -2114,6 +2474,10 @@ public:
 
     void execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+    }
 
     Operand dst() const { return m_dst; }
 
@@ -2131,6 +2495,10 @@ public:
 
     void execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+    }
 
     Operand dst() const { return m_dst; }
 
@@ -2149,6 +2517,10 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_dst);
+    }
 
     Operand dst() const { return m_dst; }
     IdentifierTableIndex identifier() const { return m_identifier; }
@@ -2170,6 +2542,10 @@ public:
 
     ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_value);
+    }
 
     Operand value() const { return m_value; }
 
@@ -2188,6 +2564,10 @@ public:
 
     void execute_impl(Bytecode::Interpreter&) const;
     ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_value);
+    }
 
 private:
     StringView m_text;

+ 2 - 0
Userland/Libraries/LibJS/Bytecode/Operand.h

@@ -38,6 +38,8 @@ public:
 
     [[nodiscard]] Register as_register() const;
 
+    void offset_index_by(u32 offset) { m_index += offset; }
+
 private:
     Type m_type {};
     u32 m_index { 0 };

+ 2 - 4
Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp

@@ -380,8 +380,6 @@ ThrowCompletionOr<Value> ECMAScriptFunctionObject::internal_call(Value this_argu
 
     auto callee_context = ExecutionContext::create(heap());
 
-    callee_context->locals.resize(m_local_variables_names.size());
-
     // Non-standard
     callee_context->arguments.ensure_capacity(max(arguments_list.size(), m_formal_parameters.size()));
     callee_context->arguments.append(arguments_list.data(), arguments_list.size());
@@ -457,8 +455,6 @@ ThrowCompletionOr<NonnullGCPtr<Object>> ECMAScriptFunctionObject::internal_const
 
     auto callee_context = ExecutionContext::create(heap());
 
-    callee_context->locals.resize(m_local_variables_names.size());
-
     // Non-standard
     callee_context->arguments.ensure_capacity(max(arguments_list.size(), m_formal_parameters.size()));
     callee_context->arguments.append(arguments_list.data(), arguments_list.size());
@@ -841,6 +837,8 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
         m_bytecode_executable = m_ecmascript_code->bytecode_executable();
     }
 
+    vm.running_execution_context().registers_and_constants_and_locals.resize(m_local_variables_names.size() + m_bytecode_executable->number_of_registers + m_bytecode_executable->constants.size());
+
     auto result_and_frame = vm.bytecode_interpreter().run_executable(*m_bytecode_executable, {});
 
     if (result_and_frame.value.is_error())

+ 2 - 4
Userland/Libraries/LibJS/Runtime/ExecutionContext.cpp

@@ -43,8 +43,7 @@ NonnullOwnPtr<ExecutionContext> ExecutionContext::copy() const
     copy->executable = executable;
     copy->arguments = arguments;
     copy->passed_argument_count = passed_argument_count;
-    copy->locals = locals;
-    copy->registers = registers;
+    copy->registers_and_constants_and_locals = registers_and_constants_and_locals;
     copy->unwind_contexts = unwind_contexts;
     copy->saved_lexical_environments = saved_lexical_environments;
     copy->previously_scheduled_jumps = previously_scheduled_jumps;
@@ -63,8 +62,7 @@ void ExecutionContext::visit_edges(Cell::Visitor& visitor)
     visitor.visit(executable);
     visitor.visit(function_name);
     visitor.visit(arguments);
-    visitor.visit(locals);
-    visitor.visit(registers);
+    visitor.visit(registers_and_constants_and_locals);
     for (auto& context : unwind_contexts) {
         visitor.visit(context.lexical_environment);
     }

+ 2 - 3
Userland/Libraries/LibJS/Runtime/ExecutionContext.h

@@ -70,14 +70,13 @@ public:
 
     Value& local(size_t index)
     {
-        return locals[index];
+        return registers_and_constants_and_locals[index];
     }
 
     u32 passed_argument_count { 0 };
 
     Vector<Value> arguments;
-    Vector<Value> locals;
-    Vector<Value> registers;
+    Vector<Value> registers_and_constants_and_locals;
     Vector<Bytecode::UnwindInfo> unwind_contexts;
     Vector<Optional<size_t>> previously_scheduled_jumps;
     Vector<GCPtr<Environment>> saved_lexical_environments;