• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2019 the V8 project authors. All rights reserved. Use of this
2// source code is governed by a BSD-style license that can be found in the
3// LICENSE file.
4
5#include 'src/ic/binary-op-assembler.h'
6
7extern enum Operation extends uint31 {
8  // Binary operations.
9  kAdd,
10  kSubtract,
11  kMultiply,
12  kDivide,
13  kModulus,
14  kExponentiate,
15  kBitwiseAnd,
16  kBitwiseOr,
17  kBitwiseXor,
18  kShiftLeft,
19  kShiftRight,
20  kShiftRightLogical,
21  // Unary operations.
22  kBitwiseNot,
23  kNegate,
24  kIncrement,
25  kDecrement,
26  // Compare operations.
27  kEqual,
28  kStrictEqual,
29  kLessThan,
30  kLessThanOrEqual,
31  kGreaterThan,
32  kGreaterThanOrEqual
33}
34
35namespace runtime {
36extern transitioning runtime
37DoubleToStringWithRadix(implicit context: Context)(Number, Number): String;
38
39extern transitioning runtime StringParseFloat(implicit context: Context)(
40    String): Number;
41extern transitioning runtime StringParseInt(implicit context: Context)(
42    JSAny, JSAny): Number;
43
44extern runtime BigIntUnaryOp(Context, BigInt, SmiTagged<Operation>): BigInt;
45extern runtime BigIntBinaryOp(
46    Context, Numeric, Numeric, SmiTagged<Operation>): BigInt;
47}  // namespace runtime
48
49namespace number {
50extern macro NaNStringConstant(): String;
51extern macro ZeroStringConstant(): String;
52extern macro InfinityStringConstant(): String;
53extern macro MinusInfinityStringConstant(): String;
54
55const kAsciiZero: constexpr int32 = 48;        // '0' (ascii)
56const kAsciiLowerCaseA: constexpr int32 = 97;  // 'a' (ascii)
57
58transitioning macro ThisNumberValue(implicit context: Context)(
59    receiver: JSAny, method: constexpr string): Number {
60  return UnsafeCast<Number>(
61      ToThisValue(receiver, PrimitiveType::kNumber, method));
62}
63
64macro ToCharCode(input: int32): char8 {
65  dcheck(0 <= input && input < 36);
66  return input < 10 ?
67      %RawDownCast<char8>(Unsigned(input + kAsciiZero)) :
68      %RawDownCast<char8>(Unsigned(input - 10 + kAsciiLowerCaseA));
69}
70
71@export
72macro NumberToStringSmi(x: int32, radix: int32): String labels Slow {
73  const isNegative: bool = x < 0;
74  let n: int32 = x;
75  if (!isNegative) {
76    // Fast case where the result is a one character string.
77    if (x < radix) {
78      if (x == 0) {
79        return ZeroStringConstant();
80      }
81      return StringFromSingleCharCode(ToCharCode(n));
82    }
83  } else {
84    dcheck(isNegative);
85    if (n == kMinInt32) {
86      goto Slow;
87    }
88    n = 0 - n;
89  }
90
91  // Calculate length and pre-allocate the result string.
92  let temp: int32 = n;
93  let length: int32 = isNegative ? Convert<int32>(1) : Convert<int32>(0);
94  while (temp > 0) {
95    temp = temp / radix;
96    length = length + 1;
97  }
98  dcheck(length > 0);
99  const strSeq = AllocateNonEmptySeqOneByteString(Unsigned(length));
100  let cursor: intptr = Convert<intptr>(length) - 1;
101  while (n > 0) {
102    const digit: int32 = n % radix;
103    n = n / radix;
104    *UnsafeConstCast(&strSeq.chars[cursor]) = ToCharCode(digit);
105    cursor = cursor - 1;
106  }
107  if (isNegative) {
108    dcheck(cursor == 0);
109    // Insert '-' to result.
110    *UnsafeConstCast(&strSeq.chars[0]) = 45;
111  } else {
112    dcheck(cursor == -1);
113    // In sync with Factory::SmiToString: If radix = 10 and positive number,
114    // update hash for string.
115    if (radix == 10) {
116      dcheck(strSeq.raw_hash_field == kNameEmptyHashField);
117      strSeq.raw_hash_field = MakeArrayIndexHash(Unsigned(x), Unsigned(length));
118    }
119  }
120  return strSeq;
121}
122
123// https://tc39.github.io/ecma262/#sec-number.prototype.tostring
124transitioning javascript builtin NumberPrototypeToString(
125    js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
126  // 1. Let x be ? thisNumberValue(this value).
127  const x = ThisNumberValue(receiver, 'Number.prototype.toString');
128
129  // 2. If radix is not present, let radixNumber be 10.
130  // 3. Else if radix is undefined, let radixNumber be 10.
131  // 4. Else, let radixNumber be ? ToInteger(radix).
132  const radix: JSAny = arguments[0];
133  const radixNumber: Number = radix == Undefined ? 10 : ToInteger_Inline(radix);
134
135  // 5. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception.
136  if (radixNumber < 2 || radixNumber > 36) {
137    ThrowRangeError(MessageTemplate::kToRadixFormatRange);
138  }
139
140  // 6. If radixNumber = 10, return ! ToString(x).
141  if (radixNumber == 10) {
142    return NumberToString(x);
143  }
144
145  // 7. Return the String representation of this Number
146  //    value using the radix specified by radixNumber.
147
148  if (TaggedIsSmi(x)) {
149    return NumberToStringSmi(Convert<int32>(x), Convert<int32>(radixNumber))
150        otherwise return runtime::DoubleToStringWithRadix(x, radixNumber);
151  }
152
153  if (x == -0) {
154    return ZeroStringConstant();
155  } else if (::NumberIsNaN(x)) {
156    return NaNStringConstant();
157  } else if (x == V8_INFINITY) {
158    return InfinityStringConstant();
159  } else if (x == MINUS_V8_INFINITY) {
160    return MinusInfinityStringConstant();
161  }
162
163  return runtime::DoubleToStringWithRadix(x, radixNumber);
164}
165
166// ES6 #sec-number.isfinite
167javascript builtin NumberIsFinite(
168    js-implicit context: NativeContext,
169    receiver: JSAny)(value: JSAny): Boolean {
170  typeswitch (value) {
171    case (Smi): {
172      return True;
173    }
174    case (h: HeapNumber): {
175      const number: float64 = Convert<float64>(h);
176      const infiniteOrNaN: bool = Float64IsNaN(number - number);
177      return Convert<Boolean>(!infiniteOrNaN);
178    }
179    case (JSAnyNotNumber): {
180      return False;
181    }
182  }
183}
184
185// ES6 #sec-number.isinteger
186javascript builtin NumberIsInteger(js-implicit context: NativeContext)(
187    value: JSAny): Boolean {
188  return SelectBooleanConstant(IsInteger(value));
189}
190
191// ES6 #sec-number.isnan
192javascript builtin NumberIsNaN(js-implicit context: NativeContext)(
193    value: JSAny): Boolean {
194  typeswitch (value) {
195    case (Smi): {
196      return False;
197    }
198    case (h: HeapNumber): {
199      const number: float64 = Convert<float64>(h);
200      return Convert<Boolean>(Float64IsNaN(number));
201    }
202    case (JSAnyNotNumber): {
203      return False;
204    }
205  }
206}
207
208// ES6 #sec-number.issafeinteger
209javascript builtin NumberIsSafeInteger(js-implicit context: NativeContext)(
210    value: JSAny): Boolean {
211  return SelectBooleanConstant(IsSafeInteger(value));
212}
213
214// ES6 #sec-number.prototype.valueof
215transitioning javascript builtin NumberPrototypeValueOf(
216    js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
217  return ToThisValue(
218      receiver, PrimitiveType::kNumber, 'Number.prototype.valueOf');
219}
220
221// ES6 #sec-number.parsefloat
222transitioning javascript builtin NumberParseFloat(
223    js-implicit context: NativeContext)(value: JSAny): Number {
224  try {
225    typeswitch (value) {
226      case (s: Smi): {
227        return s;
228      }
229      case (h: HeapNumber): {
230        // The input is already a Number. Take care of -0.
231        // The sense of comparison is important for the NaN case.
232        return (Convert<float64>(h) == 0) ? SmiConstant(0) : h;
233      }
234      case (s: String): {
235        goto String(s);
236      }
237      case (HeapObject): {
238        goto String(string::ToString(context, value));
239      }
240    }
241  } label String(s: String) {
242    // Check if the string is a cached array index.
243    const hash: NameHash = s.raw_hash_field;
244    if (IsIntegerIndex(hash) &&
245        hash.array_index_length < kMaxCachedArrayIndexLength) {
246      const arrayIndex: uint32 = hash.array_index_value;
247      return SmiFromUint32(arrayIndex);
248    }
249    // Fall back to the runtime to convert string to a number.
250    return runtime::StringParseFloat(s);
251  }
252}
253
254extern macro TruncateFloat64ToWord32(float64): uint32;
255
256transitioning builtin ParseInt(implicit context: Context)(
257    input: JSAny, radix: JSAny): Number {
258  try {
259    // Check if radix should be 10 (i.e. undefined, 0 or 10).
260    if (radix != Undefined && !TaggedEqual(radix, SmiConstant(10)) &&
261        !TaggedEqual(radix, SmiConstant(0))) {
262      goto CallRuntime;
263    }
264
265    typeswitch (input) {
266      case (s: Smi): {
267        return s;
268      }
269      case (h: HeapNumber): {
270        // Check if the input value is in Signed32 range.
271        const asFloat64: float64 = Convert<float64>(h);
272        const asInt32: int32 = Signed(TruncateFloat64ToWord32(asFloat64));
273        // The sense of comparison is important for the NaN case.
274        if (asFloat64 == ChangeInt32ToFloat64(asInt32)) goto Int32(asInt32);
275
276        // Check if the absolute value of input is in the [1,1<<31[ range. Call
277        // the runtime for the range [0,1[ because the result could be -0.
278        const kMaxAbsValue: float64 = 2147483648.0;
279        const absInput: float64 = math::Float64Abs(asFloat64);
280        if (absInput < kMaxAbsValue && absInput >= 1.0) goto Int32(asInt32);
281        goto CallRuntime;
282      }
283      case (s: String): {
284        goto String(s);
285      }
286      case (HeapObject): {
287        goto CallRuntime;
288      }
289    }
290  } label Int32(i: int32) {
291    return ChangeInt32ToTagged(i);
292  } label String(s: String) {
293    // Check if the string is a cached array index.
294    const hash: NameHash = s.raw_hash_field;
295    if (IsIntegerIndex(hash) &&
296        hash.array_index_length < kMaxCachedArrayIndexLength) {
297      const arrayIndex: uint32 = hash.array_index_value;
298      return SmiFromUint32(arrayIndex);
299    }
300    // Fall back to the runtime.
301    goto CallRuntime;
302  } label CallRuntime {
303    tail runtime::StringParseInt(input, radix);
304  }
305}
306
307// ES6 #sec-number.parseint
308transitioning javascript builtin NumberParseInt(
309    js-implicit context: NativeContext)(value: JSAny, radix: JSAny): Number {
310  return ParseInt(value, radix);
311}
312
313extern builtin NonNumberToNumeric(implicit context: Context)(JSAny): Numeric;
314extern builtin BitwiseXor(implicit context: Context)(Number, Number): Number;
315extern builtin Subtract(implicit context: Context)(Number, Number): Number;
316extern builtin Add(implicit context: Context)(Number, Number): Number;
317extern builtin StringAddConvertLeft(implicit context: Context)(
318    JSAny, String): JSAny;
319extern builtin StringAddConvertRight(implicit context: Context)(
320    String, JSAny): JSAny;
321
322extern macro BitwiseOp(int32, int32, constexpr Operation): Number;
323extern macro RelationalComparison(
324    constexpr Operation, JSAny, JSAny, Context): Boolean;
325
326// TODO(bbudge) Use a simpler macro structure that doesn't loop when converting
327// non-numbers, if such a code sequence doesn't make the builtin bigger.
328
329transitioning macro ToNumericOrPrimitive(implicit context: Context)(
330    value: JSAny): JSAny {
331  typeswitch (value) {
332    case (v: JSReceiver): {
333      return NonPrimitiveToPrimitive_Default(context, v);
334    }
335    case (v: JSPrimitive): {
336      return NonNumberToNumeric(v);
337    }
338  }
339}
340
341transitioning builtin Add(implicit context: Context)(
342    leftArg: JSAny, rightArg: JSAny): JSAny {
343  let left: JSAny = leftArg;
344  let right: JSAny = rightArg;
345  try {
346    while (true) {
347      typeswitch (left) {
348        case (left: Smi): {
349          typeswitch (right) {
350            case (right: Smi): {
351              return math::TrySmiAdd(left, right) otherwise goto Float64s(
352                  SmiToFloat64(left), SmiToFloat64(right));
353            }
354            case (right: HeapNumber): {
355              goto Float64s(SmiToFloat64(left), Convert<float64>(right));
356            }
357            case (right: BigInt): {
358              goto Numerics(left, right);
359            }
360            case (right: String): {
361              goto StringAddConvertLeft(left, right);
362            }
363            case (HeapObject): {
364              right = ToNumericOrPrimitive(right);
365              continue;
366            }
367          }
368        }
369        case (left: HeapNumber): {
370          typeswitch (right) {
371            case (right: Smi): {
372              goto Float64s(Convert<float64>(left), SmiToFloat64(right));
373            }
374            case (right: HeapNumber): {
375              goto Float64s(Convert<float64>(left), Convert<float64>(right));
376            }
377            case (right: BigInt): {
378              goto Numerics(left, right);
379            }
380            case (right: String): {
381              goto StringAddConvertLeft(left, right);
382            }
383            case (HeapObject): {
384              right = ToNumericOrPrimitive(right);
385              continue;
386            }
387          }
388        }
389        case (left: BigInt): {
390          typeswitch (right) {
391            case (right: Numeric): {
392              goto Numerics(left, right);
393            }
394            case (right: String): {
395              goto StringAddConvertLeft(left, right);
396            }
397            case (HeapObject): {
398              right = ToNumericOrPrimitive(right);
399              continue;
400            }
401          }
402        }
403        case (left: String): {
404          goto StringAddConvertRight(left, right);
405        }
406        case (leftReceiver: JSReceiver): {
407          left = ToPrimitiveDefault(leftReceiver);
408        }
409        case (HeapObject): {
410          // left: HeapObject
411          typeswitch (right) {
412            case (right: String): {
413              goto StringAddConvertLeft(left, right);
414            }
415            case (rightReceiver: JSReceiver): {
416              // left is JSPrimitive and right is JSReceiver, convert right
417              // with priority.
418              right = ToPrimitiveDefault(rightReceiver);
419              continue;
420            }
421            case (JSPrimitive): {
422              // Neither left or right is JSReceiver, convert left.
423              left = NonNumberToNumeric(left);
424              continue;
425            }
426          }
427        }
428      }
429    }
430  } label StringAddConvertLeft(left: JSAny, right: String) {
431    tail StringAddConvertLeft(left, right);
432  } label StringAddConvertRight(left: String, right: JSAny) {
433    tail StringAddConvertRight(left, right);
434  } label Numerics(left: Numeric, right: Numeric) {
435    tail bigint::BigIntAdd(left, right);
436  } label Float64s(left: float64, right: float64) {
437    return AllocateHeapNumberWithValue(left + right);
438  }
439  unreachable;
440}
441
442// Unary type switch on Number | BigInt.
443macro UnaryOp1(implicit context: Context)(value: JSAny): never labels
444Number(Number), BigInt(BigInt) {
445  let x: JSAny = value;
446  while (true) {
447    typeswitch (x) {
448      case (n: Number): {
449        goto Number(n);
450      }
451      case (b: BigInt): {
452        goto BigInt(b);
453      }
454      case (JSAnyNotNumeric): {
455        x = NonNumberToNumeric(x);
456      }
457    }
458  }
459  unreachable;
460}
461
462// Unary type switch on Smi | HeapNumber | BigInt.
463macro UnaryOp2(implicit context: Context)(value: JSAny): never labels
464Smi(Smi), HeapNumber(HeapNumber), BigInt(BigInt) {
465  let x: JSAny = value;
466  while (true) {
467    typeswitch (x) {
468      case (s: Smi): {
469        goto Smi(s);
470      }
471      case (h: HeapNumber): {
472        goto HeapNumber(h);
473      }
474      case (b: BigInt): {
475        goto BigInt(b);
476      }
477      case (JSAnyNotNumeric): {
478        x = NonNumberToNumeric(x);
479      }
480    }
481  }
482  unreachable;
483}
484
485// Binary type switch on Number | BigInt.
486macro BinaryOp1(implicit context: Context)(
487    leftVal: JSAny, rightVal: JSAny): never labels
488Number(Number, Number), AtLeastOneBigInt(Numeric, Numeric) {
489  let left: JSAny = leftVal;
490  let right: JSAny = rightVal;
491  while (true) {
492    try {
493      typeswitch (left) {
494        case (left: Number): {
495          typeswitch (right) {
496            case (right: Number): {
497              goto Number(left, right);
498            }
499            case (right: BigInt): {
500              goto AtLeastOneBigInt(left, right);
501            }
502            case (JSAnyNotNumeric): {
503              goto RightNotNumeric;
504            }
505          }
506        }
507        case (left: BigInt): {
508          typeswitch (right) {
509            case (right: Numeric): {
510              goto AtLeastOneBigInt(left, right);
511            }
512            case (JSAnyNotNumeric): {
513              goto RightNotNumeric;
514            }
515          }
516        }
517        case (JSAnyNotNumeric): {
518          left = NonNumberToNumeric(left);
519        }
520      }
521    } label RightNotNumeric {
522      right = NonNumberToNumeric(right);
523    }
524  }
525  unreachable;
526}
527
528// Binary type switch on Smi | HeapNumber | BigInt.
529macro BinaryOp2(implicit context: Context)(leftVal: JSAny, rightVal: JSAny):
530    never labels Smis(Smi, Smi), Float64s(float64, float64),
531    AtLeastOneBigInt(Numeric, Numeric) {
532  let left: JSAny = leftVal;
533  let right: JSAny = rightVal;
534  while (true) {
535    try {
536      typeswitch (left) {
537        case (left: Smi): {
538          typeswitch (right) {
539            case (right: Smi): {
540              goto Smis(left, right);
541            }
542            case (right: HeapNumber): {
543              goto Float64s(SmiToFloat64(left), Convert<float64>(right));
544            }
545            case (right: BigInt): {
546              goto AtLeastOneBigInt(left, right);
547            }
548            case (JSAnyNotNumeric): {
549              goto RightNotNumeric;
550            }
551          }
552        }
553        case (left: HeapNumber): {
554          typeswitch (right) {
555            case (right: Smi): {
556              goto Float64s(Convert<float64>(left), SmiToFloat64(right));
557            }
558            case (right: HeapNumber): {
559              goto Float64s(Convert<float64>(left), Convert<float64>(right));
560            }
561            case (right: BigInt): {
562              goto AtLeastOneBigInt(left, right);
563            }
564            case (JSAnyNotNumeric): {
565              goto RightNotNumeric;
566            }
567          }
568        }
569        case (left: BigInt): {
570          typeswitch (right) {
571            case (right: Numeric): {
572              goto AtLeastOneBigInt(left, right);
573            }
574            case (JSAnyNotNumeric): {
575              goto RightNotNumeric;
576            }
577          }
578        }
579        case (JSAnyNotNumeric): {
580          left = NonNumberToNumeric(left);
581        }
582      }
583    } label RightNotNumeric {
584      right = NonNumberToNumeric(right);
585    }
586  }
587  unreachable;
588}
589
590builtin Subtract(implicit context: Context)(
591    left: JSAny, right: JSAny): Numeric {
592  try {
593    BinaryOp2(left, right) otherwise Smis, Float64s, AtLeastOneBigInt;
594  } label Smis(left: Smi, right: Smi) {
595    try {
596      return math::TrySmiSub(left, right) otherwise Overflow;
597    } label Overflow {
598      goto Float64s(SmiToFloat64(left), SmiToFloat64(right));
599    }
600  } label Float64s(left: float64, right: float64) {
601    return AllocateHeapNumberWithValue(left - right);
602  } label AtLeastOneBigInt(left: Numeric, right: Numeric) {
603    tail bigint::BigIntSubtract(left, right);
604  }
605}
606
607builtin Multiply(implicit context: Context)(
608    left: JSAny, right: JSAny): Numeric {
609  try {
610    BinaryOp2(left, right) otherwise Smis, Float64s, AtLeastOneBigInt;
611  } label Smis(left: Smi, right: Smi) {
612    // The result is not necessarily a smi, in case of overflow.
613    return SmiMul(left, right);
614  } label Float64s(left: float64, right: float64) {
615    return AllocateHeapNumberWithValue(left * right);
616  } label AtLeastOneBigInt(left: Numeric, right: Numeric) {
617    tail runtime::BigIntBinaryOp(
618        context, left, right, SmiTag<Operation>(Operation::kMultiply));
619  }
620}
621
622const kSmiValueSize: constexpr int32 generates 'kSmiValueSize';
623const kMinInt32: constexpr int32 generates 'kMinInt';
624const kMinInt31: constexpr int32 generates 'kMinInt31';
625const kMinimumDividend: int32 = (kSmiValueSize == 32) ? kMinInt32 : kMinInt31;
626
627builtin Divide(implicit context: Context)(left: JSAny, right: JSAny): Numeric {
628  try {
629    BinaryOp2(left, right) otherwise Smis, Float64s, AtLeastOneBigInt;
630  } label Smis(left: Smi, right: Smi) {
631    // TODO(jkummerow): Consider just always doing a double division.
632    // Bail out if {divisor} is zero.
633    if (right == 0) goto SmiBailout(left, right);
634
635    // Bail out if dividend is zero and divisor is negative.
636    if (left == 0 && right < 0) goto SmiBailout(left, right);
637
638    const dividend: int32 = SmiToInt32(left);
639    const divisor: int32 = SmiToInt32(right);
640
641    // Bail out if dividend is kMinInt31 (or kMinInt32 if Smis are 32 bits)
642    // and divisor is -1.
643    if (divisor == -1 && dividend == kMinimumDividend) {
644      goto SmiBailout(left, right);
645    }
646    // TODO(epertoso): consider adding a machine instruction that returns
647    // both the result and the remainder.
648    const result: int32 = dividend / divisor;
649    const truncated: int32 = result * divisor;
650    if (dividend != truncated) goto SmiBailout(left, right);
651    return SmiFromInt32(result);
652  } label SmiBailout(left: Smi, right: Smi) {
653    goto Float64s(SmiToFloat64(left), SmiToFloat64(right));
654  } label Float64s(left: float64, right: float64) {
655    return AllocateHeapNumberWithValue(left / right);
656  } label AtLeastOneBigInt(left: Numeric, right: Numeric) {
657    tail runtime::BigIntBinaryOp(
658        context, left, right, SmiTag<Operation>(Operation::kDivide));
659  }
660}
661
662builtin Modulus(implicit context: Context)(left: JSAny, right: JSAny): Numeric {
663  try {
664    BinaryOp2(left, right) otherwise Smis, Float64s, AtLeastOneBigInt;
665  } label Smis(left: Smi, right: Smi) {
666    return SmiMod(left, right);
667  } label Float64s(left: float64, right: float64) {
668    return AllocateHeapNumberWithValue(left % right);
669  } label AtLeastOneBigInt(left: Numeric, right: Numeric) {
670    tail runtime::BigIntBinaryOp(
671        context, left, right, SmiTag<Operation>(Operation::kModulus));
672  }
673}
674
675builtin Exponentiate(implicit context: Context)(
676    left: JSAny, right: JSAny): Numeric {
677  try {
678    BinaryOp1(left, right) otherwise Numbers, AtLeastOneBigInt;
679  } label Numbers(left: Number, right: Number) {
680    return math::MathPowImpl(left, right);
681  } label AtLeastOneBigInt(left: Numeric, right: Numeric) {
682    tail runtime::BigIntBinaryOp(
683        context, left, right, SmiTag<Operation>(Operation::kExponentiate));
684  }
685}
686
687builtin Negate(implicit context: Context)(value: JSAny): Numeric {
688  try {
689    UnaryOp2(value) otherwise Smi, HeapNumber, BigInt;
690  } label Smi(s: Smi) {
691    return SmiMul(s, -1);
692  } label HeapNumber(h: HeapNumber) {
693    return AllocateHeapNumberWithValue(Convert<float64>(h) * -1.0);
694  } label BigInt(b: BigInt) {
695    tail runtime::BigIntUnaryOp(
696        context, b, SmiTag<Operation>(Operation::kNegate));
697  }
698}
699
700builtin BitwiseNot(implicit context: Context)(value: JSAny): Numeric {
701  try {
702    UnaryOp1(value) otherwise Number, BigInt;
703  } label Number(n: Number) {
704    tail BitwiseXor(n, -1);
705  } label BigInt(b: BigInt) {
706    return runtime::BigIntUnaryOp(
707        context, b, SmiTag<Operation>(Operation::kBitwiseNot));
708  }
709}
710
711builtin Decrement(implicit context: Context)(value: JSAny): Numeric {
712  try {
713    UnaryOp1(value) otherwise Number, BigInt;
714  } label Number(n: Number) {
715    tail Subtract(n, 1);
716  } label BigInt(b: BigInt) {
717    return runtime::BigIntUnaryOp(
718        context, b, SmiTag<Operation>(Operation::kDecrement));
719  }
720}
721
722builtin Increment(implicit context: Context)(value: JSAny): Numeric {
723  try {
724    UnaryOp1(value) otherwise Number, BigInt;
725  } label Number(n: Number) {
726    tail Add(n, 1);
727  } label BigInt(b: BigInt) {
728    return runtime::BigIntUnaryOp(
729        context, b, SmiTag<Operation>(Operation::kIncrement));
730  }
731}
732
733// Bitwise binary operations.
734
735extern macro BinaryOpAssembler::Generate_BitwiseBinaryOp(
736    constexpr Operation, JSAny, JSAny, Context): Object;
737
738builtin ShiftLeft(implicit context: Context)(
739    left: JSAny, right: JSAny): Object {
740  return Generate_BitwiseBinaryOp(Operation::kShiftLeft, left, right, context);
741}
742
743builtin ShiftRight(implicit context: Context)(
744    left: JSAny, right: JSAny): Object {
745  return Generate_BitwiseBinaryOp(Operation::kShiftRight, left, right, context);
746}
747
748builtin ShiftRightLogical(implicit context: Context)(
749    left: JSAny, right: JSAny): Object {
750  return Generate_BitwiseBinaryOp(
751      Operation::kShiftRightLogical, left, right, context);
752}
753
754builtin BitwiseAnd(implicit context: Context)(
755    left: JSAny, right: JSAny): Object {
756  return Generate_BitwiseBinaryOp(Operation::kBitwiseAnd, left, right, context);
757}
758
759builtin BitwiseOr(implicit context: Context)(
760    left: JSAny, right: JSAny): Object {
761  return Generate_BitwiseBinaryOp(Operation::kBitwiseOr, left, right, context);
762}
763
764builtin BitwiseXor(implicit context: Context)(
765    left: JSAny, right: JSAny): Object {
766  return Generate_BitwiseBinaryOp(Operation::kBitwiseXor, left, right, context);
767}
768
769// Relational builtins.
770
771builtin LessThan(implicit context: Context)(left: JSAny, right: JSAny): Object {
772  return RelationalComparison(Operation::kLessThan, left, right, context);
773}
774
775builtin LessThanOrEqual(implicit context: Context)(
776    left: JSAny, right: JSAny): Object {
777  return RelationalComparison(
778      Operation::kLessThanOrEqual, left, right, context);
779}
780
781builtin GreaterThan(implicit context: Context)(
782    left: JSAny, right: JSAny): Object {
783  return RelationalComparison(Operation::kGreaterThan, left, right, context);
784}
785
786builtin GreaterThanOrEqual(implicit context: Context)(
787    left: JSAny, right: JSAny): Object {
788  return RelationalComparison(
789      Operation::kGreaterThanOrEqual, left, right, context);
790}
791
792builtin Equal(implicit context: Context)(left: JSAny, right: JSAny): Object {
793  return Equal(left, right, context);
794}
795
796builtin StrictEqual(implicit context: Context)(
797    left: JSAny, right: JSAny): Object {
798  return ::StrictEqual(left, right);
799}
800
801}  // namespace number
802