diff --git a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp index 1a5f94063c..2048213c60 100644 --- a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp +++ b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp @@ -6,6 +6,9 @@ #include #include +#include +#include +#include namespace JS::Bytecode { @@ -86,4 +89,54 @@ ThrowCompletionOr get_by_value(Bytecode::Interpreter& interpreter, Value return TRY(object->internal_get(property_key, base_value)); } +ThrowCompletionOr get_global(Bytecode::Interpreter& interpreter, IdentifierTableIndex identifier, u32 cache_index) +{ + auto& vm = interpreter.vm(); + auto& realm = *vm.current_realm(); + + auto& cache = interpreter.current_executable().global_variable_caches[cache_index]; + auto& binding_object = realm.global_environment().object_record().binding_object(); + auto& declarative_record = realm.global_environment().declarative_record(); + + // OPTIMIZATION: If the shape of the object hasn't changed, we can use the cached property offset. + // NOTE: Unique shapes don't change identity, so we compare their serial numbers instead. + auto& shape = binding_object.shape(); + if (cache.environment_serial_number == declarative_record.environment_serial_number() + && &shape == cache.shape + && (!shape.is_unique() || shape.unique_shape_serial_number() == cache.unique_shape_serial_number)) { + return binding_object.get_direct(cache.property_offset.value()); + } + + cache.environment_serial_number = declarative_record.environment_serial_number(); + + auto const& name = interpreter.current_executable().get_identifier(identifier); + if (vm.running_execution_context().script_or_module.has>()) { + // NOTE: GetGlobal is used to access variables stored in the module environment and global environment. + // The module environment is checked first since it precedes the global environment in the environment chain. + auto& module_environment = *vm.running_execution_context().script_or_module.get>()->environment(); + if (TRY(module_environment.has_binding(name))) { + // TODO: Cache offset of binding value + return TRY(module_environment.get_binding_value(vm, name, vm.in_strict_mode())); + } + } + + if (TRY(declarative_record.has_binding(name))) { + // TODO: Cache offset of binding value + return TRY(declarative_record.get_binding_value(vm, name, vm.in_strict_mode())); + } + + if (TRY(binding_object.has_property(name))) { + CacheablePropertyMetadata cacheable_metadata; + auto value = TRY(binding_object.internal_get(name, js_undefined(), &cacheable_metadata)); + if (cacheable_metadata.type == CacheablePropertyMetadata::Type::OwnProperty) { + cache.shape = shape; + cache.property_offset = cacheable_metadata.property_offset.value(); + cache.unique_shape_serial_number = shape.unique_shape_serial_number(); + } + return value; + } + + return vm.throw_completion(ErrorType::UnknownIdentifier, name); +} + } diff --git a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h index baa72f5d41..222e4ad327 100644 --- a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h +++ b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h @@ -14,5 +14,6 @@ namespace JS::Bytecode { ThrowCompletionOr> base_object_for_get(Bytecode::Interpreter&, Value base_value); ThrowCompletionOr get_by_id(Bytecode::Interpreter&, IdentifierTableIndex, Value base_value, Value this_value, u32 cache_index); ThrowCompletionOr get_by_value(Bytecode::Interpreter&, Value base_value, Value property_key_value); +ThrowCompletionOr get_global(Bytecode::Interpreter&, IdentifierTableIndex, u32 cache_index); } diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index d6aadca2bd..0cec07fcff 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -892,56 +892,8 @@ ThrowCompletionOr GetCalleeAndThisFromEnvironment::execute_impl(Bytecode:: ThrowCompletionOr GetGlobal::execute_impl(Bytecode::Interpreter& interpreter) const { - auto& vm = interpreter.vm(); - auto& realm = *vm.current_realm(); - - auto& cache = interpreter.current_executable().global_variable_caches[m_cache_index]; - auto& binding_object = realm.global_environment().object_record().binding_object(); - auto& declarative_record = realm.global_environment().declarative_record(); - - // OPTIMIZATION: If the shape of the object hasn't changed, we can use the cached property offset. - // NOTE: Unique shapes don't change identity, so we compare their serial numbers instead. - auto& shape = binding_object.shape(); - if (cache.environment_serial_number == declarative_record.environment_serial_number() - && &shape == cache.shape - && (!shape.is_unique() || shape.unique_shape_serial_number() == cache.unique_shape_serial_number)) { - interpreter.accumulator() = binding_object.get_direct(cache.property_offset.value()); - return {}; - } - - cache.environment_serial_number = declarative_record.environment_serial_number(); - - auto const& name = interpreter.current_executable().get_identifier(m_identifier); - if (vm.running_execution_context().script_or_module.has>()) { - // NOTE: GetGlobal is used to access variables stored in the module environment and global environment. - // The module environment is checked first since it precedes the global environment in the environment chain. - auto& module_environment = *vm.running_execution_context().script_or_module.get>()->environment(); - if (TRY(module_environment.has_binding(name))) { - // TODO: Cache offset of binding value - interpreter.accumulator() = TRY(module_environment.get_binding_value(vm, name, vm.in_strict_mode())); - return {}; - } - } - - if (TRY(declarative_record.has_binding(name))) { - // TODO: Cache offset of binding value - interpreter.accumulator() = TRY(declarative_record.get_binding_value(vm, name, vm.in_strict_mode())); - return {}; - } - - if (TRY(binding_object.has_property(name))) { - CacheablePropertyMetadata cacheable_metadata; - interpreter.accumulator() = js_undefined(); - interpreter.accumulator() = TRY(binding_object.internal_get(name, interpreter.accumulator(), &cacheable_metadata)); - if (cacheable_metadata.type == CacheablePropertyMetadata::Type::OwnProperty) { - cache.shape = shape; - cache.property_offset = cacheable_metadata.property_offset.value(); - cache.unique_shape_serial_number = shape.unique_shape_serial_number(); - } - return {}; - } - - return vm.throw_completion(ErrorType::UnknownIdentifier, name); + interpreter.accumulator() = TRY(get_global(interpreter, m_identifier, m_cache_index)); + return {}; } ThrowCompletionOr GetLocal::execute_impl(Bytecode::Interpreter&) const diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index 1e4e38324d..0ee7b949a0 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -508,6 +508,9 @@ public: ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; + IdentifierTableIndex identifier() const { return m_identifier; } + u32 cache_index() const { return m_cache_index; } + private: IdentifierTableIndex m_identifier; u32 m_cache_index { 0 }; diff --git a/Userland/Libraries/LibJS/JIT/Compiler.cpp b/Userland/Libraries/LibJS/JIT/Compiler.cpp index f4eabf98ea..b403ce5252 100644 --- a/Userland/Libraries/LibJS/JIT/Compiler.cpp +++ b/Userland/Libraries/LibJS/JIT/Compiler.cpp @@ -416,6 +416,24 @@ void Compiler::compile_get_by_value(Bytecode::Op::GetByValue const& op) check_exception(); } +static Value cxx_get_global(VM& vm, Bytecode::IdentifierTableIndex identifier, u32 cache_index) +{ + return TRY_OR_SET_EXCEPTION(Bytecode::get_global(vm.bytecode_interpreter(), identifier, cache_index)); +} + +void Compiler::compile_get_global(Bytecode::Op::GetGlobal const& op) +{ + m_assembler.mov( + Assembler::Operand::Register(ARG1), + Assembler::Operand::Imm64(op.identifier().value())); + m_assembler.mov( + Assembler::Operand::Register(ARG2), + Assembler::Operand::Imm64(op.cache_index())); + m_assembler.native_call((void*)cxx_get_global); + store_vm_register(Bytecode::Register::accumulator(), RET); + check_exception(); +} + static Value cxx_to_numeric(VM& vm, Value value) { return TRY_OR_SET_EXCEPTION(value.to_numeric(vm)); @@ -514,6 +532,9 @@ OwnPtr Compiler::compile(Bytecode::Executable& bytecode_execut case Bytecode::Instruction::Type::GetByValue: compiler.compile_get_by_value(static_cast(op)); break; + case Bytecode::Instruction::Type::GetGlobal: + compiler.compile_get_global(static_cast(op)); + break; case Bytecode::Instruction::Type::ToNumeric: compiler.compile_to_numeric(static_cast(op)); break; diff --git a/Userland/Libraries/LibJS/JIT/Compiler.h b/Userland/Libraries/LibJS/JIT/Compiler.h index 8b189acbb0..51c6d2c452 100644 --- a/Userland/Libraries/LibJS/JIT/Compiler.h +++ b/Userland/Libraries/LibJS/JIT/Compiler.h @@ -55,6 +55,7 @@ private: void compile_new_string(Bytecode::Op::NewString const&); void compile_get_by_id(Bytecode::Op::GetById const&); void compile_get_by_value(Bytecode::Op::GetByValue const&); + void compile_get_global(Bytecode::Op::GetGlobal const&); void store_vm_register(Bytecode::Register, Assembler::Reg); void load_vm_register(Assembler::Reg, Bytecode::Register);