• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ecmascript/compiler/builtins/builtins_array_stub_builder.h"
17 
18 #include "ecmascript/compiler/builtins/builtins_string_stub_builder.h"
19 #include "ecmascript/compiler/circuit_builder-inl.h"
20 
21 namespace panda::ecmascript::kungfu {
MakeIndexOfOptions(BuiltinsStubCSigns::ID id,bool resultIsTagged)22 BuiltinsArrayStubBuilder::IndexOfOptions BuiltinsArrayStubBuilder::MakeIndexOfOptions(
23     BuiltinsStubCSigns::ID id, bool resultIsTagged)
24 {
25     IndexOfOptions options;
26     switch (id) {
27         case BuiltinsStubCSigns::ID::ArrayIndexOf:
28         case BuiltinsStubCSigns::ID::ArrayLastIndexOf:
29             options.compType = ComparisonType::STRICT_EQUAL;
30             options.returnType = resultIsTagged
31                 ? IndexOfReturnType::TAGGED_FOUND_INDEX
32                 : IndexOfReturnType::UNTAGGED_FOUND_INDEX;
33             options.holeAsUndefined = false;
34             options.reversedOrder = (id == BuiltinsStubCSigns::ID::ArrayLastIndexOf);
35             break;
36         case BuiltinsStubCSigns::ID::ArrayIncludes:
37             options.compType = ComparisonType::SAME_VALUE_ZERO;
38             options.returnType = resultIsTagged
39                 ? IndexOfReturnType::TAGGED_FOUND_OR_NOT
40                 : IndexOfReturnType::UNTAGGED_FOUND_OR_NOT;
41             options.holeAsUndefined = true;
42             options.reversedOrder = false;
43             break;
44         default:
45             LOG_ECMA(FATAL) << "Implementation Error: Unreachable branch.";
46             UNREACHABLE();
47     }
48     return options;
49 }
50 
MakeFromIndex(GateRef input,GateRef length,bool reversedOrder)51 GateRef BuiltinsArrayStubBuilder::MakeFromIndex(GateRef input, GateRef length, bool reversedOrder)
52 {
53     ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), TaggedIsNumber(input));
54 
55     auto env = GetEnvironment();
56     Label entry(env);
57     env->SubCfgEntry(&entry);
58     Label inputIsTaggedInt(env);
59     Label inputIsTaggedDouble(env);
60     Label exit(env);
61     DEFVARIABLE(result, VariableType::INT64(), Int64(0));
62 
63     BRANCH_LIKELY(TaggedIsInt(input), &inputIsTaggedInt, &inputIsTaggedDouble);
64     Bind(&inputIsTaggedInt);
65     result.WriteVariable(MakeFromIndexWithInt(TaggedGetInt(input), length, reversedOrder));
66     Jump(&exit);
67 
68     Bind(&inputIsTaggedDouble);
69     result.WriteVariable(MakeFromIndexWithDouble(GetDoubleOfTDouble(input), length, reversedOrder));
70     Jump(&exit);
71 
72     Bind(&exit);
73     GateRef ret = result.ReadVariable();
74     env->SubCfgExit();
75     return ret;
76 }
77 
MakeFromIndexWithInt(GateRef intValue,GateRef length,bool reversedOrder)78 GateRef BuiltinsArrayStubBuilder::MakeFromIndexWithInt(GateRef intValue, GateRef length, bool reversedOrder)
79 {
80     auto env = GetEnvironment();
81     Label entry(env);
82     env->SubCfgEntry(&entry);
83 
84     Label reachesLength(env);
85     Label lessThanLength(env);
86     Label isNegative(env);
87     Label plusLengthIsNegative(env);
88     Label plusLengthIsNonNegative(env);
89     Label exit(env);
90 
91     GateRef i64Value = SExtInt32ToInt64(intValue);
92     GateRef i64Length = ZExtInt32ToInt64(length);
93     DEFVARIABLE(result, VariableType::INT64(), i64Value);
94 
95     BRANCH_UNLIKELY(Int64GreaterThanOrEqual(i64Value, i64Length), &reachesLength, &lessThanLength);
96     Bind(&reachesLength);
97     if (reversedOrder) {
98         result.WriteVariable(Int64Sub(i64Length, Int64(1))); // lastIndexOf
99     } else {
100         result.WriteVariable(i64Length); // indexOf, includes
101     }
102     Jump(&exit);
103     // No change to result if it falls in range [0, length - 1]
104     Bind(&lessThanLength);
105     BRANCH(Int64LessThan(i64Value, Int64(0)), &isNegative, &exit);
106 
107     Bind(&isNegative);
108     GateRef i64PlusLength = Int64Add(i64Value, i64Length);
109     BRANCH(Int64LessThan(i64PlusLength, Int64(0)), &plusLengthIsNegative, &plusLengthIsNonNegative);
110 
111     Bind(&plusLengthIsNegative);
112     if (reversedOrder) {
113         result.WriteVariable(Int64(-1)); // lastIndexOf
114     } else {
115         result.WriteVariable(Int64(0)); // indexOf, includes
116     }
117     Jump(&exit);
118 
119     Bind(&plusLengthIsNonNegative);
120     result.WriteVariable(i64PlusLength);
121     Jump(&exit);
122 
123     Bind(&exit);
124     GateRef ret = result.ReadVariable();
125     env->SubCfgExit();
126     return ret;
127 }
128 
MakeFromIndexWithDouble(GateRef doubleValue,GateRef length,bool reversedOrder)129 GateRef BuiltinsArrayStubBuilder::MakeFromIndexWithDouble(GateRef doubleValue, GateRef length, bool reversedOrder)
130 {
131     auto env = GetEnvironment();
132     Label entry(env);
133     env->SubCfgEntry(&entry);
134     Label isNotNAN(env);
135     Label reachesLength(env);
136     Label lessThanLength(env);
137     Label isNonNegativeAfterTruncation(env);
138     Label isNegative(env);
139     Label plusLengthIsNonNegative(env);
140     Label plusLengthIsNegative(env);
141     Label exit(env);
142 
143     DEFVARIABLE(result, VariableType::INT64(), Int64(0));
144 
145     BRANCH_UNLIKELY(DoubleIsNAN(doubleValue), &exit, &isNotNAN); // NaN -> 0
146     Bind(&isNotNAN);
147     GateRef f64Length = ChangeUInt32ToFloat64(length);
148     BRANCH(DoubleGreaterThanOrEqual(doubleValue, f64Length), &reachesLength, &lessThanLength);
149 
150     Bind(&reachesLength); // Including the case where doubleValue is +inf
151     GateRef i64Length = ZExtInt32ToInt64(length);
152     if (reversedOrder) {
153         result.WriteVariable(Int64Sub(i64Length, Int64(1))); // lastIndexOf
154     } else {
155         result.WriteVariable(i64Length); // indexOf, includes
156     }
157     Jump(&exit);
158 
159     Bind(&lessThanLength);
160     GateRef trunkValue = DoubleTrunc(doubleValue);
161     BRANCH(DoubleGreaterThanOrEqual(trunkValue, Double(0.0)), &isNonNegativeAfterTruncation, &isNegative);
162 
163     Bind(&isNonNegativeAfterTruncation);
164     result.WriteVariable(ZExtInt32ToInt64(ChangeFloat64ToInt32(trunkValue)));
165     Jump(&exit);
166 
167     Bind(&isNegative);
168     GateRef plusLength = DoubleAdd(trunkValue, f64Length);
169     // If doubleValue + length < 0, then result is 0. (Including the case where doubleValue is -inf)
170     BRANCH(DoubleGreaterThanOrEqual(plusLength, Double(0.0)), &plusLengthIsNonNegative, &plusLengthIsNegative);
171 
172     Bind(&plusLengthIsNonNegative);
173     result.WriteVariable(ZExtInt32ToInt64(ChangeFloat64ToInt32(plusLength)));
174     Jump(&exit);
175 
176     Bind(&plusLengthIsNegative);
177     if (reversedOrder) {
178         result.WriteVariable(Int64(-1)); // lastIndexOf
179     } // indexOf, includes: result is 0
180     Jump(&exit);
181 
182     Bind(&exit);
183     GateRef ret = result.ReadVariable();
184     env->SubCfgExit();
185     return ret;
186 }
187 
MakeResultVariableDefaultNotFound(IndexOfOptions options)188 Variable BuiltinsArrayStubBuilder::MakeResultVariableDefaultNotFound(IndexOfOptions options)
189 {
190     VariableType type;
191     GateRef value;
192     switch (options.returnType) {
193         case IndexOfReturnType::TAGGED_FOUND_INDEX:
194             type = VariableType::JS_ANY();
195             value = IntToTaggedPtr(Int32(-1));
196             break;
197         case IndexOfReturnType::TAGGED_FOUND_OR_NOT:
198             type = VariableType::JS_ANY();
199             value = TaggedFalse();
200             break;
201         case IndexOfReturnType::UNTAGGED_FOUND_INDEX:
202             type = VariableType::INT32();
203             value = Int32(-1);
204             break;
205         case IndexOfReturnType::UNTAGGED_FOUND_OR_NOT:
206             type = VariableType::BOOL();
207             value = False();
208             break;
209         default:
210             LOG_ECMA(FATAL) << "Implementation Error: Unreachable branch.";
211             UNREACHABLE();
212     }
213     return Variable(GetEnvironment(), type, NextVariableId(), value);
214 }
215 
216 template <class Predicate>
IndexOfElements(GateRef elements,Predicate predicate,GateRef fromIndex,GateRef len,IndexOfOptions options)217 GateRef BuiltinsArrayStubBuilder::IndexOfElements(
218     GateRef elements, Predicate predicate, GateRef fromIndex, GateRef len, IndexOfOptions options)
219 {
220     static_assert(std::is_invocable_r_v<GateRef, Predicate, GateRef>, "Invalid call signature.");
221     auto env = GetEnvironment();
222     Label entry(env);
223     env->SubCfgEntry(&entry);
224 
225     Label loopHead(env);
226     Label loopBodyBegin(env);
227     Label loopBodyEnd(env);
228     Label loopExitFound(env);
229     Label exit(env);
230 
231     GateRef sizeBytesPerElement = IntPtr(JSTaggedValue::TaggedTypeSize());
232     // Decomposed from StubBuilder::GetValueFromTaggedArray
233     auto indexToOffset = [this, sizeBytesPerElement](GateRef i64Index) {
234         return PtrAdd(
235             PtrMul(sizeBytesPerElement, ChangeInt64ToIntPtr(i64Index)),
236             IntPtr(TaggedArray::DATA_OFFSET));
237     };
238 
239     GateRef boundOffset;
240     GateRef initialOffset;
241     if (options.reversedOrder) {
242         // lastIndexOf: Closed range [0, fromIndex]
243         boundOffset = IntPtr(TaggedArray::DATA_OFFSET);
244         initialOffset = indexToOffset(fromIndex);
245     } else {
246         // indexOf: Half-closed range [fromIndex, len)
247         initialOffset = indexToOffset(fromIndex);
248         boundOffset = indexToOffset(ZExtInt32ToInt64(len));
249     }
250     DEFVARIABLE(curOffset, VariableType::NATIVE_POINTER(), initialOffset);
251     Variable result = MakeResultVariableDefaultNotFound(options);
252 
253     Jump(&loopHead);
254     LoopBegin(&loopHead);
255     {
256         GateRef continues = options.reversedOrder
257             ? IntPtrGreaterThanOrEqual(*curOffset, boundOffset)
258             : IntPtrLessThan(*curOffset, boundOffset);
259         BRANCH_LIKELY(continues, &loopBodyBegin, &exit);
260 
261         Bind(&loopBodyBegin);
262         GateRef curValue = Load(VariableType::JS_ANY(), elements, *curOffset);
263         GateRef curValueMatches = std::invoke(predicate, curValue);
264         BRANCH_UNLIKELY(curValueMatches, &loopExitFound, &loopBodyEnd);
265 
266         Bind(&loopBodyEnd);
267         if (options.reversedOrder) {
268             curOffset = PtrSub(*curOffset, sizeBytesPerElement);
269         } else {
270             curOffset = PtrAdd(*curOffset, sizeBytesPerElement);
271         }
272     }
273     LoopEnd(&loopHead);
274 
275     Bind(&loopExitFound);
276     switch (options.returnType) {
277         case IndexOfReturnType::TAGGED_FOUND_INDEX:
278         case IndexOfReturnType::UNTAGGED_FOUND_INDEX: {
279             GateRef iptrIndex = IntPtrDiv(
280                 PtrSub(*curOffset, IntPtr(TaggedArray::DATA_OFFSET)),
281                 sizeBytesPerElement);
282             // Note: We assume the size of a stable array will never exceed 2^31 - 1 (i.e. max of signed int32).
283             GateRef i32Index = ChangeIntPtrToInt32(iptrIndex);
284             GateRef resultIndex = (options.returnType == IndexOfReturnType::TAGGED_FOUND_INDEX)
285                 ? IntToTaggedPtr(i32Index)
286                 : i32Index;
287             result.WriteVariable(resultIndex);
288             break;
289         }
290         case IndexOfReturnType::TAGGED_FOUND_OR_NOT:
291             result.WriteVariable(TaggedTrue());
292             break;
293         case IndexOfReturnType::UNTAGGED_FOUND_OR_NOT:
294             result.WriteVariable(True());
295             break;
296         default:
297             LOG_ECMA(FATAL) << "Implementation Error: Unreachable branch.";
298             UNREACHABLE();
299     }
300     Jump(&exit);
301 
302     Bind(&exit);
303     GateRef ret = result.ReadVariable();
304     env->SubCfgExit();
305     return ret;
306 }
307 
StringEqual(GateRef glue,GateRef left,GateRef right,StringElementsCondition rightCondition)308 GateRef BuiltinsArrayStubBuilder::StringEqual(
309     GateRef glue, GateRef left, GateRef right, StringElementsCondition rightCondition)
310 {
311     auto env = GetEnvironment();
312     Label entry(env);
313     env->SubCfgEntry(&entry);
314 
315     Label rightIsString(env);
316     Label addrEquals(env);
317     Label addrNotEquals(env);
318     Label exit(env);
319     DEFVARIABLE(result, VariableType::BOOL(), False());
320 
321     if (rightCondition == StringElementsCondition::MAY_BE_ANY) {
322         BRANCH(TaggedIsString(right), &rightIsString, &exit);
323         Bind(&rightIsString);
324     } else if (rightCondition == StringElementsCondition::MAY_BE_HOLE) {
325         BRANCH(TaggedIsNotHole(right), &rightIsString, &exit);
326         Bind(&rightIsString);
327     }
328     BRANCH_UNLIKELY(Equal(left, right), &addrEquals, &addrNotEquals);
329     Bind(&addrEquals);
330     result.WriteVariable(True());
331     Jump(&exit);
332     Bind(&addrNotEquals);
333     BuiltinsStringStubBuilder stringBuilder(env);
334     result.WriteVariable(stringBuilder.FastStringEqualWithoutRTStub(glue, left, right));
335     Jump(&exit);
336 
337     Bind(&exit);
338     GateRef ret = result.ReadVariable();
339     env->SubCfgExit();
340     return ret;
341 }
342 
BigIntEqual(GateRef glue,GateRef left,GateRef right)343 GateRef BuiltinsArrayStubBuilder::BigIntEqual(GateRef glue, GateRef left, GateRef right)
344 {
345     auto env = GetEnvironment();
346     Label entry(env);
347     env->SubCfgEntry(&entry);
348     DEFVARIABLE(predResult, VariableType::BOOL(), False());
349 
350     Label addrEquals(env);
351     Label addrNotEquals(env);
352     Label rightIsBigInt(env);
353     Label exit(env);
354 
355     BRANCH(Equal(left, right), &addrEquals, &addrNotEquals);
356     Bind(&addrEquals);
357     predResult.WriteVariable(True());
358     Jump(&exit);
359     Bind(&addrNotEquals);
360     BRANCH(TaggedIsBigInt(right), &rightIsBigInt, &exit);
361     Bind(&rightIsBigInt);
362     predResult.WriteVariable(CallNGCRuntime(glue, RTSTUB_ID(BigIntSameValueZero), { left, right }));
363     Jump(&exit);
364 
365     Bind(&exit);
366     GateRef ret = predResult.ReadVariable();
367     env->SubCfgExit();
368     return ret;
369 }
370 
IndexOfTaggedUndefined(GateRef elements,GateRef fromIndex,GateRef len,IndexOfOptions options)371 GateRef BuiltinsArrayStubBuilder::IndexOfTaggedUndefined(
372     GateRef elements, GateRef fromIndex, GateRef len, IndexOfOptions options)
373 {
374     if (options.holeAsUndefined) {
375         return IndexOfElements(elements, [this](GateRef curValue) {
376             return BitOr(TaggedIsUndefined(curValue), TaggedIsHole(curValue));
377         }, fromIndex, len, options);
378     }
379     return IndexOfElements(elements, [this](GateRef curValue) {
380         return TaggedIsUndefined(curValue);
381     }, fromIndex, len, options);
382 }
383 
IndexOfTaggedZero(GateRef elements,GateRef fromIndex,GateRef len,IndexOfOptions options)384 GateRef BuiltinsArrayStubBuilder::IndexOfTaggedZero(
385     GateRef elements, GateRef fromIndex, GateRef len, IndexOfOptions options)
386 {
387     auto env = GetEnvironment();
388     return IndexOfElements(elements, [this, env](GateRef curValue) {
389         GateRef underlying = ChangeTaggedPointerToInt64(curValue);
390         return LogicOrBuilder(env)
391             .Or(Equal(Int64(JSTaggedValue::VALUE_ZERO), underlying))
392             .Or(Equal(Int64(JSTaggedValue::VALUE_POSITIVE_ZERO), underlying))
393             .Or(Equal(Int64(JSTaggedValue::VALUE_NEGATIVE_ZERO), underlying))
394             .Done();
395     }, fromIndex, len, options);
396 }
397 
IndexOfTaggedIntElements(GateRef elements,GateRef target,GateRef fromIndex,GateRef len,IndexOfOptions options)398 GateRef BuiltinsArrayStubBuilder::IndexOfTaggedIntElements(
399     GateRef elements, GateRef target, GateRef fromIndex, GateRef len, IndexOfOptions options)
400 {
401     // assume: ElementsKind is INT or HOLE_INT
402     ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), BoolNot(TaggedIsUndefined(target)));
403 
404     auto env = GetEnvironment();
405     Label entry(env);
406     env->SubCfgEntry(&entry);
407 
408     Label targetIsInt(env);
409     Label targetIsNotInt(env);
410     Label targetIsDouble(env);
411     Label targetIsWithinInt32(env);
412     Label exit(env);
413     Variable result = MakeResultVariableDefaultNotFound(options);
414 
415     BRANCH_LIKELY(TaggedIsInt(target), &targetIsInt, &targetIsNotInt);
416     Bind(&targetIsInt);
417     result = IndexOfElements(elements, [this, ti32 = target](GateRef curValue) {
418         return Equal(curValue, ti32);
419     }, fromIndex, len, options);
420     Jump(&exit);
421 
422     Bind(&targetIsNotInt);
423     BRANCH_LIKELY(TaggedIsDouble(target), &targetIsDouble, &exit);
424     Bind(&targetIsDouble);
425     GateRef doubleValue = GetDoubleOfTDouble(target);
426     BRANCH(DoubleIsWithinInt32(doubleValue), &targetIsWithinInt32, &exit);
427 
428     Bind(&targetIsWithinInt32);
429     GateRef taggedInt32 = IntToTaggedPtr(ChangeFloat64ToInt32(doubleValue));
430     result = IndexOfElements(elements, [this, ti32 = taggedInt32](GateRef curValue) {
431         return Equal(curValue, ti32);
432     }, fromIndex, len, options);
433     Jump(&exit);
434 
435     Bind(&exit);
436     GateRef ret = result.ReadVariable();
437     env->SubCfgExit();
438     return ret;
439 }
440 
IndexOfTaggedIntTarget(GateRef elements,GateRef target,GateRef fromIndex,GateRef len,IndexOfOptions options)441 GateRef BuiltinsArrayStubBuilder::IndexOfTaggedIntTarget(
442     GateRef elements, GateRef target, GateRef fromIndex, GateRef len, IndexOfOptions options)
443 {
444     ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), TaggedIsInt(target));
445 
446     auto env = GetEnvironment();
447     Label entry(env);
448     env->SubCfgEntry(&entry);
449 
450     Label isZero(env);
451     Label isNotZero(env);
452     Label exit(env);
453     Variable result = MakeResultVariableDefaultNotFound(options);
454 
455     GateRef int32Value = TaggedGetInt(target);
456     BRANCH(Int32Equal(Int32(0), int32Value), &isZero, &isNotZero);
457     Bind(&isZero);
458     result = IndexOfTaggedZero(elements, fromIndex, len, options);
459     Jump(&exit);
460     Bind(&isNotZero);
461     GateRef taggedDouble = DoubleToTaggedDoublePtr(ChangeInt32ToFloat64(int32Value));
462     result = IndexOfElements(elements, [this, ti32 = target, tf64 = taggedDouble](GateRef curValue) {
463         return BitOr(Equal(curValue, ti32), Equal(curValue, tf64));
464     }, fromIndex, len, options);
465     Jump(&exit);
466 
467     Bind(&exit);
468     GateRef ret = result.ReadVariable();
469     env->SubCfgExit();
470     return ret;
471 }
472 
IndexOfTaggedNumber(GateRef elements,GateRef target,GateRef fromIndex,GateRef len,IndexOfOptions options,bool targetIsAlwaysDouble)473 GateRef BuiltinsArrayStubBuilder::IndexOfTaggedNumber(
474     GateRef elements, GateRef target, GateRef fromIndex, GateRef len,
475     IndexOfOptions options, bool targetIsAlwaysDouble)
476 {
477     if (targetIsAlwaysDouble) {
478         // No constraints to ElementsKind
479         ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), TaggedIsDouble(target));
480     } else {
481         // assume: ElementsKind is NUMBER or HOLE_NUMBER
482         ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), BoolNot(TaggedIsUndefined(target)));
483     }
484 
485     auto env = GetEnvironment();
486     Label entry(env);
487     env->SubCfgEntry(&entry);
488     Label exit(env);
489 
490     Label targetIsWithinInt32(env);
491     Label targetDone(env);
492     Label targetIsNaN(env);
493     Label targetIsNotNaN(env);
494     Variable result = MakeResultVariableDefaultNotFound(options);
495 
496     DEFVARIABLE(doubleValue, VariableType::FLOAT64(),  Double(0.0));
497     DEFVARIABLE(taggedFloat, VariableType::JS_ANY(),  Undefined());
498     DEFVARIABLE(taggedInt32, VariableType::JS_ANY(), Undefined());
499 
500     if (!targetIsAlwaysDouble) {
501         Label targetIsInt(env);
502         Label targetIsNotInt(env);
503         Label targetIsDouble(env);
504 
505         BRANCH(TaggedIsInt(target), &targetIsInt, &targetIsNotInt);
506         Bind(&targetIsInt);
507         doubleValue = ChangeInt32ToFloat64(TaggedGetInt(target));
508         taggedFloat = DoubleToTaggedDoublePtr(*doubleValue);
509         taggedInt32 = target;
510         Jump(&targetDone);
511 
512         Bind(&targetIsNotInt);
513         BRANCH_LIKELY(TaggedIsDouble(target), &targetIsDouble, &exit);
514         Bind(&targetIsDouble);
515     }
516     // If doubleValue is not within int32, then ti32 is left Undefined().
517     doubleValue = GetDoubleOfTDouble(target);
518     taggedFloat = target;
519     BRANCH(DoubleIsWithinInt32(*doubleValue), &targetIsWithinInt32, &targetDone);
520     Bind(&targetIsWithinInt32);
521     taggedInt32 = IntToTaggedPtr(ChangeFloat64ToInt32(*doubleValue));
522     Jump(&targetDone);
523 
524     Bind(&targetDone);
525     BRANCH_UNLIKELY(DoubleIsNAN(*doubleValue), &targetIsNaN, &targetIsNotNaN);
526 
527     Bind(&targetIsNaN);
528     if (options.compType == ComparisonType::SAME_VALUE_ZERO) {
529         result = IndexOfElements(elements, [this](GateRef curValue) {
530             return DoubleIsNAN(GetDoubleOfTDouble(curValue));
531         }, fromIndex, len, options);
532     }
533     Jump(&exit);
534 
535     Bind(&targetIsNotNaN);
536     Label isZero(env);
537     Label isNotZero(env);
538     Label withinInt32Branch(env);
539     Label notWithinInt32Branch(env);
540 
541     BRANCH(DoubleEqual(Double(0.0), *doubleValue), &isZero, &isNotZero);
542     Bind(&isZero);
543     result = IndexOfTaggedZero(elements, fromIndex, len, options);
544     Jump(&exit);
545     Bind(&isNotZero);
546     // If target is not within int32, then taggedInt32 is left Undefined().
547     BRANCH(TaggedIsInt(*taggedInt32), &withinInt32Branch, &notWithinInt32Branch);
548     Bind(&withinInt32Branch);
549     {
550         auto predicate = [this, ti32 = *taggedInt32, tf64 = *taggedFloat](GateRef curValue) {
551             return BitOr(Equal(curValue, ti32), Equal(curValue, tf64));
552         };
553         result = IndexOfElements(elements, predicate, fromIndex, len, options);
554         Jump(&exit);
555     }
556     Bind(&notWithinInt32Branch);
557     result = IndexOfElements(elements, [this, tf64 = *taggedFloat](GateRef curValue) {
558         return Equal(curValue, tf64);
559     }, fromIndex, len, options);
560     Jump(&exit);
561 
562     Bind(&exit);
563     GateRef ret = result.ReadVariable();
564     env->SubCfgExit();
565     return ret;
566 }
567 
IndexOfStringElements(GateRef glue,GateRef elements,GateRef target,GateRef fromIndex,GateRef len,IndexOfOptions options,StringElementsCondition condition)568 GateRef BuiltinsArrayStubBuilder::IndexOfStringElements(
569     GateRef glue, GateRef elements, GateRef target, GateRef fromIndex, GateRef len,
570     IndexOfOptions options, StringElementsCondition condition)
571 {
572     // assume: ElementsKind is STRING or HOLE_STRING
573     ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), BoolNot(TaggedIsUndefined(target)));
574 
575     auto env = GetEnvironment();
576     Label entry(env);
577     env->SubCfgEntry(&entry);
578     Label targetIsString(env);
579     Label exit(env);
580     Variable result = MakeResultVariableDefaultNotFound(options);
581 
582     BRANCH_LIKELY(TaggedIsString(target), &targetIsString, &exit);
583     Bind(&targetIsString);
584     result = IndexOfElements(elements, [this, glue, condition, target](GateRef curValue) {
585         return StringEqual(glue, target, curValue, condition);
586     }, fromIndex, len, options);
587     Jump(&exit);
588 
589     Bind(&exit);
590     GateRef ret = result.ReadVariable();
591     env->SubCfgExit();
592     return ret;
593 }
594 
IndexOfStringTarget(GateRef glue,GateRef elements,GateRef target,GateRef fromIndex,GateRef len,IndexOfOptions options)595 GateRef BuiltinsArrayStubBuilder::IndexOfStringTarget(
596     GateRef glue, GateRef elements, GateRef target, GateRef fromIndex, GateRef len, IndexOfOptions options)
597 {
598     ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), TaggedIsString(target));
599     return IndexOfElements(elements, [this, glue, target](GateRef curValue) {
600         return StringEqual(glue, target, curValue, StringElementsCondition::MAY_BE_ANY);
601     }, fromIndex, len, options);
602 }
603 
IndexOfBigInt(GateRef glue,GateRef elements,GateRef target,GateRef fromIndex,GateRef len,IndexOfOptions options)604 GateRef BuiltinsArrayStubBuilder::IndexOfBigInt(
605     GateRef glue, GateRef elements, GateRef target, GateRef fromIndex, GateRef len, IndexOfOptions options)
606 {
607     ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), TaggedIsBigInt(target));
608     return IndexOfElements(elements, [this, glue, target](GateRef curValue) {
609         return BigIntEqual(glue, target, curValue);
610     }, fromIndex, len, options);
611 }
612 
IndexOfObject(GateRef elements,GateRef target,GateRef fromIndex,GateRef len,IndexOfOptions options)613 GateRef BuiltinsArrayStubBuilder::IndexOfObject(
614     GateRef elements, GateRef target, GateRef fromIndex, GateRef len, IndexOfOptions options)
615 {
616     return IndexOfElements(elements, [this, target](GateRef curValue) {
617         return Equal(target, curValue);
618     }, fromIndex, len, options);
619 }
620 
IndexOfBigIntOrObjectElements(GateRef glue,GateRef elements,GateRef target,GateRef fromIndex,GateRef len,IndexOfOptions options)621 GateRef BuiltinsArrayStubBuilder::IndexOfBigIntOrObjectElements(
622     GateRef glue, GateRef elements, GateRef target, GateRef fromIndex, GateRef len, IndexOfOptions options)
623 {
624     // assume: ElementsKind is OBJECT or HOLE_OBJECT
625     ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), BoolNot(TaggedIsUndefined(target)));
626 
627     auto env = GetEnvironment();
628     Label entry(env);
629     env->SubCfgEntry(&entry);
630     Label targetIsBigInt(env);
631     Label targetIsNotBigInt(env);
632     Label targetIsObject(env);
633     Label exit(env);
634     Variable result = MakeResultVariableDefaultNotFound(options);
635 
636     BRANCH(TaggedIsBigInt(target), &targetIsBigInt, &targetIsNotBigInt);
637     Bind(&targetIsBigInt);
638     result = IndexOfBigInt(glue, elements, target, fromIndex, len, options);
639     Jump(&exit);
640     Bind(&targetIsNotBigInt);
641     BRANCH(TaggedIsObject(target), &targetIsObject, &exit);
642     // Including true, false, null
643     Bind(&targetIsObject);
644     result = IndexOfObject(elements, target, fromIndex, len, options);
645     Jump(&exit);
646 
647     Bind(&exit);
648     GateRef ret = result.ReadVariable();
649     env->SubCfgExit();
650     return ret;
651 }
652 
IndexOfBigIntOrObjectTarget(GateRef glue,GateRef elements,GateRef target,GateRef fromIndex,GateRef len,IndexOfOptions options)653 GateRef BuiltinsArrayStubBuilder::IndexOfBigIntOrObjectTarget(
654     GateRef glue, GateRef elements, GateRef target, GateRef fromIndex, GateRef len, IndexOfOptions options)
655 {
656     auto env = GetEnvironment();
657     Label entry(env);
658     env->SubCfgEntry(&entry);
659     Label targetIsBigInt(env);
660     Label targetIsObject(env);
661     Label exit(env);
662     Variable result = MakeResultVariableDefaultNotFound(options);
663 
664     BRANCH(TaggedIsBigInt(target), &targetIsBigInt, &targetIsObject);
665     Bind(&targetIsBigInt);
666     result = IndexOfBigInt(glue, elements, target, fromIndex, len, options);
667     Jump(&exit);
668     // Including true, false, null
669     Bind(&targetIsObject);
670     result = IndexOfObject(elements, target, fromIndex, len, options);
671     Jump(&exit);
672 
673     Bind(&exit);
674     GateRef ret = result.ReadVariable();
675     env->SubCfgExit();
676     return ret;
677 }
678 
IndexOfGeneric(GateRef glue,GateRef elements,GateRef target,GateRef fromIndex,GateRef len,IndexOfOptions options)679 GateRef BuiltinsArrayStubBuilder::IndexOfGeneric(
680     GateRef glue, GateRef elements, GateRef target, GateRef fromIndex, GateRef len, IndexOfOptions options)
681 {
682     ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), BoolNot(TaggedIsUndefined(target)));
683 
684     auto env = GetEnvironment();
685     Label entry(env);
686     env->SubCfgEntry(&entry);
687     Label exit(env);
688     Variable result = MakeResultVariableDefaultNotFound(options);
689 
690     Label intBranch(env);
691     Label targetNotInt(env);
692     BRANCH(TaggedIsInt(target), &intBranch, &targetNotInt);
693     Bind(&intBranch);
694     result = IndexOfTaggedIntTarget(elements, target, fromIndex, len, options);
695     Jump(&exit);
696 
697     Bind(&targetNotInt);
698     Label doubleBranch(env);
699     Label targetNotNumber(env);
700     BRANCH(TaggedIsDouble(target), &doubleBranch, &targetNotNumber);
701     Bind(&doubleBranch);
702     result = IndexOfTaggedNumber(elements, target, fromIndex, len, options, true);
703     Jump(&exit);
704 
705     Bind(&targetNotNumber);
706     Label stringBranch(env);
707     Label bigIntOrObjectBranch(env);
708     BRANCH(TaggedIsString(target), &stringBranch, &bigIntOrObjectBranch);
709     Bind(&stringBranch);
710     result = IndexOfStringTarget(glue, elements, target, fromIndex, len, options);
711     Jump(&exit);
712 
713     Bind(&bigIntOrObjectBranch);
714     result = IndexOfBigIntOrObjectTarget(glue, elements, target, fromIndex, len, options);
715     Jump(&exit);
716 
717     Bind(&exit);
718     GateRef ret = result.ReadVariable();
719     env->SubCfgExit();
720     return ret;
721 }
722 } // namespace panda::ecmascript::kungfu
723