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