// Copyright 2022 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.

// Flags: --allow-natives-syntax --turbofan --no-always-turbofan

(function OptimizeAndTest() {
  function ShiftLeft(a, b) {
    return a << b;
  }
  %PrepareFunctionForOptimization(ShiftLeft);
  assertEquals(0n, ShiftLeft(0n, 42n));
  assertEquals(-42n, ShiftLeft(-42n, 0n));
  assertEquals(2n ** 42n, ShiftLeft(1n, 42n));
  assertEquals(-2n, ShiftLeft(-(2n ** 512n), -511n));
  assertEquals(-1n, ShiftLeft(-(2n ** 512n), -513n));

  %OptimizeFunctionOnNextCall(ShiftLeft);
  assertEquals(0n, ShiftLeft(0n, 42n));
  assertEquals(-42n, ShiftLeft(-42n, 0n));
  assertEquals(2n ** 42n, ShiftLeft(1n, 42n));
  assertEquals(-2n, ShiftLeft(-(2n ** 512n), -511n));
  assertEquals(-1n, ShiftLeft(-(2n ** 512n), -513n));
  assertOptimized(ShiftLeft);

  assertThrows(() => ShiftLeft(1n, 2n ** 30n), RangeError);
  assertUnoptimized(ShiftLeft);
})();

(function OptimizeAndTest() {
  function ShiftLeftByPositive(a) {
    return BigInt.asIntN(62, a << 42n);
  }
  %PrepareFunctionForOptimization(ShiftLeftByPositive);
  assertEquals(0n, ShiftLeftByPositive(0n));

  %OptimizeFunctionOnNextCall(ShiftLeftByPositive);
  assertEquals(0n, ShiftLeftByPositive(0n));
  assertEquals(2n ** 42n, ShiftLeftByPositive(1n));
  assertEquals(2n ** 42n, ShiftLeftByPositive(1n + 2n ** 62n));
  assertEquals(-(2n ** 42n), ShiftLeftByPositive(-1n - 2n ** 64n));
  assertOptimized(ShiftLeftByPositive);

  assertThrows(() => ShiftLeftByPositive(0), TypeError);
  assertUnoptimized(ShiftLeftByPositive);
})();

(function OptimizeAndTest() {
  const minus42 = -42n;
  function ShiftLeftByNegative(a) {
    return BigInt.asIntN(62, BigInt.asUintN(64, a) << minus42);
  }
  %PrepareFunctionForOptimization(ShiftLeftByNegative);
  assertEquals(0n, ShiftLeftByNegative(0n));

  %OptimizeFunctionOnNextCall(ShiftLeftByNegative);
  assertEquals(0n, ShiftLeftByNegative(42n));
  assertEquals(4194303n, ShiftLeftByNegative(-42n));
  assertEquals(2n ** 20n, ShiftLeftByNegative(1n + 2n ** 62n));
  assertEquals(3145727n, ShiftLeftByNegative(-1n - 2n ** 62n - 2n ** 64n));
  assertOptimized(ShiftLeftByNegative);

  assertThrows(() => ShiftLeftByNegative(0), TypeError);
  if (%Is64Bit()) {
    // BigInt truncation is not inlined on 32-bit platforms so there is no
    // checks for BigInt, thus deopt will not be triggered.
    assertUnoptimized(ShiftLeftByNegative);
  }
})();

(function OptimizeAndTest() {
  function ShiftLeftBy64(a) {
    return BigInt.asIntN(62, a << 64n);
  }
  %PrepareFunctionForOptimization(ShiftLeftBy64);
  assertEquals(0n, ShiftLeftBy64(0n));

  %OptimizeFunctionOnNextCall(ShiftLeftBy64);
  assertEquals(0n, ShiftLeftBy64(0n));
  assertEquals(0n, ShiftLeftBy64(1n));
  assertEquals(0n, ShiftLeftBy64(1n + 2n ** 62n));
  assertEquals(0n, ShiftLeftBy64(-1n - 2n ** 64n));
  assertOptimized(ShiftLeftBy64);

  assertThrows(() => ShiftLeftBy64(0), TypeError);
  assertUnoptimized(ShiftLeftBy64);
})();

(function OptimizeAndTest() {
  const bi = 2n ** 64n;
  function ShiftLeftByLarge(a) {
    return BigInt.asIntN(62, a << bi);
  }
  %PrepareFunctionForOptimization(ShiftLeftByLarge);
  assertEquals(0n, ShiftLeftByLarge(0n));

  %OptimizeFunctionOnNextCall(ShiftLeftByLarge);
  assertEquals(0n, ShiftLeftByLarge(0n));
  if (%Is64Bit()) {
    // After optimization, a truncated left shift will not throw a
    // BigIntTooBig exception just as truncated addition.
    assertEquals(0n, ShiftLeftByLarge(1n));
    assertEquals(0n, ShiftLeftByLarge(1n + 2n ** 62n));
    assertEquals(0n, ShiftLeftByLarge(-1n - 2n ** 64n));
  }
  assertOptimized(ShiftLeftByLarge);

  assertThrows(() => ShiftLeftByLarge(0), TypeError);
  assertUnoptimized(ShiftLeftByLarge);
})();
