* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ecmascript/compiler/builtins/builtins_array_stub_builder.h"
#include "ecmascript/compiler/builtins/builtins_string_stub_builder.h"
#include "ecmascript/compiler/circuit_builder-inl.h"
namespace panda::ecmascript::kungfu {
BuiltinsArrayStubBuilder::IndexOfOptions BuiltinsArrayStubBuilder::MakeIndexOfOptions(
BuiltinsStubCSigns::ID id, bool resultIsTagged)
{
IndexOfOptions options;
switch (id) {
case BuiltinsStubCSigns::ID::ArrayIndexOf:
case BuiltinsStubCSigns::ID::ArrayLastIndexOf:
options.compType = ComparisonType::STRICT_EQUAL;
options.returnType = resultIsTagged
? IndexOfReturnType::TAGGED_FOUND_INDEX
: IndexOfReturnType::UNTAGGED_FOUND_INDEX;
options.holeAsUndefined = false;
options.reversedOrder = (id == BuiltinsStubCSigns::ID::ArrayLastIndexOf);
break;
case BuiltinsStubCSigns::ID::ArrayIncludes:
options.compType = ComparisonType::SAME_VALUE_ZERO;
options.returnType = resultIsTagged
? IndexOfReturnType::TAGGED_FOUND_OR_NOT
: IndexOfReturnType::UNTAGGED_FOUND_OR_NOT;
options.holeAsUndefined = true;
options.reversedOrder = false;
break;
default:
LOG_ECMA(FATAL) << "Implementation Error: Unreachable branch.";
UNREACHABLE();
}
return options;
}
GateRef BuiltinsArrayStubBuilder::MakeFromIndex(GateRef input, GateRef length, bool reversedOrder)
{
ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), TaggedIsNumber(input));
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label inputIsTaggedInt(env);
Label inputIsTaggedDouble(env);
Label exit(env);
DEFVARIABLE(result, VariableType::INT64(), Int64(0));
BRANCH_LIKELY(TaggedIsInt(input), &inputIsTaggedInt, &inputIsTaggedDouble);
Bind(&inputIsTaggedInt);
result.WriteVariable(MakeFromIndexWithInt(TaggedGetInt(input), length, reversedOrder));
Jump(&exit);
Bind(&inputIsTaggedDouble);
result.WriteVariable(MakeFromIndexWithDouble(GetDoubleOfTDouble(input), length, reversedOrder));
Jump(&exit);
Bind(&exit);
GateRef ret = result.ReadVariable();
env->SubCfgExit();
return ret;
}
GateRef BuiltinsArrayStubBuilder::MakeFromIndexWithInt(GateRef intValue, GateRef length, bool reversedOrder)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label reachesLength(env);
Label lessThanLength(env);
Label isNegative(env);
Label plusLengthIsNegative(env);
Label plusLengthIsNonNegative(env);
Label exit(env);
GateRef i64Value = SExtInt32ToInt64(intValue);
GateRef i64Length = ZExtInt32ToInt64(length);
DEFVARIABLE(result, VariableType::INT64(), i64Value);
BRANCH_UNLIKELY(Int64GreaterThanOrEqual(i64Value, i64Length), &reachesLength, &lessThanLength);
Bind(&reachesLength);
if (reversedOrder) {
result.WriteVariable(Int64Sub(i64Length, Int64(1)));
} else {
result.WriteVariable(i64Length);
}
Jump(&exit);
Bind(&lessThanLength);
BRANCH(Int64LessThan(i64Value, Int64(0)), &isNegative, &exit);
Bind(&isNegative);
GateRef i64PlusLength = Int64Add(i64Value, i64Length);
BRANCH(Int64LessThan(i64PlusLength, Int64(0)), &plusLengthIsNegative, &plusLengthIsNonNegative);
Bind(&plusLengthIsNegative);
if (reversedOrder) {
result.WriteVariable(Int64(-1));
} else {
result.WriteVariable(Int64(0));
}
Jump(&exit);
Bind(&plusLengthIsNonNegative);
result.WriteVariable(i64PlusLength);
Jump(&exit);
Bind(&exit);
GateRef ret = result.ReadVariable();
env->SubCfgExit();
return ret;
}
GateRef BuiltinsArrayStubBuilder::MakeFromIndexWithDouble(GateRef doubleValue, GateRef length, bool reversedOrder)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label isNotNAN(env);
Label reachesLength(env);
Label lessThanLength(env);
Label isNonNegativeAfterTruncation(env);
Label isNegative(env);
Label plusLengthIsNonNegative(env);
Label plusLengthIsNegative(env);
Label exit(env);
DEFVARIABLE(result, VariableType::INT64(), Int64(0));
BRANCH_UNLIKELY(DoubleIsNAN(doubleValue), &exit, &isNotNAN);
Bind(&isNotNAN);
GateRef f64Length = ChangeUInt32ToFloat64(length);
BRANCH(DoubleGreaterThanOrEqual(doubleValue, f64Length), &reachesLength, &lessThanLength);
Bind(&reachesLength);
GateRef i64Length = ZExtInt32ToInt64(length);
if (reversedOrder) {
result.WriteVariable(Int64Sub(i64Length, Int64(1)));
} else {
result.WriteVariable(i64Length);
}
Jump(&exit);
Bind(&lessThanLength);
GateRef trunkValue = DoubleTrunc(doubleValue);
BRANCH(DoubleGreaterThanOrEqual(trunkValue, Double(0.0)), &isNonNegativeAfterTruncation, &isNegative);
Bind(&isNonNegativeAfterTruncation);
result.WriteVariable(ZExtInt32ToInt64(ChangeFloat64ToInt32(trunkValue)));
Jump(&exit);
Bind(&isNegative);
GateRef plusLength = DoubleAdd(trunkValue, f64Length);
BRANCH(DoubleGreaterThanOrEqual(plusLength, Double(0.0)), &plusLengthIsNonNegative, &plusLengthIsNegative);
Bind(&plusLengthIsNonNegative);
result.WriteVariable(ZExtInt32ToInt64(ChangeFloat64ToInt32(plusLength)));
Jump(&exit);
Bind(&plusLengthIsNegative);
if (reversedOrder) {
result.WriteVariable(Int64(-1));
}
Jump(&exit);
Bind(&exit);
GateRef ret = result.ReadVariable();
env->SubCfgExit();
return ret;
}
Variable BuiltinsArrayStubBuilder::MakeResultVariableDefaultNotFound(IndexOfOptions options)
{
VariableType type;
GateRef value;
switch (options.returnType) {
case IndexOfReturnType::TAGGED_FOUND_INDEX:
type = VariableType::JS_ANY();
value = IntToTaggedPtr(Int32(-1));
break;
case IndexOfReturnType::TAGGED_FOUND_OR_NOT:
type = VariableType::JS_ANY();
value = TaggedFalse();
break;
case IndexOfReturnType::UNTAGGED_FOUND_INDEX:
type = VariableType::INT32();
value = Int32(-1);
break;
case IndexOfReturnType::UNTAGGED_FOUND_OR_NOT:
type = VariableType::BOOL();
value = False();
break;
default:
LOG_ECMA(FATAL) << "Implementation Error: Unreachable branch.";
UNREACHABLE();
}
return Variable(GetEnvironment(), type, NextVariableId(), value);
}
template <class Predicate>
GateRef BuiltinsArrayStubBuilder::IndexOfElements(
GateRef glue, GateRef elements, Predicate predicate, GateRef fromIndex, GateRef len, IndexOfOptions options)
{
static_assert(std::is_invocable_r_v<GateRef, Predicate, GateRef>, "Invalid call signature.");
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label loopHead(env);
Label loopBodyBegin(env);
Label loopBodyEnd(env);
Label loopExitFound(env);
Label exit(env);
GateRef sizeBytesPerElement = IntPtr(JSTaggedValue::TaggedTypeSize());
auto indexToOffset = [this, sizeBytesPerElement](GateRef i64Index) {
return PtrAdd(
PtrMul(sizeBytesPerElement, ChangeInt64ToIntPtr(i64Index)),
IntPtr(TaggedArray::DATA_OFFSET));
};
GateRef boundOffset;
GateRef initialOffset;
if (options.reversedOrder) {
boundOffset = IntPtr(TaggedArray::DATA_OFFSET);
initialOffset = indexToOffset(fromIndex);
} else {
initialOffset = indexToOffset(fromIndex);
boundOffset = indexToOffset(ZExtInt32ToInt64(len));
}
DEFVARIABLE(curOffset, VariableType::NATIVE_POINTER(), initialOffset);
Variable result = MakeResultVariableDefaultNotFound(options);
Jump(&loopHead);
LoopBegin(&loopHead);
{
GateRef continues = options.reversedOrder
? IntPtrGreaterThanOrEqual(*curOffset, boundOffset)
: IntPtrLessThan(*curOffset, boundOffset);
BRANCH_LIKELY(continues, &loopBodyBegin, &exit);
Bind(&loopBodyBegin);
GateRef curValue = Load(VariableType::JS_ANY(), glue, elements, *curOffset);
GateRef curValueMatches = std::invoke(predicate, curValue);
BRANCH_UNLIKELY(curValueMatches, &loopExitFound, &loopBodyEnd);
Bind(&loopBodyEnd);
if (options.reversedOrder) {
curOffset = PtrSub(*curOffset, sizeBytesPerElement);
} else {
curOffset = PtrAdd(*curOffset, sizeBytesPerElement);
}
}
LoopEnd(&loopHead);
Bind(&loopExitFound);
switch (options.returnType) {
case IndexOfReturnType::TAGGED_FOUND_INDEX:
case IndexOfReturnType::UNTAGGED_FOUND_INDEX: {
GateRef iptrIndex = IntPtrDiv(
PtrSub(*curOffset, IntPtr(TaggedArray::DATA_OFFSET)),
sizeBytesPerElement);
GateRef i32Index = ChangeIntPtrToInt32(iptrIndex);
GateRef resultIndex = (options.returnType == IndexOfReturnType::TAGGED_FOUND_INDEX)
? IntToTaggedPtr(i32Index)
: i32Index;
result.WriteVariable(resultIndex);
break;
}
case IndexOfReturnType::TAGGED_FOUND_OR_NOT:
result.WriteVariable(TaggedTrue());
break;
case IndexOfReturnType::UNTAGGED_FOUND_OR_NOT:
result.WriteVariable(True());
break;
default:
LOG_ECMA(FATAL) << "Implementation Error: Unreachable branch.";
UNREACHABLE();
}
Jump(&exit);
Bind(&exit);
GateRef ret = result.ReadVariable();
env->SubCfgExit();
return ret;
}
GateRef BuiltinsArrayStubBuilder::StringEqual(
GateRef glue, GateRef left, GateRef right, StringElementsCondition rightCondition)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label rightIsString(env);
Label addrEquals(env);
Label addrNotEquals(env);
Label exit(env);
DEFVARIABLE(result, VariableType::BOOL(), False());
if (rightCondition == StringElementsCondition::MAY_BE_ANY) {
BRANCH(TaggedIsString(glue, right), &rightIsString, &exit);
Bind(&rightIsString);
} else if (rightCondition == StringElementsCondition::MAY_BE_HOLE) {
BRANCH(TaggedIsNotHole(right), &rightIsString, &exit);
Bind(&rightIsString);
}
BRANCH_UNLIKELY(Equal(left, right), &addrEquals, &addrNotEquals);
Bind(&addrEquals);
result.WriteVariable(True());
Jump(&exit);
Bind(&addrNotEquals);
BuiltinsStringStubBuilder stringBuilder(env, GetCurrentGlobalEnv());
result.WriteVariable(stringBuilder.FastStringEqualWithoutRTStub(glue, left, right));
Jump(&exit);
Bind(&exit);
GateRef ret = result.ReadVariable();
env->SubCfgExit();
return ret;
}
GateRef BuiltinsArrayStubBuilder::BigIntEqual(GateRef glue, GateRef left, GateRef right)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
DEFVARIABLE(predResult, VariableType::BOOL(), False());
Label addrEquals(env);
Label addrNotEquals(env);
Label rightIsBigInt(env);
Label exit(env);
BRANCH(Equal(left, right), &addrEquals, &addrNotEquals);
Bind(&addrEquals);
predResult.WriteVariable(True());
Jump(&exit);
Bind(&addrNotEquals);
BRANCH(TaggedIsBigInt(glue, right), &rightIsBigInt, &exit);
Bind(&rightIsBigInt);
predResult.WriteVariable(CallNGCRuntime(glue, RTSTUB_ID(BigIntSameValueZero), { left, right }));
Jump(&exit);
Bind(&exit);
GateRef ret = predResult.ReadVariable();
env->SubCfgExit();
return ret;
}
GateRef BuiltinsArrayStubBuilder::IndexOfTaggedUndefined(
GateRef glue, GateRef elements, GateRef fromIndex, GateRef len, IndexOfOptions options)
{
if (options.holeAsUndefined) {
return IndexOfElements(glue, elements, [this](GateRef curValue) {
return BitOr(TaggedIsUndefined(curValue), TaggedIsHole(curValue));
}, fromIndex, len, options);
}
return IndexOfElements(glue, elements, [this](GateRef curValue) {
return TaggedIsUndefined(curValue);
}, fromIndex, len, options);
}
GateRef BuiltinsArrayStubBuilder::IndexOfTaggedZero(
GateRef glue, GateRef elements, GateRef fromIndex, GateRef len, IndexOfOptions options)
{
auto env = GetEnvironment();
return IndexOfElements(glue, elements, [this, env](GateRef curValue) {
GateRef underlying = ChangeTaggedPointerToInt64(curValue);
return LogicOrBuilder(env)
.Or(Equal(Int64(JSTaggedValue::VALUE_ZERO), underlying))
.Or(Equal(Int64(JSTaggedValue::VALUE_POSITIVE_ZERO), underlying))
.Or(Equal(Int64(JSTaggedValue::VALUE_NEGATIVE_ZERO), underlying))
.Done();
}, fromIndex, len, options);
}
GateRef BuiltinsArrayStubBuilder::IndexOfTaggedIntElements(
GateRef glue, GateRef elements, GateRef target, GateRef fromIndex, GateRef len, IndexOfOptions options)
{
ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), BoolNot(TaggedIsUndefined(target)));
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label targetIsInt(env);
Label targetIsNotInt(env);
Label targetIsDouble(env);
Label targetIsWithinInt32(env);
Label exit(env);
Variable result = MakeResultVariableDefaultNotFound(options);
BRANCH_LIKELY(TaggedIsInt(target), &targetIsInt, &targetIsNotInt);
Bind(&targetIsInt);
result = IndexOfElements(glue, elements, [this, ti32 = target](GateRef curValue) {
return Equal(curValue, ti32);
}, fromIndex, len, options);
Jump(&exit);
Bind(&targetIsNotInt);
BRANCH_LIKELY(TaggedIsDouble(target), &targetIsDouble, &exit);
Bind(&targetIsDouble);
GateRef doubleValue = GetDoubleOfTDouble(target);
BRANCH(DoubleIsWithinInt32(doubleValue), &targetIsWithinInt32, &exit);
Bind(&targetIsWithinInt32);
GateRef taggedInt32 = IntToTaggedPtr(ChangeFloat64ToInt32(doubleValue));
result = IndexOfElements(glue, elements, [this, ti32 = taggedInt32](GateRef curValue) {
return Equal(curValue, ti32);
}, fromIndex, len, options);
Jump(&exit);
Bind(&exit);
GateRef ret = result.ReadVariable();
env->SubCfgExit();
return ret;
}
GateRef BuiltinsArrayStubBuilder::IndexOfTaggedIntTarget(
GateRef glue, GateRef elements, GateRef target, GateRef fromIndex, GateRef len, IndexOfOptions options)
{
ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), TaggedIsInt(target));
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label isZero(env);
Label isNotZero(env);
Label exit(env);
Variable result = MakeResultVariableDefaultNotFound(options);
GateRef int32Value = TaggedGetInt(target);
BRANCH(Int32Equal(Int32(0), int32Value), &isZero, &isNotZero);
Bind(&isZero);
result = IndexOfTaggedZero(glue, elements, fromIndex, len, options);
Jump(&exit);
Bind(&isNotZero);
GateRef taggedDouble = DoubleToTaggedDoublePtr(ChangeInt32ToFloat64(int32Value));
result = IndexOfElements(glue, elements, [this, ti32 = target, tf64 = taggedDouble](GateRef curValue) {
return BitOr(Equal(curValue, ti32), Equal(curValue, tf64));
}, fromIndex, len, options);
Jump(&exit);
Bind(&exit);
GateRef ret = result.ReadVariable();
env->SubCfgExit();
return ret;
}
GateRef BuiltinsArrayStubBuilder::IndexOfTaggedNumber(
GateRef glue, GateRef elements, GateRef target, GateRef fromIndex, GateRef len,
IndexOfOptions options, bool targetIsAlwaysDouble)
{
if (targetIsAlwaysDouble) {
ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), TaggedIsDouble(target));
} else {
ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), BoolNot(TaggedIsUndefined(target)));
}
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label exit(env);
Label targetIsWithinInt32(env);
Label targetDone(env);
Label targetIsNaN(env);
Label targetIsNotNaN(env);
Variable result = MakeResultVariableDefaultNotFound(options);
DEFVARIABLE(doubleValue, VariableType::FLOAT64(), Double(0.0));
DEFVARIABLE(taggedFloat, VariableType::JS_ANY(), Undefined());
DEFVARIABLE(taggedInt32, VariableType::JS_ANY(), Undefined());
if (!targetIsAlwaysDouble) {
Label targetIsInt(env);
Label targetIsNotInt(env);
Label targetIsDouble(env);
BRANCH(TaggedIsInt(target), &targetIsInt, &targetIsNotInt);
Bind(&targetIsInt);
doubleValue = ChangeInt32ToFloat64(TaggedGetInt(target));
taggedFloat = DoubleToTaggedDoublePtr(*doubleValue);
taggedInt32 = target;
Jump(&targetDone);
Bind(&targetIsNotInt);
BRANCH_LIKELY(TaggedIsDouble(target), &targetIsDouble, &exit);
Bind(&targetIsDouble);
}
doubleValue = GetDoubleOfTDouble(target);
taggedFloat = target;
BRANCH(DoubleIsWithinInt32(*doubleValue), &targetIsWithinInt32, &targetDone);
Bind(&targetIsWithinInt32);
taggedInt32 = IntToTaggedPtr(ChangeFloat64ToInt32(*doubleValue));
Jump(&targetDone);
Bind(&targetDone);
BRANCH_UNLIKELY(DoubleIsNAN(*doubleValue), &targetIsNaN, &targetIsNotNaN);
Bind(&targetIsNaN);
if (options.compType == ComparisonType::SAME_VALUE_ZERO) {
result = IndexOfElements(glue, elements, [this](GateRef curValue) {
return BitAnd(TaggedIsDouble(curValue), DoubleIsNAN(GetDoubleOfTDouble(curValue)));
}, fromIndex, len, options);
}
Jump(&exit);
Bind(&targetIsNotNaN);
Label isZero(env);
Label isNotZero(env);
Label withinInt32Branch(env);
Label notWithinInt32Branch(env);
BRANCH(DoubleEqual(Double(0.0), *doubleValue), &isZero, &isNotZero);
Bind(&isZero);
result = IndexOfTaggedZero(glue, elements, fromIndex, len, options);
Jump(&exit);
Bind(&isNotZero);
BRANCH(TaggedIsInt(*taggedInt32), &withinInt32Branch, ¬WithinInt32Branch);
Bind(&withinInt32Branch);
{
auto predicate = [this, ti32 = *taggedInt32, tf64 = *taggedFloat](GateRef curValue) {
return BitOr(Equal(curValue, ti32), Equal(curValue, tf64));
};
result = IndexOfElements(glue, elements, predicate, fromIndex, len, options);
Jump(&exit);
}
Bind(¬WithinInt32Branch);
result = IndexOfElements(glue, elements, [this, tf64 = *taggedFloat](GateRef curValue) {
return Equal(curValue, tf64);
}, fromIndex, len, options);
Jump(&exit);
Bind(&exit);
GateRef ret = result.ReadVariable();
env->SubCfgExit();
return ret;
}
GateRef BuiltinsArrayStubBuilder::IndexOfStringElements(
GateRef glue, GateRef elements, GateRef target, GateRef fromIndex, GateRef len,
IndexOfOptions options, StringElementsCondition condition)
{
ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), BoolNot(TaggedIsUndefined(target)));
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label targetIsString(env);
Label exit(env);
Variable result = MakeResultVariableDefaultNotFound(options);
BRANCH_LIKELY(TaggedIsString(glue, target), &targetIsString, &exit);
Bind(&targetIsString);
result = IndexOfElements(glue, elements, [this, glue, condition, target](GateRef curValue) {
return StringEqual(glue, target, curValue, condition);
}, fromIndex, len, options);
Jump(&exit);
Bind(&exit);
GateRef ret = result.ReadVariable();
env->SubCfgExit();
return ret;
}
GateRef BuiltinsArrayStubBuilder::IndexOfStringTarget(
GateRef glue, GateRef elements, GateRef target, GateRef fromIndex, GateRef len, IndexOfOptions options)
{
ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), TaggedIsString(glue, target));
return IndexOfElements(glue, elements, [this, glue, target](GateRef curValue) {
return StringEqual(glue, target, curValue, StringElementsCondition::MAY_BE_ANY);
}, fromIndex, len, options);
}
GateRef BuiltinsArrayStubBuilder::IndexOfBigInt(
GateRef glue, GateRef elements, GateRef target, GateRef fromIndex, GateRef len, IndexOfOptions options)
{
ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), TaggedIsBigInt(glue, target));
return IndexOfElements(glue, elements, [this, glue, target](GateRef curValue) {
return BigIntEqual(glue, target, curValue);
}, fromIndex, len, options);
}
GateRef BuiltinsArrayStubBuilder::IndexOfObject(
GateRef glue, GateRef elements, GateRef target, GateRef fromIndex, GateRef len, IndexOfOptions options)
{
return IndexOfElements(glue, elements, [this, target](GateRef curValue) {
return Equal(target, curValue);
}, fromIndex, len, options);
}
GateRef BuiltinsArrayStubBuilder::IndexOfBigIntOrObjectElements(
GateRef glue, GateRef elements, GateRef target, GateRef fromIndex, GateRef len, IndexOfOptions options)
{
ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), BoolNot(TaggedIsUndefined(target)));
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label targetIsBigInt(env);
Label targetIsNotBigInt(env);
Label targetIsObject(env);
Label exit(env);
Variable result = MakeResultVariableDefaultNotFound(options);
BRANCH(TaggedIsBigInt(glue, target), &targetIsBigInt, &targetIsNotBigInt);
Bind(&targetIsBigInt);
result = IndexOfBigInt(glue, elements, target, fromIndex, len, options);
Jump(&exit);
Bind(&targetIsNotBigInt);
BRANCH(TaggedIsObject(target), &targetIsObject, &exit);
Bind(&targetIsObject);
result = IndexOfObject(glue, elements, target, fromIndex, len, options);
Jump(&exit);
Bind(&exit);
GateRef ret = result.ReadVariable();
env->SubCfgExit();
return ret;
}
GateRef BuiltinsArrayStubBuilder::IndexOfBigIntOrObjectTarget(
GateRef glue, GateRef elements, GateRef target, GateRef fromIndex, GateRef len, IndexOfOptions options)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label targetIsBigInt(env);
Label targetIsObject(env);
Label exit(env);
Variable result = MakeResultVariableDefaultNotFound(options);
BRANCH(TaggedIsBigInt(glue, target), &targetIsBigInt, &targetIsObject);
Bind(&targetIsBigInt);
result = IndexOfBigInt(glue, elements, target, fromIndex, len, options);
Jump(&exit);
Bind(&targetIsObject);
result = IndexOfObject(glue, elements, target, fromIndex, len, options);
Jump(&exit);
Bind(&exit);
GateRef ret = result.ReadVariable();
env->SubCfgExit();
return ret;
}
GateRef BuiltinsArrayStubBuilder::IndexOfGeneric(
GateRef glue, GateRef elements, GateRef target, GateRef fromIndex, GateRef len, IndexOfOptions options)
{
ASM_ASSERT(GET_MESSAGE_STRING_ID(ArrayIndexOf), BoolNot(TaggedIsUndefined(target)));
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label exit(env);
Variable result = MakeResultVariableDefaultNotFound(options);
Label intBranch(env);
Label targetNotInt(env);
BRANCH(TaggedIsInt(target), &intBranch, &targetNotInt);
Bind(&intBranch);
result = IndexOfTaggedIntTarget(glue, elements, target, fromIndex, len, options);
Jump(&exit);
Bind(&targetNotInt);
Label doubleBranch(env);
Label targetNotNumber(env);
BRANCH(TaggedIsDouble(target), &doubleBranch, &targetNotNumber);
Bind(&doubleBranch);
result = IndexOfTaggedNumber(glue, elements, target, fromIndex, len, options, true);
Jump(&exit);
Bind(&targetNotNumber);
Label stringBranch(env);
Label bigIntOrObjectBranch(env);
BRANCH(TaggedIsString(glue, target), &stringBranch, &bigIntOrObjectBranch);
Bind(&stringBranch);
result = IndexOfStringTarget(glue, elements, target, fromIndex, len, options);
Jump(&exit);
Bind(&bigIntOrObjectBranch);
result = IndexOfBigIntOrObjectTarget(glue, elements, target, fromIndex, len, options);
Jump(&exit);
Bind(&exit);
GateRef ret = result.ReadVariable();
env->SubCfgExit();
return ret;
}
}