Переглянути джерело

LibJS/JIT: Add fastpath for set variable

Stephan Vedder 1 рік тому
батько
коміт
84eecbb10e

+ 5 - 5
Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp

@@ -1080,7 +1080,7 @@ Bytecode::CodeGenerationErrorOr<void> FunctionDeclaration::generate_bytecode(Byt
         Bytecode::Generator::SourceLocationScope scope(generator, *this);
         auto index = generator.intern_identifier(name());
         generator.emit<Bytecode::Op::GetVariable>(index, generator.next_environment_variable_cache());
-        generator.emit<Bytecode::Op::SetVariable>(index, Bytecode::Op::SetVariable::InitializationMode::Set, Bytecode::Op::EnvironmentMode::Var);
+        generator.emit<Bytecode::Op::SetVariable>(index, generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Set, Bytecode::Op::EnvironmentMode::Var);
     }
     return {};
 }
@@ -1101,7 +1101,7 @@ Bytecode::CodeGenerationErrorOr<void> FunctionExpression::generate_bytecode_with
     generator.emit_new_function(*this, lhs_name);
 
     if (has_name) {
-        generator.emit<Bytecode::Op::SetVariable>(*name_identifier, Bytecode::Op::SetVariable::InitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Lexical);
+        generator.emit<Bytecode::Op::SetVariable>(*name_identifier, generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Lexical);
         generator.end_variable_scope();
     }
 
@@ -2368,7 +2368,7 @@ Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode::
                     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, Bytecode::Op::SetVariable::InitializationMode::Initialize);
+                    generator.emit<Bytecode::Op::SetVariable>(parameter_identifier, generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize);
                 }
                 return {};
             },
@@ -3196,7 +3196,7 @@ Bytecode::CodeGenerationErrorOr<void> ExportStatement::generate_bytecode(Bytecod
         TRY(generator.emit_named_evaluation_if_anonymous_function(static_cast<ClassExpression const&>(*m_statement), generator.intern_identifier("default"sv)));
 
         if (!static_cast<ClassExpression const&>(*m_statement).has_name())
-            generator.emit<Bytecode::Op::SetVariable>(generator.intern_identifier(ExportStatement::local_name_for_default), Bytecode::Op::SetVariable::InitializationMode::Initialize);
+            generator.emit<Bytecode::Op::SetVariable>(generator.intern_identifier(ExportStatement::local_name_for_default), generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize);
 
         return {};
     }
@@ -3204,7 +3204,7 @@ Bytecode::CodeGenerationErrorOr<void> ExportStatement::generate_bytecode(Bytecod
     // ExportDeclaration : export default AssignmentExpression ;
     VERIFY(is<Expression>(*m_statement));
     TRY(generator.emit_named_evaluation_if_anonymous_function(static_cast<Expression const&>(*m_statement), generator.intern_identifier("default"sv)));
-    generator.emit<Bytecode::Op::SetVariable>(generator.intern_identifier(ExportStatement::local_name_for_default), Bytecode::Op::SetVariable::InitializationMode::Initialize);
+    generator.emit<Bytecode::Op::SetVariable>(generator.intern_identifier(ExportStatement::local_name_for_default), generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize);
     return {};
 }
 

+ 4 - 1
Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp

@@ -264,10 +264,13 @@ ThrowCompletionOr<void> set_variable(
     DeprecatedFlyString const& name,
     Value value,
     Op::EnvironmentMode mode,
-    Op::SetVariable::InitializationMode initialization_mode)
+    Op::SetVariable::InitializationMode initialization_mode,
+    EnvironmentVariableCache& 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();
     switch (initialization_mode) {
     case Op::SetVariable::InitializationMode::Initialize:
         TRY(reference.initialize_referenced_binding(vm, value));

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

@@ -20,7 +20,7 @@ ThrowCompletionOr<void> put_by_property_key(VM&, Value base, Value this_value, V
 ThrowCompletionOr<Value> perform_call(Interpreter&, Value this_value, Op::CallType, Value callee, MarkedVector<Value> argument_values);
 ThrowCompletionOr<void> throw_if_needed_for_call(Interpreter&, Value callee, Op::CallType, Optional<StringTableIndex> const& expression_string);
 ThrowCompletionOr<Value> typeof_variable(VM&, DeprecatedFlyString const&);
-ThrowCompletionOr<void> set_variable(VM&, DeprecatedFlyString const&, Value, Op::EnvironmentMode, Op::SetVariable::InitializationMode);
+ThrowCompletionOr<void> set_variable(VM&, DeprecatedFlyString const&, Value, Op::EnvironmentMode, Op::SetVariable::InitializationMode, EnvironmentVariableCache&);
 Value new_function(VM&, FunctionExpression const&, Optional<IdentifierTableIndex> const& lhs_name, Optional<Register> const& home_object);
 ThrowCompletionOr<void> put_by_value(VM&, Value base, Value property_key_value, Value value, Op::PropertyKind);
 ThrowCompletionOr<Value> get_variable(Bytecode::Interpreter&, DeprecatedFlyString const& name, EnvironmentVariableCache&);

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

@@ -414,7 +414,7 @@ void Generator::emit_set_variable(JS::Identifier const& identifier, Bytecode::Op
     if (identifier.is_local()) {
         emit<Bytecode::Op::SetLocal>(identifier.local_variable_index());
     } else {
-        emit<Bytecode::Op::SetVariable>(intern_identifier(identifier.string()), initialization_mode, mode);
+        emit<Bytecode::Op::SetVariable>(intern_identifier(identifier.string()), next_environment_variable_cache(), initialization_mode, mode);
     }
 }
 

+ 6 - 1
Userland/Libraries/LibJS/Bytecode/Interpreter.cpp

@@ -780,7 +780,12 @@ ThrowCompletionOr<void> SetVariable::execute_impl(Bytecode::Interpreter& interpr
 {
     auto& vm = interpreter.vm();
     auto const& name = interpreter.current_executable().get_identifier(m_identifier);
-    TRY(set_variable(vm, name, interpreter.accumulator(), m_mode, m_initialization_mode));
+    TRY(set_variable(vm,
+        name,
+        interpreter.accumulator(),
+        m_mode,
+        m_initialization_mode,
+        interpreter.current_executable().environment_variable_caches[m_cache_index]));
     return {};
 }
 

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

@@ -472,11 +472,12 @@ public:
         Initialize,
         Set,
     };
-    explicit SetVariable(IdentifierTableIndex identifier, InitializationMode initialization_mode = InitializationMode::Set, EnvironmentMode mode = EnvironmentMode::Lexical)
+    explicit SetVariable(IdentifierTableIndex identifier, u32 cache_index, InitializationMode initialization_mode = InitializationMode::Set, EnvironmentMode mode = EnvironmentMode::Lexical)
         : Instruction(Type::SetVariable, sizeof(*this))
         , m_identifier(identifier)
         , m_mode(mode)
         , m_initialization_mode(initialization_mode)
+        , m_cache_index(cache_index)
     {
     }
 
@@ -486,11 +487,13 @@ public:
     IdentifierTableIndex identifier() const { return m_identifier; }
     EnvironmentMode mode() const { return m_mode; }
     InitializationMode initialization_mode() const { return m_initialization_mode; }
+    u32 cache_index() const { return m_cache_index; }
 
 private:
     IdentifierTableIndex m_identifier;
     EnvironmentMode m_mode;
     InitializationMode m_initialization_mode { InitializationMode::Set };
+    u32 m_cache_index { 0 };
 };
 
 class SetLocal final : public Instruction {

+ 126 - 2
Userland/Libraries/LibJS/JIT/Compiler.cpp

@@ -2736,18 +2736,140 @@ static Value cxx_set_variable(
     DeprecatedFlyString const& identifier,
     Value value,
     Bytecode::Op::EnvironmentMode environment_mode,
-    Bytecode::Op::SetVariable::InitializationMode initialization_mode)
+    Bytecode::Op::SetVariable::InitializationMode initialization_mode,
+    Bytecode::EnvironmentVariableCache& cache)
 {
-    TRY_OR_SET_EXCEPTION(Bytecode::set_variable(vm, identifier, value, environment_mode, initialization_mode));
+    TRY_OR_SET_EXCEPTION(Bytecode::set_variable(vm, identifier, value, environment_mode, initialization_mode, cache));
     return {};
 }
 
 void Compiler::compile_set_variable(Bytecode::Op::SetVariable const& op)
 {
+    Assembler::Label slow_case;
+
+    // Load the identifier in ARG1 for both cases
     m_assembler.mov(
         Assembler::Operand::Register(ARG1),
         Assembler::Operand::Imm(bit_cast<u64>(&m_bytecode_executable.get_identifier(op.identifier().value()))));
+
+    // Load the value in ARG2 for both cases
     load_accumulator(ARG2);
+
+    // if (!cache.has_value()) goto slow_case;
+    m_assembler.mov(
+        Assembler::Operand::Register(ARG5),
+        Assembler::Operand::Imm(bit_cast<u64>(&m_bytecode_executable.environment_variable_caches[op.cache_index()])));
+
+    m_assembler.mov8(
+        Assembler::Operand::Register(GPR0),
+        Assembler::Operand::Mem64BaseAndOffset(ARG5, Bytecode::EnvironmentVariableCache::has_value_offset()));
+
+    m_assembler.jump_if(
+        Assembler::Operand::Register(GPR0),
+        Assembler::Condition::EqualTo,
+        Assembler::Operand::Imm(0),
+        slow_case);
+
+    if (op.mode() == Bytecode::Op::EnvironmentMode::Lexical) {
+        // auto environment = vm.running_execution_context().lexical_environment;
+        // GPR1 = current lexical environment
+        m_assembler.mov(
+            Assembler::Operand::Register(GPR1),
+            Assembler::Operand::Mem64BaseAndOffset(RUNNING_EXECUTION_CONTEXT_BASE, ExecutionContext::lexical_environment_offset()));
+    } else {
+        // auto environment = vm.running_execution_context().variable_environment;
+        // GPR1 = current variable environment
+        m_assembler.mov(
+            Assembler::Operand::Register(GPR1),
+            Assembler::Operand::Mem64BaseAndOffset(RUNNING_EXECUTION_CONTEXT_BASE, ExecutionContext::variable_environment_offset()));
+    }
+
+    // for (size_t i = 0; i < cache->hops; ++i)
+    //     environment = environment->outer_environment();
+
+    // GPR0 = hops
+    m_assembler.mov32(
+        Assembler::Operand::Register(GPR0),
+        Assembler::Operand::Mem64BaseAndOffset(ARG5, Bytecode::EnvironmentVariableCache::value_offset() + EnvironmentCoordinate::hops_offset()));
+
+    {
+        // while (GPR0--)
+        //     GPR1 = GPR1->outer_environment()
+        Assembler::Label loop_start;
+        Assembler::Label loop_end;
+        loop_start.link(m_assembler);
+        m_assembler.jump_if(
+            Assembler::Operand::Register(GPR0),
+            Assembler::Condition::EqualTo,
+            Assembler::Operand::Imm(0),
+            loop_end);
+        m_assembler.sub(
+            Assembler::Operand::Register(GPR0),
+            Assembler::Operand::Imm(1));
+        m_assembler.mov(
+            Assembler::Operand::Register(GPR1),
+            Assembler::Operand::Mem64BaseAndOffset(GPR1, Environment::outer_environment_offset()));
+        m_assembler.jump(loop_start);
+        loop_end.link(m_assembler);
+    }
+
+    // GPR1 now points to the environment holding our binding.
+
+    // if (environment->is_permanently_screwed_by_eval()) goto slow_case;
+    m_assembler.mov8(
+        Assembler::Operand::Register(GPR0),
+        Assembler::Operand::Mem64BaseAndOffset(GPR1, Environment::is_permanently_screwed_by_eval_offset()));
+    m_assembler.jump_if(
+        Assembler::Operand::Register(GPR0),
+        Assembler::Condition::NotEqualTo,
+        Assembler::Operand::Imm(0),
+        slow_case);
+
+    // GPR1 = environment->m_bindings.outline_buffer()
+    m_assembler.mov(
+        Assembler::Operand::Register(GPR1),
+        Assembler::Operand::Mem64BaseAndOffset(GPR1, DeclarativeEnvironment::bindings_offset() + Vector<DeclarativeEnvironment::Binding>::outline_buffer_offset()));
+
+    // GPR0 = index
+    m_assembler.mov32(
+        Assembler::Operand::Register(GPR0),
+        Assembler::Operand::Mem64BaseAndOffset(ARG5, Bytecode::EnvironmentVariableCache::value_offset() + EnvironmentCoordinate::index_offset()));
+
+    // GPR0 *= sizeof(DeclarativeEnvironment::Binding)
+    m_assembler.mul32(
+        Assembler::Operand::Register(GPR0),
+        Assembler::Operand::Imm(sizeof(DeclarativeEnvironment::Binding)),
+        slow_case);
+
+    // GPR1 = &binding
+    m_assembler.add(
+        Assembler::Operand::Register(GPR1),
+        Assembler::Operand::Register(GPR0));
+
+    // if (!binding.initialized) goto slow_case;
+    m_assembler.mov(
+        Assembler ::Operand::Register(GPR0),
+        Assembler::Operand::Mem64BaseAndOffset(GPR1, DeclarativeEnvironment::Binding::initialized_offset()));
+    m_assembler.bitwise_and(
+        Assembler::Operand::Register(GPR0),
+        Assembler::Operand::Imm(0xff));
+    m_assembler.jump_if(
+        Assembler::Operand::Register(GPR0),
+        Assembler::Condition::EqualTo,
+        Assembler::Operand::Imm(0),
+        slow_case);
+
+    // binding.value = accumulator;
+    m_assembler.mov(
+        Assembler::Operand::Mem64BaseAndOffset(GPR1, DeclarativeEnvironment::Binding::value_offset()),
+        Assembler::Operand::Register(ARG2));
+
+    Assembler::Label end;
+    m_assembler.jump(end);
+
+    // Slow case: Uncached access. Call C++ helper.
+    slow_case.link(m_assembler);
+
     m_assembler.mov(
         Assembler::Operand::Register(ARG3),
         Assembler::Operand::Imm(to_underlying(op.mode())));
@@ -2756,6 +2878,8 @@ void Compiler::compile_set_variable(Bytecode::Op::SetVariable const& op)
         Assembler::Operand::Imm(to_underlying(op.initialization_mode())));
     native_call((void*)cxx_set_variable);
     check_exception();
+
+    end.link(m_assembler);
 }
 
 void Compiler::compile_continue_pending_unwind(Bytecode::Op::ContinuePendingUnwind const& op)

+ 1 - 0
Userland/Libraries/LibJS/Runtime/ExecutionContext.h

@@ -32,6 +32,7 @@ struct ExecutionContext {
 
     static FlatPtr realm_offset() { return OFFSET_OF(ExecutionContext, realm); }
     static FlatPtr lexical_environment_offset() { return OFFSET_OF(ExecutionContext, lexical_environment); }
+    static FlatPtr variable_environment_offset() { return OFFSET_OF(ExecutionContext, variable_environment); }
 
 private:
     explicit ExecutionContext(MarkedVector<Value> existing_arguments, MarkedVector<Value> existing_local_variables);