diff --git a/Userland/Libraries/LibJS/JIT/Compiler.cpp b/Userland/Libraries/LibJS/JIT/Compiler.cpp index ec1ea8e8c5..0b3e8e89eb 100644 --- a/Userland/Libraries/LibJS/JIT/Compiler.cpp +++ b/Userland/Libraries/LibJS/JIT/Compiler.cpp @@ -1091,15 +1091,145 @@ static Value cxx_get_variable(VM& vm, DeprecatedFlyString const& name, Bytecode: void Compiler::compile_get_variable(Bytecode::Op::GetVariable const& op) { - m_assembler.mov( - Assembler::Operand::Register(ARG1), - Assembler::Operand::Imm(bit_cast(&m_bytecode_executable.get_identifier(op.identifier())))); + Assembler::Label slow_case; + + // if (!cache.has_value()) goto slow_case; m_assembler.mov( Assembler::Operand::Register(ARG2), Assembler::Operand::Imm(bit_cast(&m_bytecode_executable.environment_variable_caches[op.cache_index()]))); + + // FIXME: Figure out a nicer way to load a single byte. :^) + m_assembler.mov( + Assembler::Operand::Register(GPR0), + Assembler::Operand::Mem64BaseAndOffset(ARG2, Bytecode::EnvironmentVariableCache::has_value_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); + + // 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())); + + // for (size_t i = 0; i < cache->hops; ++i) + // environment = environment->outer_environment(); + + // GPR0 = hops + // FIXME: Load 32 bits directly instead of 64 and masking. + m_assembler.mov( + Assembler::Operand::Register(GPR0), + Assembler::Operand::Mem64BaseAndOffset(ARG2, Bytecode::EnvironmentVariableCache::value_offset() + EnvironmentCoordinate::hops_offset())); + m_assembler.mov( + Assembler::Operand::Register(GPR2), + Assembler::Operand::Imm(0xffffffff)); + m_assembler.bitwise_and( + Assembler::Operand::Register(GPR0), + Assembler::Operand::Register(GPR2)); + + { + // 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; + // FIXME: Load 8 bits here directly instead of loading 64 and masking. + m_assembler.mov( + Assembler::Operand::Register(GPR0), + Assembler::Operand::Mem64BaseAndOffset(GPR1, Environment::is_permanently_screwed_by_eval_offset())); + m_assembler.bitwise_and( + Assembler::Operand::Register(GPR0), + Assembler::Operand::Imm(0xff)); + 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 + // FIXME: Load 32 bits directly instead of 64 and masking. + m_assembler.mov( + Assembler::Operand::Register(GPR0), + Assembler::Operand::Mem64BaseAndOffset(ARG2, Bytecode::EnvironmentVariableCache::value_offset() + EnvironmentCoordinate::index_offset())); + m_assembler.mov( + Assembler::Operand::Register(GPR2), + Assembler::Operand::Imm(0xffffffff)); + m_assembler.bitwise_and( + Assembler::Operand::Register(GPR0), + Assembler::Operand::Register(GPR2)); + + // 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); + + // accumulator = binding.value; + m_assembler.mov( + Assembler::Operand::Register(GPR0), + Assembler::Operand::Mem64BaseAndOffset(GPR1, DeclarativeEnvironment::Binding::value_offset())); + + store_accumulator(GPR0); + 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(ARG1), + Assembler::Operand::Imm(bit_cast(&m_bytecode_executable.get_identifier(op.identifier())))); native_call((void*)cxx_get_variable); store_accumulator(RET); check_exception(); + + end.link(m_assembler); } static Value cxx_get_callee_and_this_from_environment(VM& vm, DeprecatedFlyString const& name, u32 cache_index, Bytecode::Register callee_reg, Bytecode::Register this_reg) diff --git a/Userland/Libraries/LibJS/JIT/Compiler.h b/Userland/Libraries/LibJS/JIT/Compiler.h index c84b58c9ae..21df8fa3e2 100644 --- a/Userland/Libraries/LibJS/JIT/Compiler.h +++ b/Userland/Libraries/LibJS/JIT/Compiler.h @@ -27,6 +27,7 @@ private: # if ARCH(X86_64) static constexpr auto GPR0 = Assembler::Reg::RAX; static constexpr auto GPR1 = Assembler::Reg::RCX; + static constexpr auto GPR2 = Assembler::Reg::R12; static constexpr auto ARG0 = Assembler::Reg::RDI; static constexpr auto ARG1 = Assembler::Reg::RSI; static constexpr auto ARG2 = Assembler::Reg::RDX; diff --git a/Userland/Libraries/LibJS/Runtime/DeclarativeEnvironment.h b/Userland/Libraries/LibJS/Runtime/DeclarativeEnvironment.h index 775a5e4fb4..197ef334b2 100644 --- a/Userland/Libraries/LibJS/Runtime/DeclarativeEnvironment.h +++ b/Userland/Libraries/LibJS/Runtime/DeclarativeEnvironment.h @@ -19,6 +19,9 @@ class DeclarativeEnvironment : public Environment { JS_ENVIRONMENT(DeclarativeEnvironment, Environment); struct Binding { + static FlatPtr value_offset() { return OFFSET_OF(Binding, value); } + static FlatPtr initialized_offset() { return OFFSET_OF(Binding, initialized); } + DeprecatedFlyString name; Value value; bool strict { false }; @@ -67,6 +70,8 @@ public: [[nodiscard]] u64 environment_serial_number() const { return m_environment_serial_number; } + static FlatPtr bindings_offset() { return OFFSET_OF(DeclarativeEnvironment, m_bindings); } + private: ThrowCompletionOr get_binding_value_direct(VM&, Binding&, bool strict); ThrowCompletionOr set_mutable_binding_direct(VM&, Binding&, Value, bool strict); diff --git a/Userland/Libraries/LibJS/Runtime/Environment.h b/Userland/Libraries/LibJS/Runtime/Environment.h index 480057abca..481ff52595 100644 --- a/Userland/Libraries/LibJS/Runtime/Environment.h +++ b/Userland/Libraries/LibJS/Runtime/Environment.h @@ -57,6 +57,9 @@ public: bool is_permanently_screwed_by_eval() const { return m_permanently_screwed_by_eval; } void set_permanently_screwed_by_eval(); + static FlatPtr is_permanently_screwed_by_eval_offset() { return OFFSET_OF(Environment, m_permanently_screwed_by_eval); } + static FlatPtr outer_environment_offset() { return OFFSET_OF(Environment, m_outer_environment); } + protected: explicit Environment(Environment* parent); diff --git a/Userland/Libraries/LibJS/Runtime/EnvironmentCoordinate.h b/Userland/Libraries/LibJS/Runtime/EnvironmentCoordinate.h index 7f2cc9c354..45f6d62be2 100644 --- a/Userland/Libraries/LibJS/Runtime/EnvironmentCoordinate.h +++ b/Userland/Libraries/LibJS/Runtime/EnvironmentCoordinate.h @@ -15,6 +15,9 @@ struct EnvironmentCoordinate { u32 hops { invalid_marker }; u32 index { invalid_marker }; + static FlatPtr hops_offset() { return OFFSET_OF(EnvironmentCoordinate, hops); } + static FlatPtr index_offset() { return OFFSET_OF(EnvironmentCoordinate, index); } + bool is_valid() const { return hops != invalid_marker && index != invalid_marker; } static constexpr u32 invalid_marker = 0xfffffffe; diff --git a/Userland/Libraries/LibJS/Runtime/ExecutionContext.h b/Userland/Libraries/LibJS/Runtime/ExecutionContext.h index 55de4aec24..f53f9aa076 100644 --- a/Userland/Libraries/LibJS/Runtime/ExecutionContext.h +++ b/Userland/Libraries/LibJS/Runtime/ExecutionContext.h @@ -30,6 +30,8 @@ struct ExecutionContext { void visit_edges(Cell::Visitor&); + static FlatPtr lexical_environment_offset() { return OFFSET_OF(ExecutionContext, lexical_environment); } + private: explicit ExecutionContext(MarkedVector existing_arguments, MarkedVector existing_local_variables);