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, ¬WithinInt32Branch);
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(¬WithinInt32Branch);
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