diff --git a/Userland/Libraries/LibJS/JIT/Compiler.cpp b/Userland/Libraries/LibJS/JIT/Compiler.cpp index 9097e00ff8..f0d53107db 100644 --- a/Userland/Libraries/LibJS/JIT/Compiler.cpp +++ b/Userland/Libraries/LibJS/JIT/Compiler.cpp @@ -1239,6 +1239,66 @@ void Compiler::compile_get_by_id(Bytecode::Op::GetById const& op) branch_if_object(ARG1, [&] { extract_object_pointer(GPR0, ARG1); + // NOTE: Fast path for Array.length which magically reflects + // the "array-like size" of the array object's property storage. + + if (m_bytecode_executable.get_identifier(op.property()) == "length"sv) { + Assembler::Label no_magical_length_property_case; + + // if (!object.has_magical_length_property) goto no_magical_length_property_case; + m_assembler.mov8( + Assembler::Operand::Register(GPR1), + Assembler::Operand::Mem64BaseAndOffset(GPR0, Object::has_magical_length_property_offset())); + m_assembler.jump_if( + Assembler::Operand::Register(GPR1), + Assembler::Condition::EqualTo, + Assembler::Operand::Imm(0), + no_magical_length_property_case); + + // NOTE: The base object has a magical "length" property, so now we just need + // to extract the "array-like size" from the object property storage. + // If we run into any issues, we'll jump to the slow case and figure things out in C++. + + // GPR0 = object->indexed_properties().storage() + m_assembler.mov( + Assembler::Operand::Register(GPR0), + Assembler::Operand::Mem64BaseAndOffset(GPR0, Object::indexed_properties_offset() + IndexedProperties::storage_offset())); + + // if (GPR0 == nullptr) goto slow_case; + m_assembler.jump_if( + Assembler::Operand::Register(GPR0), + Assembler::Condition::EqualTo, + Assembler::Operand::Imm(0), + slow_case); + + // if (!GPR0->is_simple_storage()) goto slow_case; + m_assembler.mov8( + Assembler::Operand::Register(GPR1), + Assembler::Operand::Mem64BaseAndOffset(GPR0, IndexedPropertyStorage::is_simple_storage_offset())); + m_assembler.jump_if( + Assembler::Operand::Register(GPR1), + Assembler::Condition::EqualTo, + Assembler::Operand::Imm(0), + slow_case); + + // accumulator = GPR0->array_like_size() | SHIFT_INT32_TAG + // return + m_assembler.mov( + Assembler::Operand::Register(GPR1), + Assembler::Operand::Mem64BaseAndOffset(GPR0, SimpleIndexedPropertyStorage::array_size_offset())); + m_assembler.mov( + Assembler::Operand::Register(GPR0), + Assembler::Operand::Imm(SHIFTED_INT32_TAG)); + m_assembler.bitwise_or( + Assembler::Operand::Register(GPR1), + Assembler::Operand::Register(GPR0)); + + store_accumulator(GPR1); + m_assembler.jump(end); + + no_magical_length_property_case.link(m_assembler); + } + // if (cache.shape != &object->shape()) goto slow_case; m_assembler.mov( Assembler::Operand::Register(GPR2), diff --git a/Userland/Libraries/LibJS/Runtime/Array.cpp b/Userland/Libraries/LibJS/Runtime/Array.cpp index f8cb17849c..7fc55f9aed 100644 --- a/Userland/Libraries/LibJS/Runtime/Array.cpp +++ b/Userland/Libraries/LibJS/Runtime/Array.cpp @@ -64,6 +64,7 @@ NonnullGCPtr Array::create_from(Realm& realm, Vector const& elemen Array::Array(Object& prototype) : Object(ConstructWithPrototypeTag::Tag, prototype) { + m_has_magical_length_property = true; } // 10.4.2.4 ArraySetLength ( A, Desc ), https://tc39.es/ecma262/#sec-arraysetlength diff --git a/Userland/Libraries/LibJS/Runtime/Object.h b/Userland/Libraries/LibJS/Runtime/Object.h index d41c8364d1..552107b710 100644 --- a/Userland/Libraries/LibJS/Runtime/Object.h +++ b/Userland/Libraries/LibJS/Runtime/Object.h @@ -221,6 +221,8 @@ public: static FlatPtr may_interfere_with_indexed_property_access_offset() { return OFFSET_OF(Object, m_may_interfere_with_indexed_property_access); } static FlatPtr indexed_properties_offset() { return OFFSET_OF(Object, m_indexed_properties); } + static FlatPtr has_magical_length_property_offset() { return OFFSET_OF(Object, m_has_magical_length_property); } + protected: enum class GlobalObjectTag { Tag }; enum class ConstructWithoutPrototypeTag { Tag }; @@ -238,6 +240,8 @@ protected: // [[ParameterMap]] bool m_has_parameter_map { false }; + bool m_has_magical_length_property { false }; + private: void set_shape(Shape& shape) { m_shape = &shape; }