%PDF- %PDF-
| Direktori : /home2/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/builtins/ |
| Current File : //home2/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/builtins/number.tq |
// Copyright 2019 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.
#include 'src/ic/binary-op-assembler.h'
extern enum Operation extends uint31 {
// Binary operations.
kAdd,
kSubtract,
kMultiply,
kDivide,
kModulus,
kExponentiate,
kBitwiseAnd,
kBitwiseOr,
kBitwiseXor,
kShiftLeft,
kShiftRight,
kShiftRightLogical,
// Unary operations.
kBitwiseNot,
kNegate,
kIncrement,
kDecrement,
// Compare operations.
kEqual,
kStrictEqual,
kLessThan,
kLessThanOrEqual,
kGreaterThan,
kGreaterThanOrEqual
}
namespace runtime {
extern transitioning runtime DoubleToStringWithRadix(
implicit context: Context)(Number, Number): String;
extern transitioning runtime StringParseFloat(
implicit context: Context)(String): Number;
extern transitioning runtime StringParseInt(
implicit context: Context)(JSAny, JSAny): Number;
extern runtime BigIntUnaryOp(Context, BigInt, SmiTagged<Operation>): BigInt;
extern runtime BigIntBinaryOp(Context, Numeric, Numeric, SmiTagged<Operation>):
BigInt;
} // namespace runtime
namespace number {
extern macro NaNStringConstant(): String;
extern macro ZeroStringConstant(): String;
extern macro InfinityStringConstant(): String;
extern macro MinusInfinityStringConstant(): String;
extern macro Log10OffsetTable(): RawPtr<uint64>;
transitioning macro ThisNumberValue(
implicit context: Context)(receiver: JSAny,
method: constexpr string): Number {
return UnsafeCast<Number>(
ToThisValue(receiver, PrimitiveType::kNumber, method));
}
macro ToCharCode(input: uint32): char8 {
dcheck(input < 36);
// 48 == '0', 97 == 'a'.
return input < 10 ? %RawDownCast<char8>(input + 48) :
%RawDownCast<char8>(input - 10 + 97);
}
macro IntToDecimalStringImpl(
x: int32, log10OffsetsTable: RawPtr<uint64>,
isPositive: constexpr bool): String {
dcheck(isPositive == (x >= 0));
let n: uint32 = isPositive ? Unsigned(x) : Unsigned(0 - x);
const log2: int32 = 31 - math::Word32Clz(Signed(n) | 1);
const tableEntry: uint64 = log10OffsetsTable[Convert<intptr>(log2)];
const digitCount: uint64 = (Convert<uint64>(n) + tableEntry) >>> 32;
let length = Convert<uint32>(digitCount);
if constexpr (!isPositive) length++; // For the '-'.
const string = AllocateNonEmptySeqOneByteString(length);
if constexpr (isPositive) {
string.raw_hash_field = MakeArrayIndexHash(n, length);
}
const lengthIntptr = Convert<intptr>(Signed(length));
let cursor: intptr = lengthIntptr - 1;
const rawChars = &string.chars;
while (true) {
const kInverse: uint64 = 0xcccccccd;
const quotient = Convert<uint32>((Convert<uint64>(n) * kInverse) >>> 35);
const remainder = n - quotient * 10;
const nextChar = %RawDownCast<char8>(remainder | 48); // 48 == '0'
// Writing to string.chars[cursor] directly would implicitly emit a
// bounds check, and we don't want no bounds check, thank you very much.
*UnsafeConstCast(rawChars.UncheckedAtIndex(cursor)) = nextChar;
cursor--;
n = quotient;
if (n == 0) break;
}
if constexpr (!isPositive) {
*UnsafeConstCast(rawChars.UncheckedAtIndex(0)) = 45; // 45 == '-'
}
return string;
}
@export
macro IntToDecimalString(x: int32): String {
if constexpr (Is64()) {
const log10OffsetsTable: RawPtr<uint64> = Log10OffsetTable();
if (x >= 0) {
if (x < 10) {
if (x == 0) {
return ZeroStringConstant();
}
return StringFromSingleCharCode(ToCharCode(Unsigned(x)));
}
return IntToDecimalStringImpl(x, log10OffsetsTable, true);
} else {
return IntToDecimalStringImpl(x, log10OffsetsTable, false);
}
} else {
// The generic implementation doesn't rely on 64-bit instructions.
return IntToString(x, 10);
}
}
macro IntToString(x: int32, radix: uint32): String {
if constexpr (Is64()) {
dcheck(radix != 10); // Use IntToDecimalString otherwise.
}
const isNegative: bool = x < 0;
let n: uint32;
if (!isNegative) {
// Fast case where the result is a one character string.
n = Unsigned(x);
if (n < radix) {
if (n == 0) {
return ZeroStringConstant();
}
return StringFromSingleCharCode(ToCharCode(n));
}
} else {
dcheck(isNegative);
n = Unsigned(0 - x);
}
// Calculate length and pre-allocate the result string.
let temp: uint32 = n;
let length: int32 = isNegative ? Convert<int32>(1) : Convert<int32>(0);
while (temp > 0) {
temp = temp / radix;
length = length + 1;
}
dcheck(length > 0);
const strSeq = AllocateNonEmptySeqOneByteString(Unsigned(length));
let cursor: intptr = Convert<intptr>(length) - 1;
while (n > 0) {
const digit: uint32 = n % radix;
n = n / radix;
*UnsafeConstCast(&strSeq.chars[cursor]) = ToCharCode(digit);
cursor = cursor - 1;
}
if (isNegative) {
dcheck(cursor == 0);
// Insert '-' to result.
*UnsafeConstCast(&strSeq.chars[0]) = 45;
} else {
dcheck(cursor == -1);
if constexpr (!Is64()) {
if (radix == 10) {
dcheck(strSeq.raw_hash_field == kNameEmptyHashField);
strSeq.raw_hash_field =
MakeArrayIndexHash(Unsigned(x), Unsigned(length));
}
}
}
return strSeq;
}
// https://tc39.github.io/ecma262/#sec-number.prototype.tostring
transitioning javascript builtin NumberPrototypeToString(
js-implicit context: NativeContext, receiver: JSAny)(
...arguments): String {
// 1. Let x be ? thisNumberValue(this value).
const x = ThisNumberValue(receiver, 'Number.prototype.toString');
// 2. If radix is not present, let radixNumber be 10.
// 3. Else if radix is undefined, let radixNumber be 10.
// 4. Else, let radixNumber be ? ToInteger(radix).
const radix: JSAny = arguments[0];
const radixNumber: Number = radix == Undefined ? 10 : ToInteger_Inline(radix);
// 5. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception.
if (radixNumber < 2 || radixNumber > 36) {
ThrowRangeError(MessageTemplate::kToRadixFormatRange);
}
// 6. If radixNumber = 10, return ! ToString(x).
if (radixNumber == 10) {
return NumberToString(x);
}
// 7. Return the String representation of this Number
// value using the radix specified by radixNumber.
if (TaggedIsSmi(x)) {
return IntToString(
Convert<int32>(x), Unsigned(Convert<int32>(radixNumber)));
}
if (x == -0) {
return ZeroStringConstant();
} else if (::NumberIsNaN(x)) {
return NaNStringConstant();
} else if (x == V8_INFINITY) {
return InfinityStringConstant();
} else if (x == MINUS_V8_INFINITY) {
return MinusInfinityStringConstant();
}
return runtime::DoubleToStringWithRadix(x, radixNumber);
}
// ES6 #sec-number.isfinite
javascript builtin NumberIsFinite(
js-implicit context: NativeContext, receiver: JSAny)(
value: JSAny): Boolean {
typeswitch (value) {
case (Smi): {
return True;
}
case (h: HeapNumber): {
const number: float64 = Convert<float64>(h);
const infiniteOrNaN: bool = Float64IsNaN(number - number);
return Convert<Boolean>(!infiniteOrNaN);
}
case (JSAnyNotNumber): {
return False;
}
}
}
// ES6 #sec-number.isinteger
javascript builtin NumberIsInteger(
js-implicit context: NativeContext)(value: JSAny): Boolean {
return SelectBooleanConstant(IsInteger(value));
}
// ES6 #sec-number.isnan
javascript builtin NumberIsNaN(
js-implicit context: NativeContext)(value: JSAny): Boolean {
typeswitch (value) {
case (Smi): {
return False;
}
case (h: HeapNumber): {
const number: float64 = Convert<float64>(h);
return Convert<Boolean>(Float64IsNaN(number));
}
case (JSAnyNotNumber): {
return False;
}
}
}
// ES6 #sec-number.issafeinteger
javascript builtin NumberIsSafeInteger(
js-implicit context: NativeContext)(value: JSAny): Boolean {
return SelectBooleanConstant(IsSafeInteger(value));
}
// ES6 #sec-number.prototype.valueof
transitioning javascript builtin NumberPrototypeValueOf(
js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
return ToThisValue(
receiver, PrimitiveType::kNumber, 'Number.prototype.valueOf');
}
// ES6 #sec-number.parsefloat
transitioning javascript builtin NumberParseFloat(
js-implicit context: NativeContext)(value: JSAny): Number {
try {
typeswitch (value) {
case (s: Smi): {
return s;
}
case (h: HeapNumber): {
// The input is already a Number. Take care of -0.
// The sense of comparison is important for the NaN case.
return (Convert<float64>(h) == 0) ? SmiConstant(0) : h;
}
case (s: String): {
goto String(s);
}
case (HeapObject): {
goto String(string::ToString(context, value));
}
}
} label String(s: String) {
// Check if the string is a cached array index.
const hash: NameHash = s.raw_hash_field;
if (IsIntegerIndex(hash) &&
hash.array_index_length < kMaxCachedArrayIndexLength) {
const arrayIndex: uint32 = hash.array_index_value;
return SmiFromUint32(arrayIndex);
}
// Fall back to the runtime to convert string to a number.
return runtime::StringParseFloat(s);
}
}
extern macro TruncateFloat64ToWord32(float64): uint32;
transitioning builtin ParseInt(
implicit context: Context)(input: JSAny, radix: JSAny): Number {
try {
// Check if radix should be 10 (i.e. undefined, 0 or 10).
if (radix != Undefined && !TaggedEqual(radix, SmiConstant(10)) &&
!TaggedEqual(radix, SmiConstant(0))) {
goto CallRuntime;
}
typeswitch (input) {
case (s: Smi): {
return s;
}
case (h: HeapNumber): {
// Check if the input value is in Signed32 range.
const asFloat64: float64 = Convert<float64>(h);
const asInt32: int32 = Signed(TruncateFloat64ToWord32(asFloat64));
// The sense of comparison is important for the NaN case.
if (asFloat64 == ChangeInt32ToFloat64(asInt32)) goto Int32(asInt32);
// Check if the absolute value of input is in the [1,1<<31[ range. Call
// the runtime for the range [0,1[ because the result could be -0.
const kMaxAbsValue: float64 = 2147483648.0;
const absInput: float64 = math::Float64Abs(asFloat64);
if (absInput < kMaxAbsValue && absInput >= 1.0) goto Int32(asInt32);
goto CallRuntime;
}
case (s: String): {
goto String(s);
}
case (HeapObject): {
goto CallRuntime;
}
}
} label Int32(i: int32) {
return ChangeInt32ToTagged(i);
} label String(s: String) {
// Check if the string is a cached array index.
const hash: NameHash = s.raw_hash_field;
if (IsIntegerIndex(hash) &&
hash.array_index_length < kMaxCachedArrayIndexLength) {
const arrayIndex: uint32 = hash.array_index_value;
return SmiFromUint32(arrayIndex);
}
// Fall back to the runtime.
goto CallRuntime;
} label CallRuntime {
tail runtime::StringParseInt(input, radix);
}
}
// ES6 #sec-number.parseint
transitioning javascript builtin NumberParseInt(
js-implicit context: NativeContext)(value: JSAny, radix: JSAny): Number {
return ParseInt(value, radix);
}
extern builtin NonNumberToNumeric(implicit context: Context)(JSAny): Numeric;
extern builtin Subtract(implicit context: Context)(Number, Number): Number;
extern builtin Add(implicit context: Context)(Number, Number): Number;
extern builtin StringAddConvertLeft(implicit context: Context)(JSAny, String):
JSAny;
extern builtin StringAddConvertRight(implicit context: Context)(String, JSAny):
JSAny;
extern macro BitwiseOp(int32, int32, constexpr Operation): Number;
extern macro RelationalComparison(constexpr Operation, JSAny, JSAny, Context):
Boolean;
extern macro TruncateNumberToWord32(Number): int32;
// TODO(bbudge) Use a simpler macro structure that doesn't loop when converting
// non-numbers, if such a code sequence doesn't make the builtin bigger.
transitioning macro ToNumericOrPrimitive(
implicit context: Context)(value: JSAny): JSAny {
typeswitch (value) {
case (v: JSReceiver): {
return NonPrimitiveToPrimitive_Default(context, v);
}
case (v: JSPrimitive): {
return NonNumberToNumeric(v);
}
}
}
transitioning builtin Add(
implicit context: Context)(leftArg: JSAny, rightArg: JSAny): JSAny {
let left: JSAny = leftArg;
let right: JSAny = rightArg;
try {
while (true) {
typeswitch (left) {
case (left: Smi): {
typeswitch (right) {
case (right: Smi): {
return math::TrySmiAdd(left, right) otherwise goto Float64s(
SmiToFloat64(left), SmiToFloat64(right));
}
case (right: HeapNumber): {
goto Float64s(SmiToFloat64(left), Convert<float64>(right));
}
case (right: BigInt): {
goto Numerics(left, right);
}
case (right: String): {
goto StringAddConvertLeft(left, right);
}
case (HeapObject): {
right = ToNumericOrPrimitive(right);
continue;
}
}
}
case (left: HeapNumber): {
typeswitch (right) {
case (right: Smi): {
goto Float64s(Convert<float64>(left), SmiToFloat64(right));
}
case (right: HeapNumber): {
goto Float64s(Convert<float64>(left), Convert<float64>(right));
}
case (right: BigInt): {
goto Numerics(left, right);
}
case (right: String): {
goto StringAddConvertLeft(left, right);
}
case (HeapObject): {
right = ToNumericOrPrimitive(right);
continue;
}
}
}
case (left: BigInt): {
typeswitch (right) {
case (right: Numeric): {
goto Numerics(left, right);
}
case (right: String): {
goto StringAddConvertLeft(left, right);
}
case (HeapObject): {
right = ToNumericOrPrimitive(right);
continue;
}
}
}
case (left: String): {
goto StringAddConvertRight(left, right);
}
case (leftReceiver: JSReceiver): {
left = ToPrimitiveDefault(leftReceiver);
}
case (HeapObject): {
// left: HeapObject
typeswitch (right) {
case (right: String): {
goto StringAddConvertLeft(left, right);
}
case (rightReceiver: JSReceiver): {
// left is JSPrimitive and right is JSReceiver, convert right
// with priority.
right = ToPrimitiveDefault(rightReceiver);
continue;
}
case (JSPrimitive): {
// Neither left or right is JSReceiver, convert left.
left = NonNumberToNumeric(left);
continue;
}
}
}
}
}
} label StringAddConvertLeft(left: JSAny, right: String) {
tail StringAddConvertLeft(left, right);
} label StringAddConvertRight(left: String, right: JSAny) {
tail StringAddConvertRight(left, right);
} label Numerics(left: Numeric, right: Numeric) {
tail bigint::BigIntAdd(left, right);
} label Float64s(left: float64, right: float64) {
return AllocateHeapNumberWithValue(left + right);
}
unreachable;
}
// Unary type switch on Number | BigInt.
macro UnaryOp1(implicit context: Context)(value: JSAny): never labels
Number(Number), BigInt(BigInt) {
let x: JSAny = value;
while (true) {
typeswitch (x) {
case (n: Number): {
goto Number(n);
}
case (b: BigInt): {
goto BigInt(b);
}
case (JSAnyNotNumeric): {
x = NonNumberToNumeric(x);
}
}
}
unreachable;
}
// Unary type switch on Smi | HeapNumber | BigInt.
macro UnaryOp2(implicit context: Context)(value: JSAny): never labels
Smi(Smi), HeapNumber(HeapNumber), BigInt(BigInt) {
let x: JSAny = value;
while (true) {
typeswitch (x) {
case (s: Smi): {
goto Smi(s);
}
case (h: HeapNumber): {
goto HeapNumber(h);
}
case (b: BigInt): {
goto BigInt(b);
}
case (JSAnyNotNumeric): {
x = NonNumberToNumeric(x);
}
}
}
unreachable;
}
// Binary type switch on Number | BigInt.
macro BinaryOp1(implicit context: Context)(leftVal: JSAny, rightVal: JSAny):
never labels
Number(Number, Number), AtLeastOneBigInt(Numeric, Numeric) {
let left: JSAny = leftVal;
let right: JSAny = rightVal;
while (true) {
try {
typeswitch (left) {
case (left: Number): {
typeswitch (right) {
case (right: Number): {
goto Number(left, right);
}
case (right: BigInt): {
goto AtLeastOneBigInt(left, right);
}
case (JSAnyNotNumeric): {
goto RightNotNumeric;
}
}
}
case (left: BigInt): {
typeswitch (right) {
case (right: Numeric): {
goto AtLeastOneBigInt(left, right);
}
case (JSAnyNotNumeric): {
goto RightNotNumeric;
}
}
}
case (JSAnyNotNumeric): {
left = NonNumberToNumeric(left);
}
}
} label RightNotNumeric {
right = NonNumberToNumeric(right);
}
}
unreachable;
}
// Binary type switch on Smi | HeapNumber | BigInt.
macro BinaryOp2(implicit context: Context)(leftVal: JSAny, rightVal: JSAny):
never labels Smis(Smi, Smi), Float64s(float64, float64),
AtLeastOneBigInt(Numeric, Numeric) {
let left: JSAny = leftVal;
let right: JSAny = rightVal;
while (true) {
try {
typeswitch (left) {
case (left: Smi): {
typeswitch (right) {
case (right: Smi): {
goto Smis(left, right);
}
case (right: HeapNumber): {
goto Float64s(SmiToFloat64(left), Convert<float64>(right));
}
case (right: BigInt): {
goto AtLeastOneBigInt(left, right);
}
case (JSAnyNotNumeric): {
goto RightNotNumeric;
}
}
}
case (left: HeapNumber): {
typeswitch (right) {
case (right: Smi): {
goto Float64s(Convert<float64>(left), SmiToFloat64(right));
}
case (right: HeapNumber): {
goto Float64s(Convert<float64>(left), Convert<float64>(right));
}
case (right: BigInt): {
goto AtLeastOneBigInt(left, right);
}
case (JSAnyNotNumeric): {
goto RightNotNumeric;
}
}
}
case (left: BigInt): {
typeswitch (right) {
case (right: Numeric): {
goto AtLeastOneBigInt(left, right);
}
case (JSAnyNotNumeric): {
goto RightNotNumeric;
}
}
}
case (JSAnyNotNumeric): {
left = NonNumberToNumeric(left);
}
}
} label RightNotNumeric {
right = NonNumberToNumeric(right);
}
}
unreachable;
}
builtin Subtract(implicit context: Context)(left: JSAny, right: JSAny):
Numeric {
try {
BinaryOp2(left, right) otherwise Smis, Float64s, AtLeastOneBigInt;
} label Smis(left: Smi, right: Smi) {
try {
return math::TrySmiSub(left, right) otherwise Overflow;
} label Overflow {
goto Float64s(SmiToFloat64(left), SmiToFloat64(right));
}
} label Float64s(left: float64, right: float64) {
return AllocateHeapNumberWithValue(left - right);
} label AtLeastOneBigInt(left: Numeric, right: Numeric) {
tail bigint::BigIntSubtract(left, right);
}
}
builtin Multiply(implicit context: Context)(left: JSAny, right: JSAny):
Numeric {
try {
BinaryOp2(left, right) otherwise Smis, Float64s, AtLeastOneBigInt;
} label Smis(left: Smi, right: Smi) {
// The result is not necessarily a smi, in case of overflow.
return SmiMul(left, right);
} label Float64s(left: float64, right: float64) {
return AllocateHeapNumberWithValue(left * right);
} label AtLeastOneBigInt(left: Numeric, right: Numeric) {
tail bigint::BigIntMultiply(left, right);
}
}
const kSmiValueSize: constexpr int32 generates 'kSmiValueSize';
const kMinInt32: constexpr int32 generates 'kMinInt';
const kMinInt31: constexpr int32 generates 'kMinInt31';
const kMinimumDividend: int32 = (kSmiValueSize == 32) ? kMinInt32 : kMinInt31;
builtin Divide(implicit context: Context)(left: JSAny, right: JSAny):
Numeric {
try {
BinaryOp2(left, right) otherwise Smis, Float64s, AtLeastOneBigInt;
} label Smis(left: Smi, right: Smi) {
// TODO(jkummerow): Consider just always doing a double division.
// Bail out if {divisor} is zero.
if (right == 0) goto SmiBailout(left, right);
// Bail out if dividend is zero and divisor is negative.
if (left == 0 && right < 0) goto SmiBailout(left, right);
const dividend: int32 = SmiToInt32(left);
const divisor: int32 = SmiToInt32(right);
// Bail out if dividend is kMinInt31 (or kMinInt32 if Smis are 32 bits)
// and divisor is -1.
if (divisor == -1 && dividend == kMinimumDividend) {
goto SmiBailout(left, right);
}
// TODO(epertoso): consider adding a machine instruction that returns
// both the result and the remainder.
const result: int32 = dividend / divisor;
const truncated: int32 = result * divisor;
if (dividend != truncated) goto SmiBailout(left, right);
return SmiFromInt32(result);
} label SmiBailout(left: Smi, right: Smi) {
goto Float64s(SmiToFloat64(left), SmiToFloat64(right));
} label Float64s(left: float64, right: float64) {
return AllocateHeapNumberWithValue(left / right);
} label AtLeastOneBigInt(left: Numeric, right: Numeric) {
tail bigint::BigIntDivide(left, right);
}
}
builtin Modulus(implicit context: Context)(left: JSAny, right: JSAny):
Numeric {
try {
BinaryOp2(left, right) otherwise Smis, Float64s, AtLeastOneBigInt;
} label Smis(left: Smi, right: Smi) {
return SmiMod(left, right);
} label Float64s(left: float64, right: float64) {
return AllocateHeapNumberWithValue(left % right);
} label AtLeastOneBigInt(left: Numeric, right: Numeric) {
tail bigint::BigIntModulus(left, right);
}
}
builtin Exponentiate(implicit context: Context)(left: JSAny, right: JSAny):
Numeric {
try {
BinaryOp1(left, right) otherwise Numbers, AtLeastOneBigInt;
} label Numbers(left: Number, right: Number) {
return math::MathPowImpl(left, right);
} label AtLeastOneBigInt(left: Numeric, right: Numeric) {
tail runtime::BigIntBinaryOp(
context, left, right, SmiTag<Operation>(Operation::kExponentiate));
}
}
builtin Negate(implicit context: Context)(value: JSAny): Numeric {
try {
UnaryOp2(value) otherwise Smi, HeapNumber, BigInt;
} label Smi(s: Smi) {
return SmiMul(s, -1);
} label HeapNumber(h: HeapNumber) {
return AllocateHeapNumberWithValue(Convert<float64>(h) * -1.0);
} label BigInt(b: BigInt) {
tail runtime::BigIntUnaryOp(
context, b, SmiTag<Operation>(Operation::kNegate));
}
}
builtin BitwiseNot(implicit context: Context)(value: JSAny): Numeric {
try {
UnaryOp1(value) otherwise Number, BigInt;
} label Number(n: Number) {
return BitwiseOp(TruncateNumberToWord32(n), -1, Operation::kBitwiseXor);
} label BigInt(b: BigInt) {
return runtime::BigIntUnaryOp(
context, b, SmiTag<Operation>(Operation::kBitwiseNot));
}
}
builtin Decrement(implicit context: Context)(value: JSAny): Numeric {
try {
UnaryOp1(value) otherwise Number, BigInt;
} label Number(n: Number) {
tail Subtract(n, 1);
} label BigInt(b: BigInt) {
return runtime::BigIntUnaryOp(
context, b, SmiTag<Operation>(Operation::kDecrement));
}
}
builtin Increment(implicit context: Context)(value: JSAny): Numeric {
try {
UnaryOp1(value) otherwise Number, BigInt;
} label Number(n: Number) {
tail Add(n, 1);
} label BigInt(b: BigInt) {
return runtime::BigIntUnaryOp(
context, b, SmiTag<Operation>(Operation::kIncrement));
}
}
// Bitwise binary operations.
extern macro BinaryOpAssembler::Generate_BitwiseBinaryOp(
constexpr Operation, JSAny, JSAny, Context): Object;
builtin ShiftLeft(implicit context: Context)(left: JSAny, right: JSAny):
Object {
return Generate_BitwiseBinaryOp(Operation::kShiftLeft, left, right, context);
}
builtin ShiftRight(implicit context: Context)(left: JSAny, right: JSAny):
Object {
return Generate_BitwiseBinaryOp(Operation::kShiftRight, left, right, context);
}
builtin ShiftRightLogical(
implicit context: Context)(left: JSAny, right: JSAny): Object {
return Generate_BitwiseBinaryOp(
Operation::kShiftRightLogical, left, right, context);
}
builtin BitwiseAnd(implicit context: Context)(left: JSAny, right: JSAny):
Numeric {
try {
BinaryOp1(left, right) otherwise Number, AtLeastOneBigInt;
} label Number(left: Number, right: Number) {
return BitwiseOp(
TruncateNumberToWord32(left), TruncateNumberToWord32(right),
Operation::kBitwiseAnd);
} label AtLeastOneBigInt(left: Numeric, right: Numeric) {
tail bigint::BigIntBitwiseAnd(left, right);
}
}
builtin BitwiseOr(implicit context: Context)(left: JSAny, right: JSAny):
Numeric {
try {
BinaryOp1(left, right) otherwise Number, AtLeastOneBigInt;
} label Number(left: Number, right: Number) {
return BitwiseOp(
TruncateNumberToWord32(left), TruncateNumberToWord32(right),
Operation::kBitwiseOr);
} label AtLeastOneBigInt(left: Numeric, right: Numeric) {
tail bigint::BigIntBitwiseOr(left, right);
}
}
builtin BitwiseXor(implicit context: Context)(left: JSAny, right: JSAny):
Numeric {
try {
BinaryOp1(left, right) otherwise Number, AtLeastOneBigInt;
} label Number(left: Number, right: Number) {
return BitwiseOp(
TruncateNumberToWord32(left), TruncateNumberToWord32(right),
Operation::kBitwiseXor);
} label AtLeastOneBigInt(left: Numeric, right: Numeric) {
tail bigint::BigIntBitwiseXor(left, right);
}
}
// Relational builtins.
builtin LessThan(implicit context: Context)(left: JSAny, right: JSAny):
Object {
return RelationalComparison(Operation::kLessThan, left, right, context);
}
builtin LessThanOrEqual(implicit context: Context)(left: JSAny, right: JSAny):
Object {
return RelationalComparison(
Operation::kLessThanOrEqual, left, right, context);
}
builtin GreaterThan(implicit context: Context)(left: JSAny, right: JSAny):
Object {
return RelationalComparison(Operation::kGreaterThan, left, right, context);
}
builtin GreaterThanOrEqual(
implicit context: Context)(left: JSAny, right: JSAny): Object {
return RelationalComparison(
Operation::kGreaterThanOrEqual, left, right, context);
}
builtin Equal(implicit context: Context)(left: JSAny, right: JSAny): Object {
return Equal(left, right, context);
}
builtin StrictEqual(implicit context: Context)(left: JSAny, right: JSAny):
Object {
return ::StrictEqual(left, right);
}
} // namespace number