| // 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; |
| |
| const kAsciiZero: constexpr int32 = 48; // '0' (ascii) |
| const kAsciiLowerCaseA: constexpr int32 = 97; // 'a' (ascii) |
| |
| transitioning macro ThisNumberValue(implicit context: Context)( |
| receiver: JSAny, method: constexpr string): Number { |
| return UnsafeCast<Number>( |
| ToThisValue(receiver, PrimitiveType::kNumber, method)); |
| } |
| |
| macro ToCharCode(input: int32): char8 { |
| assert(0 <= input && input < 36); |
| return input < 10 ? %RawDownCast<char8>(input + kAsciiZero) : |
| %RawDownCast<char8>(input - 10 + kAsciiLowerCaseA); |
| } |
| |
| @export |
| macro NumberToStringSmi(x: int32, radix: int32): String labels Slow { |
| const isNegative: bool = x < 0; |
| let n: int32 = x; |
| if (!isNegative) { |
| // Fast case where the result is a one character string. |
| if (x < radix) { |
| return StringFromSingleCharCode(ToCharCode(n)); |
| } |
| } else { |
| assert(isNegative); |
| if (n == kMinInt32) { |
| goto Slow; |
| } |
| n = 0 - n; |
| } |
| |
| // Calculate length and pre-allocate the result string. |
| let temp: int32 = n; |
| let length: int32 = isNegative ? 1 : 0; |
| while (temp > 0) { |
| temp = temp / radix; |
| length = length + 1; |
| } |
| assert(length > 0); |
| const strSeq: SeqOneByteString = AllocateSeqOneByteString(Unsigned(length)); |
| let cursor: intptr = Convert<intptr>(length) - 1; |
| while (n > 0) { |
| const digit: int32 = n % radix; |
| n = n / radix; |
| strSeq.chars[cursor] = ToCharCode(digit); |
| cursor = cursor - 1; |
| } |
| if (isNegative) { |
| assert(cursor == 0); |
| // Insert '-' to result. |
| strSeq.chars[0] = 45; |
| } else { |
| assert(cursor == -1); |
| // In sync with Factory::SmiToString: If radix = 10 and positive number, |
| // update hash for string. |
| if (radix == 10) { |
| assert(strSeq.hash_field == kNameEmptyHashField); |
| strSeq.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 NumberToStringSmi(Convert<int32>(x), Convert<int32>(radixNumber)) |
| otherwise return runtime::DoubleToStringWithRadix(x, 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.hash_field; |
| if (!hash.is_not_integer_index_mask && |
| 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) 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.hash_field; |
| if (!hash.is_not_integer_index_mask && |
| 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 BitwiseXor(implicit context: Context)(Number, Number): Number; |
| 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; |
| |
| // 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 runtime::BigIntBinaryOp( |
| context, left, right, SmiTag<Operation>(Operation::kMultiply)); |
| } |
| } |
| |
| 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 runtime::BigIntBinaryOp( |
| context, left, right, SmiTag<Operation>(Operation::kDivide)); |
| } |
| } |
| |
| 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 runtime::BigIntBinaryOp( |
| context, left, right, SmiTag<Operation>(Operation::kModulus)); |
| } |
| } |
| |
| 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); |
| } 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) { |
| tail BitwiseXor(n, -1); |
| } 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): Object { |
| return Generate_BitwiseBinaryOp(Operation::kBitwiseAnd, left, right, context); |
| } |
| |
| builtin BitwiseOr(implicit context: Context)( |
| left: JSAny, right: JSAny): Object { |
| return Generate_BitwiseBinaryOp(Operation::kBitwiseOr, left, right, context); |
| } |
| |
| builtin BitwiseXor(implicit context: Context)( |
| left: JSAny, right: JSAny): Object { |
| return Generate_BitwiseBinaryOp(Operation::kBitwiseXor, left, right, context); |
| } |
| |
| // 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 |