Explorar el Código

LibJS/Bytecode: Split SetVariable into four separate instructions

Instead of SetVariable having 2x2 modes for variable/lexical and
initialize/set, those 4 modes are now separate instructions, which
makes each instruction much less branchy.
Andreas Kling hace 1 año
padre
commit
b7c04f999a

+ 1 - 1
Userland/Libraries/LibJS/AST.h

@@ -647,7 +647,7 @@ struct BindingPattern : RefCounted<BindingPattern> {
 
     bool contains_expression() const;
 
-    Bytecode::CodeGenerationErrorOr<void> generate_bytecode(Bytecode::Generator&, Bytecode::Op::SetVariable::InitializationMode initialization_mode, Bytecode::ScopedOperand const& object, bool create_variables) const;
+    Bytecode::CodeGenerationErrorOr<void> generate_bytecode(Bytecode::Generator&, Bytecode::Op::BindingInitializationMode initialization_mode, Bytecode::ScopedOperand const& object, bool create_variables) const;
 
     Vector<BindingEntry> entries;
     Kind kind { Kind::Object };

+ 17 - 19
Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp

@@ -585,7 +585,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> AssignmentExpression::g
                 auto rval = TRY(m_rhs->generate_bytecode(generator)).value();
 
                 // 5. Perform ? DestructuringAssignmentEvaluation of assignmentPattern with argument rval.
-                TRY(pattern->generate_bytecode(generator, Bytecode::Op::SetVariable::InitializationMode::Set, rval, false));
+                TRY(pattern->generate_bytecode(generator, Bytecode::Op::BindingInitializationMode::Set, rval, false));
 
                 // 6. Return rval.
                 return rval;
@@ -1151,7 +1151,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> FunctionDeclaration::ge
         auto index = generator.intern_identifier(name());
         auto value = generator.allocate_register();
         generator.emit<Bytecode::Op::GetVariable>(value, index);
-        generator.emit<Bytecode::Op::SetVariable>(index, value, Bytecode::Op::SetVariable::InitializationMode::Set, Bytecode::Op::EnvironmentMode::Var);
+        generator.emit<Bytecode::Op::SetVariableBinding>(index, value);
     }
     return Optional<ScopedOperand> {};
 }
@@ -1173,7 +1173,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> FunctionExpression::gen
     generator.emit_new_function(new_function, *this, lhs_name);
 
     if (has_name) {
-        generator.emit<Bytecode::Op::SetVariable>(*name_identifier, new_function, Bytecode::Op::SetVariable::InitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Lexical);
+        generator.emit<Bytecode::Op::InitializeLexicalBinding>(*name_identifier, new_function);
         generator.end_variable_scope();
     }
 
@@ -1186,7 +1186,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> FunctionExpression::gen
     return generate_bytecode_with_lhs_name(generator, {}, preferred_dst);
 }
 
-static Bytecode::CodeGenerationErrorOr<void> generate_object_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::SetVariable::InitializationMode initialization_mode, ScopedOperand const& object, bool create_variables)
+static Bytecode::CodeGenerationErrorOr<void> generate_object_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::BindingInitializationMode initialization_mode, ScopedOperand const& object, bool create_variables)
 {
     generator.emit<Bytecode::Op::ThrowIfNullish>(object);
 
@@ -1300,7 +1300,7 @@ static Bytecode::CodeGenerationErrorOr<void> generate_object_binding_pattern_byt
     return {};
 }
 
-static Bytecode::CodeGenerationErrorOr<void> 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<ScopedOperand> preferred_dst = {})
+static Bytecode::CodeGenerationErrorOr<void> generate_array_binding_pattern_bytecode(Bytecode::Generator& generator, BindingPattern const& pattern, Bytecode::Op::BindingInitializationMode initialization_mode, ScopedOperand const& input_array, bool create_variables, [[maybe_unused]] Optional<ScopedOperand> preferred_dst = {})
 {
     /*
      * Consider the following destructuring assignment:
@@ -1476,7 +1476,7 @@ static Bytecode::CodeGenerationErrorOr<void> generate_array_binding_pattern_byte
     return {};
 }
 
-Bytecode::CodeGenerationErrorOr<void> BindingPattern::generate_bytecode(Bytecode::Generator& generator, Bytecode::Op::SetVariable::InitializationMode initialization_mode, ScopedOperand const& input_value, bool create_variables) const
+Bytecode::CodeGenerationErrorOr<void> BindingPattern::generate_bytecode(Bytecode::Generator& generator, Bytecode::Op::BindingInitializationMode initialization_mode, ScopedOperand const& input_value, bool create_variables) const
 {
     if (kind == Kind::Object)
         return generate_object_binding_pattern_bytecode(generator, *this, initialization_mode, input_value, create_variables);
@@ -1486,7 +1486,7 @@ Bytecode::CodeGenerationErrorOr<void> BindingPattern::generate_bytecode(Bytecode
 
 static Bytecode::CodeGenerationErrorOr<void> 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;
+    auto initialization_mode = declaration.is_lexical_declaration() ? Bytecode::Op::BindingInitializationMode::Initialize : Bytecode::Op::BindingInitializationMode::Set;
 
     return declarator.target().visit(
         [&](NonnullRefPtr<Identifier const> const& id) -> Bytecode::CodeGenerationErrorOr<void> {
@@ -2569,14 +2569,14 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> TryStatement::generate_
                     did_create_variable_scope_for_catch_clause = true;
                     auto parameter_identifier = generator.intern_identifier(parameter);
                     generator.emit<Bytecode::Op::CreateVariable>(parameter_identifier, Bytecode::Op::EnvironmentMode::Lexical, false);
-                    generator.emit<Bytecode::Op::SetVariable>(parameter_identifier, caught_value, Bytecode::Op::SetVariable::InitializationMode::Initialize);
+                    generator.emit<Bytecode::Op::InitializeLexicalBinding>(parameter_identifier, caught_value);
                 }
                 return {};
             },
             [&](NonnullRefPtr<BindingPattern const> const& binding_pattern) -> Bytecode::CodeGenerationErrorOr<void> {
                 generator.begin_variable_scope();
                 did_create_variable_scope_for_catch_clause = true;
-                TRY(binding_pattern->generate_bytecode(generator, Bytecode::Op::SetVariable::InitializationMode::Initialize, caught_value, true));
+                TRY(binding_pattern->generate_bytecode(generator, Bytecode::Op::BindingInitializationMode::Initialize, caught_value, true));
                 return {};
             }));
 
@@ -2763,7 +2763,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ClassDeclaration::gener
 {
     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);
+    generator.emit_set_variable(*m_class_expression.ptr()->m_name, value, Bytecode::Op::BindingInitializationMode::Initialize);
     // NOTE: ClassDeclaration does not produce a value.
     return Optional<ScopedOperand> {};
 }
@@ -3130,7 +3130,7 @@ static Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> for_in_of_body_e
                     TRY(generator.emit_store_to_reference(**ptr, next_value));
                 } else {
                     auto& binding_pattern = lhs.get<NonnullRefPtr<BindingPattern const>>();
-                    TRY(binding_pattern->generate_bytecode(generator, Bytecode::Op::SetVariable::InitializationMode::Set, next_value, false));
+                    TRY(binding_pattern->generate_bytecode(generator, Bytecode::Op::BindingInitializationMode::Set, next_value, false));
                 }
             }
         }
@@ -3186,7 +3186,7 @@ static Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> for_in_of_body_e
             // 3. Let lhsRef be ! ResolveBinding(lhsName).
             // NOTE: We're skipping all the completion stuff that the spec does, as the unwinding mechanism will take case of doing that.
 
-            generator.emit_set_variable(*lhs_name, next_value, Bytecode::Op::SetVariable::InitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Lexical);
+            generator.emit_set_variable(*lhs_name, next_value, Bytecode::Op::BindingInitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Lexical);
         }
     }
     // i. If destructuring is false, then
@@ -3217,7 +3217,7 @@ static Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> for_in_of_body_e
             auto& binding_pattern = declaration.declarations().first()->target().get<NonnullRefPtr<BindingPattern const>>();
             (void)TRY(binding_pattern->generate_bytecode(
                 generator,
-                head_result.lhs_kind == LHSKind::VarBinding ? Bytecode::Op::SetVariable::InitializationMode::Set : Bytecode::Op::SetVariable::InitializationMode::Initialize,
+                head_result.lhs_kind == LHSKind::VarBinding ? Bytecode::Op::BindingInitializationMode::Set : Bytecode::Op::BindingInitializationMode::Initialize,
                 next_value,
                 false));
         } else {
@@ -3466,10 +3466,9 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ExportStatement::genera
         auto value = TRY(generator.emit_named_evaluation_if_anonymous_function(static_cast<ClassExpression const&>(*m_statement), generator.intern_identifier("default"sv))).value();
 
         if (!static_cast<ClassExpression const&>(*m_statement).has_name()) {
-            generator.emit<Bytecode::Op::SetVariable>(
+            generator.emit<Bytecode::Op::InitializeLexicalBinding>(
                 generator.intern_identifier(ExportStatement::local_name_for_default),
-                value,
-                Bytecode::Op::SetVariable::InitializationMode::Initialize);
+                value);
         }
 
         return value;
@@ -3478,10 +3477,9 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ExportStatement::genera
     // ExportDeclaration : export default AssignmentExpression ;
     VERIFY(is<Expression>(*m_statement));
     auto value = TRY(generator.emit_named_evaluation_if_anonymous_function(static_cast<Expression const&>(*m_statement), generator.intern_identifier("default"sv))).value();
-    generator.emit<Bytecode::Op::SetVariable>(
+    generator.emit<Bytecode::Op::InitializeLexicalBinding>(
         generator.intern_identifier(ExportStatement::local_name_for_default),
-        value,
-        Bytecode::Op::SetVariable::InitializationMode::Initialize);
+        value);
     return value;
 }
 

+ 0 - 23
Userland/Libraries/LibJS/Bytecode/CommonImplementations.h

@@ -399,29 +399,6 @@ inline ThrowCompletionOr<Value> typeof_variable(VM& vm, DeprecatedFlyString cons
     return PrimitiveString::create(vm, value.typeof());
 }
 
-inline ThrowCompletionOr<void> set_variable(
-    VM& vm,
-    DeprecatedFlyString const& name,
-    Value value,
-    Op::EnvironmentMode mode,
-    Op::SetVariable::InitializationMode initialization_mode,
-    EnvironmentCoordinate& cache)
-{
-    auto environment = mode == Op::EnvironmentMode::Lexical ? vm.running_execution_context().lexical_environment : vm.running_execution_context().variable_environment;
-    auto reference = TRY(vm.resolve_binding(name, environment));
-    if (reference.environment_coordinate().has_value())
-        cache = reference.environment_coordinate().value();
-    switch (initialization_mode) {
-    case Op::SetVariable::InitializationMode::Initialize:
-        TRY(reference.initialize_referenced_binding(vm, value));
-        break;
-    case Op::SetVariable::InitializationMode::Set:
-        TRY(reference.put_value(vm, value));
-        break;
-    }
-    return {};
-}
-
 inline Value new_function(VM& vm, FunctionNode const& function_node, Optional<IdentifierTableIndex> const& lhs_name, Optional<Operand> const& home_object)
 {
     Value value;

+ 28 - 12
Userland/Libraries/LibJS/Bytecode/Generator.cpp

@@ -39,7 +39,7 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(E
             auto id = intern_identifier(parameter_name.key);
             emit<Op::CreateVariable>(id, Op::EnvironmentMode::Lexical, false);
             if (function.m_has_duplicates) {
-                emit<Op::SetVariable>(id, add_constant(js_undefined()), Op::SetVariable::InitializationMode::Initialize, Op::EnvironmentMode::Lexical);
+                emit<Op::InitializeLexicalBinding>(id, add_constant(js_undefined()));
             }
         }
     }
@@ -87,17 +87,18 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(E
                 set_local_initialized((*identifier)->local_variable_index());
             } else {
                 auto id = intern_identifier((*identifier)->string());
-                auto init_mode = function.m_has_duplicates ? Op::SetVariable::InitializationMode::Set : Op::SetVariable::InitializationMode::Initialize;
                 auto argument_reg = allocate_register();
                 emit<Op::GetArgument>(argument_reg.operand(), param_index);
-                emit<Op::SetVariable>(id, argument_reg.operand(),
-                    init_mode,
-                    Op::EnvironmentMode::Lexical);
+                if (function.m_has_duplicates) {
+                    emit<Op::SetLexicalBinding>(id, argument_reg.operand());
+                } else {
+                    emit<Op::InitializeLexicalBinding>(id, argument_reg.operand());
+                }
             }
         } else if (auto const* binding_pattern = parameter.binding.get_pointer<NonnullRefPtr<BindingPattern const>>(); binding_pattern) {
             auto input_operand = allocate_register();
             emit<Op::GetArgument>(input_operand.operand(), param_index);
-            auto init_mode = function.m_has_duplicates ? Op::SetVariable::InitializationMode::Set : Bytecode::Op::SetVariable::InitializationMode::Initialize;
+            auto init_mode = function.m_has_duplicates ? Op::BindingInitializationMode::Set : Bytecode::Op::BindingInitializationMode::Initialize;
             TRY((*binding_pattern)->generate_bytecode(*this, init_mode, input_operand, false));
         }
     }
@@ -115,7 +116,7 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(E
                 } else {
                     auto intern_id = intern_identifier(id.string());
                     emit<Op::CreateVariable>(intern_id, Op::EnvironmentMode::Var, false);
-                    emit<Op::SetVariable>(intern_id, add_constant(js_undefined()), Bytecode::Op::SetVariable::InitializationMode::Initialize, Op::EnvironmentMode::Var);
+                    emit<Op::InitializeVariableBinding>(intern_id, add_constant(js_undefined()));
                 }
             }
         }
@@ -141,7 +142,7 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(E
                 } else {
                     auto intern_id = intern_identifier(id.string());
                     emit<Op::CreateVariable>(intern_id, Op::EnvironmentMode::Var, false);
-                    emit<Op::SetVariable>(intern_id, initial_value, Op::SetVariable::InitializationMode::Initialize, Op::EnvironmentMode::Var);
+                    emit<Op::InitializeVariableBinding>(intern_id, initial_value);
                 }
             }
         }
@@ -151,7 +152,7 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(E
         for (auto const& function_name : function.m_function_names_to_initialize_binding) {
             auto intern_id = intern_identifier(function_name);
             emit<Op::CreateVariable>(intern_id, Op::EnvironmentMode::Var, false);
-            emit<Op::SetVariable>(intern_id, add_constant(js_undefined()), Bytecode::Op::SetVariable::InitializationMode::Initialize, Op::EnvironmentMode::Var);
+            emit<Op::InitializeVariableBinding>(intern_id, add_constant(js_undefined()));
         }
     }
 
@@ -184,7 +185,7 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(E
         if (declaration.name_identifier()->is_local()) {
             emit<Op::Mov>(local(declaration.name_identifier()->local_variable_index()), function);
         } else {
-            emit<Op::SetVariable>(intern_identifier(declaration.name()), function, Op::SetVariable::InitializationMode::Set, Op::EnvironmentMode::Var);
+            emit<Op::SetVariableBinding>(intern_identifier(declaration.name()), function);
         }
     }
 
@@ -808,7 +809,7 @@ CodeGenerationErrorOr<Optional<ScopedOperand>> Generator::emit_delete_reference(
     return add_constant(Value(true));
 }
 
-void Generator::emit_set_variable(JS::Identifier const& identifier, ScopedOperand value, Bytecode::Op::SetVariable::InitializationMode initialization_mode, Bytecode::Op::EnvironmentMode mode)
+void Generator::emit_set_variable(JS::Identifier const& identifier, ScopedOperand value, Bytecode::Op::BindingInitializationMode initialization_mode, Bytecode::Op::EnvironmentMode environment_mode)
 {
     if (identifier.is_local()) {
         if (value.operand().is_local() && value.operand().index() == identifier.local_variable_index()) {
@@ -817,7 +818,22 @@ void Generator::emit_set_variable(JS::Identifier const& identifier, ScopedOperan
         }
         emit<Bytecode::Op::Mov>(local(identifier.local_variable_index()), value);
     } else {
-        emit<Bytecode::Op::SetVariable>(intern_identifier(identifier.string()), value, initialization_mode, mode);
+        auto identifier_index = intern_identifier(identifier.string());
+        if (environment_mode == Bytecode::Op::EnvironmentMode::Lexical) {
+            if (initialization_mode == Bytecode::Op::BindingInitializationMode::Initialize) {
+                emit<Bytecode::Op::InitializeLexicalBinding>(identifier_index, value);
+            } else if (initialization_mode == Bytecode::Op::BindingInitializationMode::Set) {
+                emit<Bytecode::Op::SetLexicalBinding>(identifier_index, value);
+            }
+        } else if (environment_mode == Bytecode::Op::EnvironmentMode::Var) {
+            if (initialization_mode == Bytecode::Op::BindingInitializationMode::Initialize) {
+                emit<Bytecode::Op::InitializeVariableBinding>(identifier_index, value);
+            } else if (initialization_mode == Bytecode::Op::BindingInitializationMode::Set) {
+                emit<Bytecode::Op::SetVariableBinding>(identifier_index, value);
+            }
+        } else {
+            VERIFY_NOT_REACHED();
+        }
     }
 }
 

+ 1 - 1
Userland/Libraries/LibJS/Bytecode/Generator.h

@@ -144,7 +144,7 @@ public:
 
     CodeGenerationErrorOr<ReferenceOperands> emit_super_reference(MemberExpression const&);
 
-    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 emit_set_variable(JS::Identifier const& identifier, ScopedOperand value, Bytecode::Op::BindingInitializationMode initialization_mode = Bytecode::Op::BindingInitializationMode::Set, Bytecode::Op::EnvironmentMode mode = Bytecode::Op::EnvironmentMode::Lexical);
 
     void push_home_object(ScopedOperand);
     void pop_home_object();

+ 4 - 1
Userland/Libraries/LibJS/Bytecode/Instruction.h

@@ -70,6 +70,8 @@
     O(ImportCall)                      \
     O(In)                              \
     O(Increment)                       \
+    O(InitializeLexicalBinding)        \
+    O(InitializeVariableBinding)       \
     O(InstanceOf)                      \
     O(IteratorClose)                   \
     O(IteratorNext)                    \
@@ -122,7 +124,8 @@
     O(RightShift)                      \
     O(ScheduleJump)                    \
     O(SetArgument)                     \
-    O(SetVariable)                     \
+    O(SetLexicalBinding)               \
+    O(SetVariableBinding)              \
     O(StrictlyEquals)                  \
     O(StrictlyInequals)                \
     O(Sub)                             \

+ 69 - 26
Userland/Libraries/LibJS/Bytecode/Interpreter.cpp

@@ -584,6 +584,8 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
             HANDLE_INSTRUCTION(ImportCall);
             HANDLE_INSTRUCTION(In);
             HANDLE_INSTRUCTION(Increment);
+            HANDLE_INSTRUCTION(InitializeLexicalBinding);
+            HANDLE_INSTRUCTION(InitializeVariableBinding);
             HANDLE_INSTRUCTION(InstanceOf);
             HANDLE_INSTRUCTION(IteratorClose);
             HANDLE_INSTRUCTION(IteratorNext);
@@ -618,7 +620,8 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
             HANDLE_INSTRUCTION(ResolveThisBinding);
             HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(RestoreScheduledJump);
             HANDLE_INSTRUCTION(RightShift);
-            HANDLE_INSTRUCTION(SetVariable);
+            HANDLE_INSTRUCTION(SetLexicalBinding);
+            HANDLE_INSTRUCTION(SetVariableBinding);
             HANDLE_INSTRUCTION(StrictlyEquals);
             HANDLE_INSTRUCTION(StrictlyInequals);
             HANDLE_INSTRUCTION(Sub);
@@ -1376,38 +1379,60 @@ ThrowCompletionOr<void> CreateArguments::execute_impl(Bytecode::Interpreter& int
     return {};
 }
 
-ThrowCompletionOr<void> SetVariable::execute_impl(Bytecode::Interpreter& interpreter) const
+template<EnvironmentMode environment_mode, BindingInitializationMode initialization_mode>
+static ThrowCompletionOr<void> initialize_or_set_binding(Interpreter& interpreter, IdentifierTableIndex identifier_index, Value value, EnvironmentCoordinate& cache)
 {
     auto& vm = interpreter.vm();
 
-    if (m_cache.is_valid()) {
-        auto* environment = m_mode == EnvironmentMode::Lexical
-            ? interpreter.running_execution_context().lexical_environment.ptr()
-            : interpreter.running_execution_context().variable_environment.ptr();
-        for (size_t i = 0; i < m_cache.hops; ++i)
+    auto* environment = environment_mode == EnvironmentMode::Lexical
+        ? interpreter.running_execution_context().lexical_environment.ptr()
+        : interpreter.running_execution_context().variable_environment.ptr();
+
+    if (cache.is_valid()) {
+        for (size_t i = 0; i < cache.hops; ++i)
             environment = environment->outer_environment();
         if (!environment->is_permanently_screwed_by_eval()) {
-            auto value = interpreter.get(src());
-            if (m_initialization_mode == InitializationMode::Initialize) {
-                TRY(static_cast<DeclarativeEnvironment&>(*environment).initialize_binding_direct(vm, m_cache.index, value, Environment::InitializeBindingHint::Normal));
-                return {};
+            if constexpr (initialization_mode == BindingInitializationMode::Initialize) {
+                TRY(static_cast<DeclarativeEnvironment&>(*environment).initialize_binding_direct(vm, cache.index, value, Environment::InitializeBindingHint::Normal));
+            } else {
+                TRY(static_cast<DeclarativeEnvironment&>(*environment).set_mutable_binding_direct(vm, cache.index, value, vm.in_strict_mode()));
             }
-            TRY(static_cast<DeclarativeEnvironment&>(*environment).set_mutable_binding_direct(vm, m_cache.index, value, vm.in_strict_mode()));
             return {};
         }
-        m_cache = {};
+        cache = {};
     }
 
-    auto const& name = interpreter.current_executable().get_identifier(m_identifier);
-    TRY(set_variable(vm,
-        name,
-        interpreter.get(src()),
-        m_mode,
-        m_initialization_mode,
-        m_cache));
+    auto reference = TRY(vm.resolve_binding(interpreter.current_executable().get_identifier(identifier_index), environment));
+    if (reference.environment_coordinate().has_value())
+        cache = reference.environment_coordinate().value();
+    if constexpr (initialization_mode == BindingInitializationMode::Initialize) {
+        TRY(reference.initialize_referenced_binding(vm, value));
+    } else if (initialization_mode == BindingInitializationMode::Set) {
+        TRY(reference.put_value(vm, value));
+    }
     return {};
 }
 
+ThrowCompletionOr<void> InitializeLexicalBinding::execute_impl(Bytecode::Interpreter& interpreter) const
+{
+    return initialize_or_set_binding<EnvironmentMode::Lexical, BindingInitializationMode::Initialize>(interpreter, m_identifier, interpreter.get(m_src), m_cache);
+}
+
+ThrowCompletionOr<void> InitializeVariableBinding::execute_impl(Bytecode::Interpreter& interpreter) const
+{
+    return initialize_or_set_binding<EnvironmentMode::Var, BindingInitializationMode::Initialize>(interpreter, m_identifier, interpreter.get(m_src), m_cache);
+}
+
+ThrowCompletionOr<void> SetLexicalBinding::execute_impl(Bytecode::Interpreter& interpreter) const
+{
+    return initialize_or_set_binding<EnvironmentMode::Lexical, BindingInitializationMode::Set>(interpreter, m_identifier, interpreter.get(m_src), m_cache);
+}
+
+ThrowCompletionOr<void> SetVariableBinding::execute_impl(Bytecode::Interpreter& interpreter) const
+{
+    return initialize_or_set_binding<EnvironmentMode::Var, BindingInitializationMode::Set>(interpreter, m_identifier, interpreter.get(m_src), m_cache);
+}
+
 ThrowCompletionOr<void> SetArgument::execute_impl(Bytecode::Interpreter&) const
 {
     // Handled in the interpreter loop.
@@ -2152,14 +2177,32 @@ ByteString EnterObjectEnvironment::to_byte_string_impl(Executable const& executa
         format_operand("object"sv, m_object, executable));
 }
 
-ByteString SetVariable::to_byte_string_impl(Bytecode::Executable const& executable) const
+ByteString InitializeLexicalBinding::to_byte_string_impl(Bytecode::Executable const& executable) const
 {
-    auto initialization_mode_name = m_initialization_mode == InitializationMode::Initialize ? "Initialize" : "Set";
-    auto mode_string = m_mode == EnvironmentMode::Lexical ? "Lexical" : "Variable";
-    return ByteString::formatted("SetVariable {}, {}, env:{} init:{}",
+    return ByteString::formatted("InitializeLexicalBinding {}, {}",
         executable.identifier_table->get(m_identifier),
-        format_operand("src"sv, src(), executable),
-        mode_string, initialization_mode_name);
+        format_operand("src"sv, src(), executable));
+}
+
+ByteString InitializeVariableBinding::to_byte_string_impl(Bytecode::Executable const& executable) const
+{
+    return ByteString::formatted("InitializeVariableBinding {}, {}",
+        executable.identifier_table->get(m_identifier),
+        format_operand("src"sv, src(), executable));
+}
+
+ByteString SetLexicalBinding::to_byte_string_impl(Bytecode::Executable const& executable) const
+{
+    return ByteString::formatted("SetLexicalBinding {}, {}",
+        executable.identifier_table->get(m_identifier),
+        format_operand("src"sv, src(), executable));
+}
+
+ByteString SetVariableBinding::to_byte_string_impl(Bytecode::Executable const& executable) const
+{
+    return ByteString::formatted("SetVariableBinding {}, {}",
+        executable.identifier_table->get(m_identifier),
+        format_operand("src"sv, src(), executable));
 }
 
 ByteString GetArgument::to_byte_string_impl(Bytecode::Executable const& executable) const

+ 83 - 13
Userland/Libraries/LibJS/Bytecode/Op.h

@@ -528,6 +528,11 @@ enum class EnvironmentMode {
     Var,
 };
 
+enum class BindingInitializationMode {
+    Initialize,
+    Set,
+};
+
 class CreateLexicalEnvironment final : public Instruction {
 public:
     explicit CreateLexicalEnvironment(u32 capacity = 0)
@@ -664,18 +669,87 @@ private:
     bool m_is_strict { false };
 };
 
-class SetVariable final : public Instruction {
+class InitializeLexicalBinding final : public Instruction {
 public:
-    enum class InitializationMode {
-        Initialize,
-        Set,
-    };
-    explicit SetVariable(IdentifierTableIndex identifier, Operand src, InitializationMode initialization_mode = InitializationMode::Set, EnvironmentMode mode = EnvironmentMode::Lexical)
-        : Instruction(Type::SetVariable)
+    explicit InitializeLexicalBinding(IdentifierTableIndex identifier, Operand src)
+        : Instruction(Type::InitializeLexicalBinding)
+        , m_identifier(identifier)
+        , m_src(src)
+    {
+    }
+
+    ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
+    ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_src);
+    }
+
+    IdentifierTableIndex identifier() const { return m_identifier; }
+    Operand src() const { return m_src; }
+
+private:
+    IdentifierTableIndex m_identifier;
+    Operand m_src;
+    mutable EnvironmentCoordinate m_cache;
+};
+
+class InitializeVariableBinding final : public Instruction {
+public:
+    explicit InitializeVariableBinding(IdentifierTableIndex identifier, Operand src)
+        : Instruction(Type::InitializeVariableBinding)
+        , m_identifier(identifier)
+        , m_src(src)
+    {
+    }
+
+    ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
+    ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_src);
+    }
+
+    IdentifierTableIndex identifier() const { return m_identifier; }
+    Operand src() const { return m_src; }
+
+private:
+    IdentifierTableIndex m_identifier;
+    Operand m_src;
+    mutable EnvironmentCoordinate m_cache;
+};
+
+class SetLexicalBinding final : public Instruction {
+public:
+    explicit SetLexicalBinding(IdentifierTableIndex identifier, Operand src)
+        : Instruction(Type::SetLexicalBinding)
+        , m_identifier(identifier)
+        , m_src(src)
+    {
+    }
+
+    ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
+    ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_operands_impl(Function<void(Operand&)> visitor)
+    {
+        visitor(m_src);
+    }
+
+    IdentifierTableIndex identifier() const { return m_identifier; }
+    Operand src() const { return m_src; }
+
+private:
+    IdentifierTableIndex m_identifier;
+    Operand m_src;
+    mutable EnvironmentCoordinate m_cache;
+};
+
+class SetVariableBinding final : public Instruction {
+public:
+    explicit SetVariableBinding(IdentifierTableIndex identifier, Operand src)
+        : Instruction(Type::SetVariableBinding)
         , m_identifier(identifier)
         , m_src(src)
-        , m_mode(mode)
-        , m_initialization_mode(initialization_mode)
     {
     }
 
@@ -688,14 +762,10 @@ public:
 
     IdentifierTableIndex identifier() const { return m_identifier; }
     Operand src() const { return m_src; }
-    EnvironmentMode mode() const { return m_mode; }
-    InitializationMode initialization_mode() const { return m_initialization_mode; }
 
 private:
     IdentifierTableIndex m_identifier;
     Operand m_src;
-    EnvironmentMode m_mode;
-    InitializationMode m_initialization_mode { InitializationMode::Set };
     mutable EnvironmentCoordinate m_cache;
 };