From b532dedc916b706514fd7f3fd160d5b59b63f807 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 12 Nov 2023 15:04:23 +0100 Subject: [PATCH] LibJS/JIT: Add fast path for GetById of Array.length Array.length is magical (since it has to reflect the number of elements in the object's property storage). We now handle it specially in jitted code, giving us a massive speed-up on Kraken/ai-astar.js (and probably many other things as well) :^) --- Userland/Libraries/LibJS/JIT/Compiler.cpp | 60 ++++++++++++++++++++++ Userland/Libraries/LibJS/Runtime/Array.cpp | 1 + Userland/Libraries/LibJS/Runtime/Object.h | 4 ++ 3 files changed, 65 insertions(+) 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; }