From 9f9f36f8759b834bc7c2b0ac4fa358d2eea5c8f9 Mon Sep 17 00:00:00 2001 From: jameslahm Date: Thu, 10 Mar 2022 21:00:59 +0800 Subject: [PATCH] [call reducer] inline Array.prototype.indexOf/includes in js-call-reducer. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - inline Array.prototype.indexOf in js-call-reducer - inline Array.prototype.includes in js-call-reducer Bug: v8:12390 Change-Id: Idb5669da3019f0f56af0084fccd1d616d4c5098e Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3473994 Reviewed-by: Tobias Tebbi Reviewed-by: Jakob Gruber Reviewed-by: Marja Hölttä Commit-Queue: Marja Hölttä Cr-Commit-Position: refs/heads/main@{#79461} --- src/compiler/js-call-reducer.cc | 329 ++++++++++++++++++--- test/js-perf-test/Array/includes.js | 67 +++++ test/js-perf-test/Array/index-of.js | 67 +++++ test/js-perf-test/Array/run.js | 3 + test/js-perf-test/JSTests2.json | 21 +- test/mjsunit/optimized-array-includes.js | 358 ++++++++++++++++++++++ test/mjsunit/optimized-array-indexof.js | 360 +++++++++++++++++++++++ 7 files changed, 1170 insertions(+), 35 deletions(-) create mode 100644 test/js-perf-test/Array/includes.js create mode 100644 test/js-perf-test/Array/index-of.js create mode 100644 test/mjsunit/optimized-array-includes.js create mode 100644 test/mjsunit/optimized-array-indexof.js diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc index ad54b68bb2..7935a68c62 100644 --- a/src/compiler/js-call-reducer.cc +++ b/src/compiler/js-call-reducer.cc @@ -265,6 +265,8 @@ class JSCallReducerAssembler : public JSGraphAssembler { // Common operators. TNode TypeGuardUnsignedSmall(TNode value); + TNode TypeGuardNumber(TNode value); + TNode TypeGuardString(TNode value); TNode TypeGuardNonInternal(TNode value); TNode TypeGuardFixedArrayLength(TNode value); TNode Call4(const Callable& callable, TNode context, @@ -519,12 +521,15 @@ class JSCallReducerAssembler : public JSGraphAssembler { }; ForBuilder0 ForZeroUntil(TNode excluded_limit) { - TNode initial_value = ZeroConstant(); + return ForStartUntil(ZeroConstant(), excluded_limit); + } + + ForBuilder0 ForStartUntil(TNode start, TNode excluded_limit) { auto cond = [=](TNode i) { return NumberLessThan(i, excluded_limit); }; auto step = [=](TNode i) { return NumberAdd(i, OneConstant()); }; - return {this, initial_value, cond, step}; + return {this, start, cond, step}; } ForBuilder0 Forever(TNode initial_value, const StepFunction1& step) { @@ -1047,6 +1052,14 @@ TNode JSCallReducerAssembler::TypeGuardUnsignedSmall(TNode value) { return TNode::UncheckedCast(TypeGuard(Type::UnsignedSmall(), value)); } +TNode JSCallReducerAssembler::TypeGuardNumber(TNode value) { + return TNode::UncheckedCast(TypeGuard(Type::Number(), value)); +} + +TNode JSCallReducerAssembler::TypeGuardString(TNode value) { + return TNode::UncheckedCast(TypeGuard(Type::String(), value)); +} + TNode JSCallReducerAssembler::TypeGuardNonInternal( TNode value) { return TNode::UncheckedCast(TypeGuard(Type::NonInternal(), value)); @@ -1983,38 +1996,33 @@ namespace { Callable GetCallableForArrayIndexOfIncludes(ArrayIndexOfIncludesVariant variant, ElementsKind elements_kind, Isolate* isolate) { + DCHECK(IsHoleyElementsKind(elements_kind)); if (variant == ArrayIndexOfIncludesVariant::kIndexOf) { switch (elements_kind) { - case PACKED_SMI_ELEMENTS: case HOLEY_SMI_ELEMENTS: - case PACKED_ELEMENTS: case HOLEY_ELEMENTS: return Builtins::CallableFor(isolate, Builtin::kArrayIndexOfSmiOrObject); - case PACKED_DOUBLE_ELEMENTS: - return Builtins::CallableFor(isolate, - Builtin::kArrayIndexOfPackedDoubles); - default: - DCHECK_EQ(HOLEY_DOUBLE_ELEMENTS, elements_kind); + case HOLEY_DOUBLE_ELEMENTS: return Builtins::CallableFor(isolate, Builtin::kArrayIndexOfHoleyDoubles); + default: { + UNREACHABLE(); + } } } else { DCHECK_EQ(variant, ArrayIndexOfIncludesVariant::kIncludes); switch (elements_kind) { - case PACKED_SMI_ELEMENTS: case HOLEY_SMI_ELEMENTS: - case PACKED_ELEMENTS: case HOLEY_ELEMENTS: return Builtins::CallableFor(isolate, Builtin::kArrayIncludesSmiOrObject); - case PACKED_DOUBLE_ELEMENTS: - return Builtins::CallableFor(isolate, - Builtin::kArrayIncludesPackedDoubles); - default: - DCHECK_EQ(HOLEY_DOUBLE_ELEMENTS, elements_kind); + case HOLEY_DOUBLE_ELEMENTS: return Builtins::CallableFor(isolate, Builtin::kArrayIncludesHoleyDoubles); + default: { + UNREACHABLE(); + } } } UNREACHABLE(); @@ -2030,13 +2038,7 @@ IteratingArrayBuiltinReducerAssembler::ReduceArrayPrototypeIndexOfIncludes( TNode search_element = ArgumentOrUndefined(0); TNode from_index = ArgumentOrZero(1); - // TODO(jgruber): This currently only reduces to a stub call. Create a full - // reduction (similar to other higher-order array builtins) instead of - // lowering to a builtin call. E.g. Array.p.every and Array.p.some have almost - // identical functionality. - - TNode length = LoadJSArrayLength(receiver, kind); - TNode elements = LoadElements(receiver); + TNode original_length = LoadJSArrayLength(receiver, kind); const bool have_from_index = ArgumentCount() > 1; if (have_from_index) { @@ -2046,18 +2048,279 @@ IteratingArrayBuiltinReducerAssembler::ReduceArrayPrototypeIndexOfIncludes( // therefore needs to be added to the length. If the result is still // negative, it needs to be clamped to 0. TNode cond = NumberLessThan(from_index_smi, ZeroConstant()); - from_index = SelectIf(cond) - .Then(_ { - return NumberMax(NumberAdd(length, from_index_smi), - ZeroConstant()); - }) - .Else(_ { return from_index_smi; }) - .ExpectFalse() - .Value(); + from_index = + SelectIf(cond) + .Then(_ { + return NumberMax(NumberAdd(original_length, from_index_smi), + ZeroConstant()); + }) + .Else(_ { return from_index_smi; }) + .ExpectFalse() + .Value(); } - return Call4(GetCallableForArrayIndexOfIncludes(variant, kind, isolate()), - context, elements, search_element, length, from_index); + if (IsHoleyElementsKind(kind)) { + TNode elements = LoadElements(receiver); + return Call4(GetCallableForArrayIndexOfIncludes(variant, kind, isolate()), + context, elements, search_element, original_length, + from_index); + } + + auto out = MakeLabel(MachineRepresentation::kTagged); + + DCHECK(IsFastPackedElementsKind(kind)); + + Node* fail_value; + if (variant == ArrayIndexOfIncludesVariant::kIncludes) { + fail_value = FalseConstant(); + } else { + fail_value = NumberConstant(-1); + } + TNode elements = LoadElements(receiver); + + switch (kind) { + case PACKED_SMI_ELEMENTS: { + TNode is_finite_number = AddNode(graph()->NewNode( + simplified()->ObjectIsFiniteNumber(), search_element)); + GotoIfNot(is_finite_number, &out, fail_value); + + TNode search_element_number = TypeGuardNumber(search_element); + ForStartUntil(TNode::UncheckedCast(from_index), original_length) + .Do([&](TNode k) { + // if from_index is not smi, it will early bailout, so here + // we could LoadElement directly. + TNode element = LoadElement( + AccessBuilder::ForFixedArrayElement(kind), elements, k); + + auto cond = NumberEqual(search_element_number, + TNode::UncheckedCast(element)); + + if (variant == ArrayIndexOfIncludesVariant::kIncludes) { + GotoIf(cond, &out, TrueConstant()); + } else { + GotoIf(cond, &out, k); + } + }); + Goto(&out, fail_value); + break; + } + case PACKED_DOUBLE_ELEMENTS: { + auto nan_loop = MakeLabel(); + TNode is_number = AddNode( + graph()->NewNode(simplified()->ObjectIsNumber(), search_element)); + GotoIfNot(is_number, &out, fail_value); + + TNode search_element_number = TypeGuardNumber(search_element); + + if (variant == ArrayIndexOfIncludesVariant::kIncludes) { + // https://tc39.es/ecma262/#sec-array.prototype.includes use + // SameValueZero, NaN == NaN, so we need to check. + TNode is_nan = AddNode(graph()->NewNode( + simplified()->NumberIsNaN(), search_element_number)); + GotoIf(is_nan, &nan_loop); + } else { + DCHECK(variant == ArrayIndexOfIncludesVariant::kIndexOf); + // https://tc39.es/ecma262/#sec-array.prototype.indexOf use + // IsStrictEqual, NaN != NaN, NaN compare will be handled by + // NumberEqual. + } + + ForStartUntil(TNode::UncheckedCast(from_index), original_length) + .Do([&](TNode k) { + TNode element = LoadElement( + AccessBuilder::ForFixedArrayElement(kind), elements, k); + + auto cond = NumberEqual(search_element_number, + TNode::UncheckedCast(element)); + + if (variant == ArrayIndexOfIncludesVariant::kIncludes) { + GotoIf(cond, &out, TrueConstant()); + } else { + GotoIf(cond, &out, k); + } + }); + Goto(&out, fail_value); + + // https://tc39.es/ecma262/#sec-array.prototype.includes use + // SameValueZero, NaN == NaN, we need to bind nan_loop to check. + if (variant == ArrayIndexOfIncludesVariant::kIncludes) { + Bind(&nan_loop); + ForStartUntil(TNode::UncheckedCast(from_index), original_length) + .Do([&](TNode k) { + TNode element = LoadElement( + AccessBuilder::ForFixedArrayElement(kind), elements, k); + + auto cond = AddNode( + graph()->NewNode(simplified()->NumberIsNaN(), + TNode::UncheckedCast(element))); + GotoIf(cond, &out, TrueConstant()); + }); + Goto(&out, fail_value); + } + break; + } + case PACKED_ELEMENTS: { + auto number_loop = MakeLabel(); + auto not_number = MakeLabel(); + auto string_loop = MakeLabel(); + auto bigint_loop = MakeLabel(); + auto ident_loop = MakeLabel(); + + auto is_number = AddNode( + graph()->NewNode(simplified()->ObjectIsNumber(), search_element)); + GotoIf(is_number, &number_loop); + Goto(¬_number); + + Bind(¬_number); + auto is_string = AddNode( + graph()->NewNode(simplified()->ObjectIsString(), search_element)); + GotoIf(is_string, &string_loop); + auto is_bigint = AddNode( + graph()->NewNode(simplified()->ObjectIsBigInt(), search_element)); + GotoIf(is_bigint, &bigint_loop); + + Goto(&ident_loop); + Bind(&ident_loop); + + ForStartUntil(TNode::UncheckedCast(from_index), original_length) + .Do([&](TNode k) { + // if from_index is not smi, it will early bailout, so here + // we could LoadElement directly. + TNode element = LoadElement( + AccessBuilder::ForFixedArrayElement(kind), elements, k); + auto cond = AddNode(graph()->NewNode(simplified()->ReferenceEqual(), + search_element, element)); + if (variant == ArrayIndexOfIncludesVariant::kIncludes) { + GotoIf(cond, &out, TrueConstant()); + } else { + GotoIf(cond, &out, k); + } + }); + + Goto(&out, fail_value); + + Bind(&number_loop); + TNode search_element_number = TypeGuardNumber(search_element); + + auto nan_loop = MakeLabel(); + + if (variant == ArrayIndexOfIncludesVariant::kIncludes) { + // https://tc39.es/ecma262/#sec-array.prototype.includes use + // SameValueZero, NaN == NaN, so we need to check. + auto is_nan = AddNode(graph()->NewNode(simplified()->NumberIsNaN(), + search_element_number)); + GotoIf(is_nan, &nan_loop); + } else { + DCHECK(variant == ArrayIndexOfIncludesVariant::kIndexOf); + // https://tc39.es/ecma262/#sec-array.prototype.indexOf use + // IsStrictEqual, NaN != NaN, NaN compare will be handled by + // NumberEqual. + } + + ForStartUntil(TNode::UncheckedCast(from_index), original_length) + .Do([&](TNode k) { + auto continue_label = MakeLabel(); + TNode element = LoadElement( + AccessBuilder::ForFixedArrayElement(kind), elements, k); + + auto is_number = AddNode( + graph()->NewNode(simplified()->ObjectIsNumber(), element)); + + GotoIfNot(is_number, &continue_label); + + TNode element_number = TypeGuardNumber(element); + auto cond = NumberEqual(search_element_number, element_number); + GotoIfNot(cond, &continue_label); + if (variant == ArrayIndexOfIncludesVariant::kIncludes) { + Goto(&out, TrueConstant()); + } else { + Goto(&out, k); + } + + Bind(&continue_label); + }); + Goto(&out, fail_value); + + // https://tc39.es/ecma262/#sec-array.prototype.includes use + // SameValueZero, NaN == NaN, we need to bind nan_loop to check. + if (variant == ArrayIndexOfIncludesVariant::kIncludes) { + Bind(&nan_loop); + ForStartUntil(TNode::UncheckedCast(from_index), original_length) + .Do([&](TNode k) { + TNode element = LoadElement( + AccessBuilder::ForFixedArrayElement(kind), elements, k); + + auto cond = AddNode( + graph()->NewNode(simplified()->ObjectIsNaN(), element)); + GotoIf(cond, &out, TrueConstant()); + }); + Goto(&out, fail_value); + } + + Bind(&string_loop); + TNode search_element_string = TypeGuardString(search_element); + ForStartUntil(TNode::UncheckedCast(from_index), original_length) + .Do([&](TNode k) { + auto continue_label = MakeLabel(); + TNode element = LoadElement( + AccessBuilder::ForFixedArrayElement(kind), elements, k); + auto is_string = AddNode( + graph()->NewNode(simplified()->ObjectIsString(), element)); + + GotoIfNot(is_string, &continue_label); + + TNode element_string = TypeGuardString(element); + auto cond = AddNode(graph()->NewNode(simplified()->StringEqual(), + element_string, + search_element_string)); + GotoIfNot(cond, &continue_label); + if (variant == ArrayIndexOfIncludesVariant::kIncludes) { + Goto(&out, TrueConstant()); + } else { + Goto(&out, k); + } + + Bind(&continue_label); + }); + Goto(&out, fail_value); + + Bind(&bigint_loop); + ForStartUntil(TNode::UncheckedCast(from_index), original_length) + .Do([&](TNode k) { + auto continue_label = MakeLabel(); + TNode element = LoadElement( + AccessBuilder::ForFixedArrayElement(kind), elements, k); + auto is_bigint = AddNode( + graph()->NewNode(simplified()->ObjectIsBigInt(), element)); + + GotoIfNot(is_bigint, &continue_label); + auto cond = AddNode(graph()->NewNode( + javascript()->CallRuntime(Runtime::kBigIntEqualToBigInt, 2), + search_element, element, context, FrameStateInput(), effect(), + control())); + + GotoIfNot(ToBoolean(cond), &continue_label); + if (variant == ArrayIndexOfIncludesVariant::kIncludes) { + Goto(&out, TrueConstant()); + } else { + Goto(&out, k); + } + + Bind(&continue_label); + }); + Goto(&out, fail_value); + break; + } + default: { + UNREACHABLE(); + } + } + Bind(&out); + if (variant == ArrayIndexOfIncludesVariant::kIncludes) { + return out.PhiAt(0); + } else { + return out.PhiAt(0); + } } namespace { diff --git a/test/js-perf-test/Array/includes.js b/test/js-perf-test/Array/includes.js new file mode 100644 index 0000000000..5be93443d9 --- /dev/null +++ b/test/js-perf-test/Array/includes.js @@ -0,0 +1,67 @@ +// Copyright 2022 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +(() => { + + function make_includes() { + return new Function('result = array.includes(target)'); + } + + createSuite('SmiIncludes', 1000, make_includes(), SmiIncludesSetup); + createSuite('SparseSmiIncludes', 1000, make_includes(), SparseSmiIncludesSetup); + createSuite('DoubleIncludes', 1000, make_includes(), SmiIncludesSetup); + createSuite('SparseDoubleIncludes', 1000, make_includes(), SparseSmiIncludesSetup); + createSuite('ObjectIncludes', 1000, make_includes(), SmiIncludesSetup); + createSuite('SparseObjectIncludes', 1000, make_includes(), SparseSmiIncludesSetup); + createSuite('StringIncludes', 1000, make_includes(), StringIncludesSetup); + createSuite('SparseStringIncludes', 1000, make_includes(), SparseStringIncludesSetup); + + function SmiIncludesSetup() { + array = new Array(); + for (let i = 0; i < array_size; ++i) array[i] = i; + target = array[array_size-1]; + } + + function SparseSmiIncludesSetup() { + SmiIncludesSetup(); + array.length = array.length * 2; + target = array[array_size-1]; + } + + function StringIncludesSetup() { + array = new Array(); + for (let i = 0; i < array_size; ++i) array[i] = `Item no. ${i}`; + target = array[array_size-1]; + } + + function SparseStringIncludesSetup() { + StringIncludesSetup(); + array.length = array.length * 2; + target = array[array_size-1]; + } + + function DoubleIncludesSetup() { + array = new Array(); + for (let i = 0; i < array_size; ++i) array[i] = i; + target = array[array_size-1]; + } + + function SparseDoubleIncludesSetup() { + DoubleIncludesSetup(); + array.length = array.length * 2; + target = array[array_size-1]; + } + + function ObjectIncludesSetup() { + array = new Array(); + for (let i = 0; i < array_size; ++i) array[i] = {i}; + target = array[array_size-1]; + } + + function SparseObjectIncludesSetup() { + ObjectIncludesSetup(); + array.length = array.length * 2; + target = array[array_size-1]; + } + + })(); diff --git a/test/js-perf-test/Array/index-of.js b/test/js-perf-test/Array/index-of.js new file mode 100644 index 0000000000..5e606382b1 --- /dev/null +++ b/test/js-perf-test/Array/index-of.js @@ -0,0 +1,67 @@ +// Copyright 2022 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +(() => { + + function make_indexOf() { + return new Function('result = array.indexOf(target)'); + } + + createSuite('SmiIndexOf', 1000, make_indexOf(), SmiIndexOfSetup); + createSuite('SparseSmiIndexOf', 1000, make_indexOf(), SparseSmiIndexOfSetup); + createSuite('DoubleIndexOf', 1000, make_indexOf(), SmiIndexOfSetup); + createSuite('SparseDoubleIndexOf', 1000, make_indexOf(), SparseSmiIndexOfSetup); + createSuite('ObjectIndexOf', 1000, make_indexOf(), SmiIndexOfSetup); + createSuite('SparseObjectIndexOf', 1000, make_indexOf(), SparseSmiIndexOfSetup); + createSuite('StringIndexOf', 1000, make_indexOf(), StringIndexOfSetup); + createSuite('SparseStringIndexOf', 1000, make_indexOf(), SparseStringIndexOfSetup); + + function SmiIndexOfSetup() { + array = new Array(); + for (let i = 0; i < array_size; ++i) array[i] = i; + target = array[array_size-1]; + } + + function SparseSmiIndexOfSetup() { + SmiIndexOfSetup(); + array.length = array.length * 2; + target = array[array_size-1]; + } + + function StringIndexOfSetup() { + array = new Array(); + for (let i = 0; i < array_size; ++i) array[i] = `Item no. ${i}`; + target = array[array_size-1]; + } + + function SparseStringIndexOfSetup() { + StringIndexOfSetup(); + array.length = array.length * 2; + target = array[array_size-1]; + } + + function DoubleIndexOfSetup() { + array = new Array(); + for (let i = 0; i < array_size; ++i) array[i] = i; + target = array[array_size-1]; + } + + function SparseDoubleIndexOfSetup() { + DoubleIndexOfSetup(); + array.length = array.length * 2; + target = array[array_size-1]; + } + + function ObjectIndexOfSetup() { + array = new Array(); + for (let i = 0; i < array_size; ++i) array[i] = {i}; + target = array[array_size-1]; + } + + function SparseObjectIndexOfSetup() { + ObjectIndexOfSetup(); + array.length = array.length * 2; + target = array[array_size-1]; + } + + })(); diff --git a/test/js-perf-test/Array/run.js b/test/js-perf-test/Array/run.js index ce6c83de4c..a7b48f0b17 100644 --- a/test/js-perf-test/Array/run.js +++ b/test/js-perf-test/Array/run.js @@ -10,6 +10,7 @@ let array; let func = 0; let this_arg; let result; +let target; const array_size = 100; const max_index = array_size - 1; // Matches what {FastSetup} below produces. @@ -141,6 +142,8 @@ d8.file.execute('join.js'); d8.file.execute('to-string.js'); d8.file.execute('slice.js'); d8.file.execute('copy-within.js'); +d8.file.execute('index-of.js') +d8.file.execute('includes.js') var success = true; diff --git a/test/js-perf-test/JSTests2.json b/test/js-perf-test/JSTests2.json index 0933c7da07..0ef7e4cc04 100644 --- a/test/js-perf-test/JSTests2.json +++ b/test/js-perf-test/JSTests2.json @@ -60,7 +60,8 @@ "resources": [ "filter.js", "map.js", "every.js", "join.js", "some.js", "reduce.js", "reduce-right.js", "to-string.js", "find.js", "find-index.js", - "from.js", "of.js", "for-each.js", "slice.js", "copy-within.js" + "from.js", "of.js", "for-each.js", "slice.js", "copy-within.js", + "index-of.js", "includes.js" ], "flags": [ "--allow-natives-syntax" @@ -181,7 +182,23 @@ {"name": "SmiCopyWithin"}, {"name": "StringCopyWithin"}, {"name": "SparseSmiCopyWithin"}, - {"name": "SparseStringCopyWithin"} + {"name": "SparseStringCopyWithin"}, + {"name": "SmiIndexOf"}, + {"name": "SparseSmiIndexOf"}, + {"name": "DoubleIndexOf"}, + {"name": "SparseDoubleIndexOf"}, + {"name": "ObjectIndexOf"}, + {"name": "SparseObjectIndexOf"}, + {"name": "StringIndexOf"}, + {"name": "SparseStringIncludes"}, + {"name": "SmiIncludes"}, + {"name": "SparseSmiIncludes"}, + {"name": "DoubleIncludes"}, + {"name": "SparseDoubleIncludes"}, + {"name": "ObjectIncludes"}, + {"name": "SparseObjectIncludes"}, + {"name": "StringIncludes"}, + {"name": "SparseStringIncludes"} ] } ] diff --git a/test/mjsunit/optimized-array-includes.js b/test/mjsunit/optimized-array-includes.js new file mode 100644 index 0000000000..a38b2e15af --- /dev/null +++ b/test/mjsunit/optimized-array-includes.js @@ -0,0 +1,358 @@ +// Copyright 2022 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax --turbo-inline-array-builtins --opt +// Flags: --no-always-opt + +// normal case +(() => { + const a = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + function testArrayIncludes() { + return a.includes(20, 0); + } + %PrepareFunctionForOptimization(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + %OptimizeFunctionOnNextCall(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + testArrayIncludes(); + assertOptimized(testArrayIncludes); +})(); + +// from_index is not smi will lead to bailout +(() => { + const a = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + function testArrayIncludes() { + return a.includes(20, { + valueOf: () => { + return 0; + } + }); + } + %PrepareFunctionForOptimization(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + %OptimizeFunctionOnNextCall(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + assertFalse(isOptimized(testArrayIncludes)); +})(); + +// Length change detected during get from_index, will bailout +(() => { + let called_values; + function testArrayIncludes(deopt) { + const a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + return a.includes(9, { + valueOf: () => { + if (deopt) { + a.length = 3; + } + return 0; + } + }); + } + %PrepareFunctionForOptimization(testArrayIncludes); + assertEquals(true, testArrayIncludes()); + testArrayIncludes(); + %OptimizeFunctionOnNextCall(testArrayIncludes); + assertEquals(true, testArrayIncludes()); + assertEquals(false, testArrayIncludes(true)); + assertFalse(isOptimized(testArrayIncludes)); +})(); + +// Input array change during get from_index, will bailout +(() => { + const a = [1, 2, 3, 4, 5]; + function testArrayIncludes() { + return a.includes(9, { + valueOf: () => { + a[0] = 9; + return 0; + } + }); + } + %PrepareFunctionForOptimization(testArrayIncludes); + assertEquals(true, testArrayIncludes()); + testArrayIncludes(); + %OptimizeFunctionOnNextCall(testArrayIncludes); + assertEquals(true, testArrayIncludes()); + assertEquals(true, testArrayIncludes()); + assertFalse(isOptimized(testArrayIncludes)); +})(); + +// Handle from_index is undefined, will bail out +(() => { + const a = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + function testArrayIncludes() { + return a.includes(20, undefined); + } + %PrepareFunctionForOptimization(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + %OptimizeFunctionOnNextCall(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + assertFalse(isOptimized(testArrayIncludes)); +})(); + +// Handle from_index is null, will bail out +(() => { + const a = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + function testArrayIncludes() { + return a.includes(20, undefined); + } + %PrepareFunctionForOptimization(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + %OptimizeFunctionOnNextCall(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + assertFalse(isOptimized(testArrayIncludes)); +})(); + +// Handle from_index is float, will bail out +(() => { + const a = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + function testArrayIncludes() { + return a.includes(20, 0.5); + } + %PrepareFunctionForOptimization(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + %OptimizeFunctionOnNextCall(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + assertFalse(isOptimized(testArrayIncludes)); +})(); + +// Handle from_index is symbol, will throw +(() => { + const a = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + function testArrayIncludes() { + return a.includes(20, Symbol.for('123')); + } + %PrepareFunctionForOptimization(testArrayIncludes); + assertThrows(() => testArrayIncludes()); + %OptimizeFunctionOnNextCall(testArrayIncludes); + assertThrows(() => testArrayIncludes()); + assertFalse(isOptimized(testArrayIncludes)); +})(); + +// Handle from_index is string, will bailout +(() => { + const a = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + function testArrayIncludes() { + return a.includes(20, '0'); + } + %PrepareFunctionForOptimization(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + %OptimizeFunctionOnNextCall(testArrayIncludes); + testArrayIncludes() + assertEquals(true, testArrayIncludes()); + assertFalse(isOptimized(testArrayIncludes)); +})(); + +// Handle from_index is object which cannot convert to smi, will throw +(() => { + const a = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + function testArrayIncludes() { + return a.includes(20, { + valueOf: () => { + return Symbol.for('123') + } + }); + } + %PrepareFunctionForOptimization(testArrayIncludes); + assertThrows(() => testArrayIncludes()); + %OptimizeFunctionOnNextCall(testArrayIncludes); + assertThrows(() => testArrayIncludes()); + assertFalse(isOptimized(testArrayIncludes)); +})(); + +// Handle input array is smi packed elements and search_element is number +// , will be inlined +(() => { + const a = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + function testArrayIncludes() { + return a.includes(20); + } + %PrepareFunctionForOptimization(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + %OptimizeFunctionOnNextCall(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + assertOptimized(testArrayIncludes); +})(); + +// Handle input array is double packed elements, will be inlined +(() => { + const a = [ + 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, + 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, + 19.5, 20.5, 21.5, 22.5, 23.5, 24.5, 25.5 + ]; + function testArrayIncludes() { + return a.includes(20.5); + } + %PrepareFunctionForOptimization(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + %OptimizeFunctionOnNextCall(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + assertOptimized(testArrayIncludes); +})(); + +// Handle input array is double packed elements and has NaN, will be inlined +(() => { + const a = [ + NaN, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, + 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, + 19.5, 20.5, 21.5, 22.5, 23.5, 24.5, 25.5 + ]; + function testArrayIncludes() { + return a.includes(NaN); + } + %PrepareFunctionForOptimization(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + %OptimizeFunctionOnNextCall(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + assertOptimized(testArrayIncludes); + +})(); + +// Handle input array is packed elements, will reach slow path +(() => { + const a = [ + 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, + 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, + 19.5, 20.5, 21.5, 22.5, 23.5, 24.5, {} + ]; + function testArrayIncludes() { + return a.includes(20.5); + } + %PrepareFunctionForOptimization(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + %OptimizeFunctionOnNextCall(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + assertOptimized(testArrayIncludes); + +})(); + + +// Handle input array is packed elements, will be inlined +(() => { + const obj = {} + const a = [ + 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, + 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, + 19.5, 20.5, 21.5, 22.5, 23.5, 24.5, obj + ]; + function testArrayIncludes() { + return a.includes(obj); + } + %PrepareFunctionForOptimization(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + %OptimizeFunctionOnNextCall(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + assertOptimized(testArrayIncludes); + +})(); + + +// Handle input array is packed elements and search_element is symbol +(() => { + const a = [ + 1.5, 2.5, Symbol.for("123"), "4.5", BigInt(123), 6.5, 7.5, 8.5, 9.5, + 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, + 19.5, 20.5, 21.5, 22.5, 23.5, 24.5, {} + ]; + function testArrayIncludes() { + return a.includes(Symbol.for("123")); + } + %PrepareFunctionForOptimization(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + %OptimizeFunctionOnNextCall(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + assertOptimized(testArrayIncludes); + +})(); + +// Handle input array is packed elements and search_element is BigInt +(() => { + const a = [ + 1.5, 2.5, Symbol.for("123"), "4.5", BigInt(123), 6.5, 7.5, 8.5, 9.5, + 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, + 19.5, 20.5, 21.5, 22.5, 23.5, 24.5, {} + ]; + function testArrayIncludes() { + return a.includes(BigInt(123)); + } + %PrepareFunctionForOptimization(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + %OptimizeFunctionOnNextCall(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + assertOptimized(testArrayIncludes); + +})(); + +// Handle input array is packed elements and search_element is string +(() => { + const a = [ + 1.5, 2.5, Symbol.for("123"), "4.5", BigInt(123), 6.5, 7.5, 8.5, 9.5, + 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, + 19.5, 20.5, 21.5, 22.5, 23.5, 24.5, {} + ]; + function testArrayIncludes() { + return a.includes("4.5"); + } + %PrepareFunctionForOptimization(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + %OptimizeFunctionOnNextCall(testArrayIncludes); + testArrayIncludes(); + assertEquals(true, testArrayIncludes()); + assertOptimized(testArrayIncludes); +})(); diff --git a/test/mjsunit/optimized-array-indexof.js b/test/mjsunit/optimized-array-indexof.js new file mode 100644 index 0000000000..d0fe067a6a --- /dev/null +++ b/test/mjsunit/optimized-array-indexof.js @@ -0,0 +1,360 @@ +// Copyright 2022 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax --turbo-inline-array-builtins --opt +// Flags: --no-always-opt + + +// normal case +(() => { + const a = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + function testArrayIndexOf() { + return a.indexOf(20, 0); + } + %PrepareFunctionForOptimization(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(19, testArrayIndexOf()); + %OptimizeFunctionOnNextCall(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(19, testArrayIndexOf()); + testArrayIndexOf(); + assertOptimized(testArrayIndexOf); +})(); + +// from_index is not smi will lead to bailout +(() => { + const a = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + function testArrayIndexOf() { + return a.indexOf(20, { + valueOf: () => { + return 0; + } + }); + } + %PrepareFunctionForOptimization(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(19, testArrayIndexOf()); + %OptimizeFunctionOnNextCall(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(19, testArrayIndexOf()); + assertFalse(isOptimized(testArrayIndexOf)); +})(); + +// Length change detected during get from_index, will bailout +(() => { + let called_values; + function testArrayIndexOf(deopt) { + const a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + return a.indexOf(9, { + valueOf: () => { + if (deopt) { + a.length = 3; + } + return 0; + } + }); + } + %PrepareFunctionForOptimization(testArrayIndexOf); + assertEquals(8, testArrayIndexOf()); + testArrayIndexOf(); + %OptimizeFunctionOnNextCall(testArrayIndexOf); + assertEquals(8, testArrayIndexOf()); + assertEquals(-1, testArrayIndexOf(true)); + assertFalse(isOptimized(testArrayIndexOf)); +})(); + +// Input array change during get from_index, will bailout +(() => { + function testArrayIndexOf(deopt) { + const a = [1, 2, 3, 4, 5]; + return a.indexOf(9, { + valueOf: () => { + if (deopt) { + a[0] = 9; + } + return 0; + } + }); + } + %PrepareFunctionForOptimization(testArrayIndexOf); + assertEquals(-1, testArrayIndexOf()); + testArrayIndexOf(); + %OptimizeFunctionOnNextCall(testArrayIndexOf); + assertEquals(0, testArrayIndexOf(true)); + assertEquals(-1, testArrayIndexOf()); + assertFalse(isOptimized(testArrayIndexOf)); +})(); + +// Handle from_index is undefined, will bail out +(() => { + const a = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + function testArrayIndexOf() { + return a.indexOf(20, undefined); + } + %PrepareFunctionForOptimization(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(19, testArrayIndexOf()); + %OptimizeFunctionOnNextCall(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(19, testArrayIndexOf()); + assertFalse(isOptimized(testArrayIndexOf)); +})(); + +// Handle from_index is null, will bail out +(() => { + const a = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + function testArrayIndexOf() { + return a.indexOf(20, undefined); + } + %PrepareFunctionForOptimization(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(19, testArrayIndexOf()); + %OptimizeFunctionOnNextCall(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(19, testArrayIndexOf()); + assertFalse(isOptimized(testArrayIndexOf)); +})(); + +// Handle from_index is float, will bail out +(() => { + const a = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + function testArrayIndexOf() { + return a.indexOf(20, 0.5); + } + %PrepareFunctionForOptimization(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(19, testArrayIndexOf()); + %OptimizeFunctionOnNextCall(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(19, testArrayIndexOf()); + assertFalse(isOptimized(testArrayIndexOf)); +})(); + +// Handle from_index is symbol, will throw +(() => { + const a = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + function testArrayIndexOf() { + return a.indexOf(20, Symbol.for('123')); + } + %PrepareFunctionForOptimization(testArrayIndexOf); + assertThrows(() => testArrayIndexOf()); + %OptimizeFunctionOnNextCall(testArrayIndexOf); + assertThrows(() => testArrayIndexOf()); + assertFalse(isOptimized(testArrayIndexOf)); +})(); + +// Handle from_index is string, will bailout +(() => { + const a = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + function testArrayIndexOf() { + return a.indexOf(20, '0'); + } + %PrepareFunctionForOptimization(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(19, testArrayIndexOf()); + %OptimizeFunctionOnNextCall(testArrayIndexOf); + testArrayIndexOf() + assertEquals(19, testArrayIndexOf()); + assertFalse(isOptimized(testArrayIndexOf)); +})(); + +// Handle from_index is object which cannot convert to smi, will throw +(() => { + const a = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + function testArrayIndexOf() { + return a.indexOf(20, { + valueOf: () => { + return Symbol.for('123') + } + }); + } + %PrepareFunctionForOptimization(testArrayIndexOf); + assertThrows(() => testArrayIndexOf()); + %OptimizeFunctionOnNextCall(testArrayIndexOf); + assertThrows(() => testArrayIndexOf()); + assertFalse(isOptimized(testArrayIndexOf)); +})(); + +// Handle input array is smi packed elements and search_element is number +// , will be inlined +(() => { + const a = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + function testArrayIndexOf() { + return a.indexOf(20); + } + %PrepareFunctionForOptimization(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(19, testArrayIndexOf()); + %OptimizeFunctionOnNextCall(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(19, testArrayIndexOf()); + assertOptimized(testArrayIndexOf); +})(); + +// Handle input array is double packed elements, will be inlined +(() => { + const a = [ + 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, + 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, + 19.5, 20.5, 21.5, 22.5, 23.5, 24.5, 25.5 + ]; + function testArrayIndexOf() { + return a.indexOf(20.5); + } + %PrepareFunctionForOptimization(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(19, testArrayIndexOf()); + %OptimizeFunctionOnNextCall(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(19, testArrayIndexOf()); + assertOptimized(testArrayIndexOf); +})(); + +// Handle input array is double packed elements and has NaN, will be inlined +(() => { + const a = [ + NaN, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, + 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, + 19.5, 20.5, 21.5, 22.5, 23.5, 24.5, 25.5 + ]; + function testArrayIndexOf() { + return a.indexOf(NaN); + } + %PrepareFunctionForOptimization(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(-1, testArrayIndexOf()); + %OptimizeFunctionOnNextCall(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(-1, testArrayIndexOf()); + assertOptimized(testArrayIndexOf); +})(); + +// Handle input array is packed elements and search_element is double, +// will be inlined +(() => { + const a = [ + 1.5, 2.5, Symbol.for("123"), "4.5", BigInt(123), 6.5, 7.5, 8.5, 9.5, + 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, + 19.5, 20.5, 21.5, 22.5, 23.5, 24.5, {} + ]; + function testArrayIndexOf() { + return a.indexOf(20.5); + } + %PrepareFunctionForOptimization(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(19, testArrayIndexOf()); + %OptimizeFunctionOnNextCall(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(19, testArrayIndexOf()); + assertOptimized(testArrayIndexOf); +})(); + + +// Handle input array is packed elements and search_element is object, +// will be inlined +(() => { + const obj = {} + const a = [ + 1.5, 2.5, Symbol.for("123"), "4.5", BigInt(123), 6.5, 7.5, 8.5, 9.5, + 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, + 19.5, 20.5, 21.5, 22.5, 23.5, 24.5, obj + ]; + function testArrayIndexOf() { + return a.indexOf(obj); + } + %PrepareFunctionForOptimization(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(24, testArrayIndexOf()); + %OptimizeFunctionOnNextCall(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(24, testArrayIndexOf()); + assertOptimized(testArrayIndexOf); +})(); + +// Handle input array is packed elements and search_element is symbol, +// will be inlined +(() => { + const a = [ + 1.5, 2.5, Symbol.for("123"), "4.5", BigInt(123), 6.5, 7.5, 8.5, 9.5, + 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, + 19.5, 20.5, 21.5, 22.5, 23.5, 24.5, {} + ]; + function testArrayIndexOf() { + return a.indexOf(Symbol.for("123")); + } + %PrepareFunctionForOptimization(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(2, testArrayIndexOf()); + %OptimizeFunctionOnNextCall(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(2, testArrayIndexOf()); + assertOptimized(testArrayIndexOf); +})(); + +// Handle input array is packed elements and search_element is BigInt, +// will be inlined +(() => { + const a = [ + 1.5, 2.5, Symbol.for("123"), "4.5", BigInt(123), 6.5, 7.5, 8.5, 9.5, + 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, + 19.5, 20.5, 21.5, 22.5, 23.5, 24.5, {} + ]; + function testArrayIndexOf() { + return a.indexOf(BigInt(123)); + } + %PrepareFunctionForOptimization(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(4, testArrayIndexOf()); + %OptimizeFunctionOnNextCall(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(4, testArrayIndexOf()); + assertOptimized(testArrayIndexOf); +})(); + +// Handle input array is packed elements and search_element is string, +// will be inlined +(() => { + const a = [ + 1.5, 2.5, Symbol.for("123"), "4.5", BigInt(123), 6.5, 7.5, 8.5, 9.5, + 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, + 19.5, 20.5, 21.5, 22.5, 23.5, 24.5, {} + ]; + function testArrayIndexOf() { + return a.indexOf("4.5"); + } + %PrepareFunctionForOptimization(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(3, testArrayIndexOf()); + %OptimizeFunctionOnNextCall(testArrayIndexOf); + testArrayIndexOf(); + assertEquals(3, testArrayIndexOf()); + assertOptimized(testArrayIndexOf); +})();