diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 9c8fc3303b..28586c9cb8 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -1080,7 +1080,7 @@ Bytecode::CodeGenerationErrorOr FunctionDeclaration::generate_bytecode(Byt Bytecode::Generator::SourceLocationScope scope(generator, *this); auto index = generator.intern_identifier(name()); generator.emit(index, generator.next_environment_variable_cache()); - generator.emit(index, Bytecode::Op::SetVariable::InitializationMode::Set, Bytecode::Op::EnvironmentMode::Var); + generator.emit(index, generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Set, Bytecode::Op::EnvironmentMode::Var); } return {}; } @@ -1101,7 +1101,7 @@ Bytecode::CodeGenerationErrorOr FunctionExpression::generate_bytecode_with generator.emit_new_function(*this, lhs_name); if (has_name) { - generator.emit(*name_identifier, Bytecode::Op::SetVariable::InitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Lexical); + generator.emit(*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 TryStatement::generate_bytecode(Bytecode:: did_create_variable_scope_for_catch_clause = true; auto parameter_identifier = generator.intern_identifier(parameter); generator.emit(parameter_identifier, Bytecode::Op::EnvironmentMode::Lexical, false); - generator.emit(parameter_identifier, Bytecode::Op::SetVariable::InitializationMode::Initialize); + generator.emit(parameter_identifier, generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize); } return {}; }, @@ -3196,7 +3196,7 @@ Bytecode::CodeGenerationErrorOr ExportStatement::generate_bytecode(Bytecod TRY(generator.emit_named_evaluation_if_anonymous_function(static_cast(*m_statement), generator.intern_identifier("default"sv))); if (!static_cast(*m_statement).has_name()) - generator.emit(generator.intern_identifier(ExportStatement::local_name_for_default), Bytecode::Op::SetVariable::InitializationMode::Initialize); + generator.emit(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 ExportStatement::generate_bytecode(Bytecod // ExportDeclaration : export default AssignmentExpression ; VERIFY(is(*m_statement)); TRY(generator.emit_named_evaluation_if_anonymous_function(static_cast(*m_statement), generator.intern_identifier("default"sv))); - generator.emit(generator.intern_identifier(ExportStatement::local_name_for_default), Bytecode::Op::SetVariable::InitializationMode::Initialize); + generator.emit(generator.intern_identifier(ExportStatement::local_name_for_default), generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize); return {}; } diff --git a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp index 8e5d9a15b2..2d6866b24e 100644 --- a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp +++ b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp @@ -264,10 +264,13 @@ ThrowCompletionOr 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)); diff --git a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h index 94725a5ed0..829a056388 100644 --- a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h +++ b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h @@ -20,7 +20,7 @@ ThrowCompletionOr put_by_property_key(VM&, Value base, Value this_value, V ThrowCompletionOr perform_call(Interpreter&, Value this_value, Op::CallType, Value callee, MarkedVector argument_values); ThrowCompletionOr throw_if_needed_for_call(Interpreter&, Value callee, Op::CallType, Optional const& expression_string); ThrowCompletionOr typeof_variable(VM&, DeprecatedFlyString const&); -ThrowCompletionOr set_variable(VM&, DeprecatedFlyString const&, Value, Op::EnvironmentMode, Op::SetVariable::InitializationMode); +ThrowCompletionOr set_variable(VM&, DeprecatedFlyString const&, Value, Op::EnvironmentMode, Op::SetVariable::InitializationMode, EnvironmentVariableCache&); Value new_function(VM&, FunctionExpression const&, Optional const& lhs_name, Optional const& home_object); ThrowCompletionOr put_by_value(VM&, Value base, Value property_key_value, Value value, Op::PropertyKind); ThrowCompletionOr get_variable(Bytecode::Interpreter&, DeprecatedFlyString const& name, EnvironmentVariableCache&); diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.cpp b/Userland/Libraries/LibJS/Bytecode/Generator.cpp index 2d555df905..044d3622df 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.cpp +++ b/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(identifier.local_variable_index()); } else { - emit(intern_identifier(identifier.string()), initialization_mode, mode); + emit(intern_identifier(identifier.string()), next_environment_variable_cache(), initialization_mode, mode); } } diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index fae1176ef5..eda879143f 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -780,7 +780,12 @@ ThrowCompletionOr 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 {}; } diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index 78a91d762f..abee872568 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/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 { diff --git a/Userland/Libraries/LibJS/JIT/Compiler.cpp b/Userland/Libraries/LibJS/JIT/Compiler.cpp index 282e9f6488..9b461f92c6 100644 --- a/Userland/Libraries/LibJS/JIT/Compiler.cpp +++ b/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(&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(&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::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) diff --git a/Userland/Libraries/LibJS/Runtime/ExecutionContext.h b/Userland/Libraries/LibJS/Runtime/ExecutionContext.h index 858e2508b1..9c710e8ecb 100644 --- a/Userland/Libraries/LibJS/Runtime/ExecutionContext.h +++ b/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 existing_arguments, MarkedVector existing_local_variables);