nodejs/deps/v8/test/mjsunit/additive-safe-int-feedback.js
Michaël Zasso 918fe04351
deps: update V8 to 13.6.233.8
PR-URL: https://github.com/nodejs/node/pull/58070
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Darshan Sen <raisinten@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
2025-05-02 15:06:53 +02:00

347 lines
11 KiB
JavaScript

// Copyright 2024 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 --additive-safe-int-feedback
// Flags: --turbofan --no-always-turbofan
const maxAdditiveSafeInteger = 4503599627370495; // 2^52 - 1
const minAdditiveSafeInteger = - 4503599627370496; // - 2^52
// If one of the inputs is a constant in the additive safe range,
// we can use AdditiveSafeInteger.
(function() {
function foo(a) {
return a + 1;
}
%PrepareFunctionForOptimization(foo);
assertEquals(1231234567891, foo(1231234567890));
assertEquals(1231234567891, foo(1231234567890));
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567891, foo(1231234567890));
assertOptimized(foo);
// We don't deopt in the maximum value.
assertEquals(maxAdditiveSafeInteger, foo(maxAdditiveSafeInteger - 1));
assertOptimized(foo);
// Nor in the minimum.
assertEquals(minAdditiveSafeInteger + 1, foo(minAdditiveSafeInteger));
assertOptimized(foo);
// We deopt if we overflow!
assertEquals(maxAdditiveSafeInteger + 1, foo(maxAdditiveSafeInteger));
assertUnoptimized(foo);
// Optimize again.
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567891, foo(1231234567890));
assertOptimized(foo);
// This time we don't overflow, since we use
// Float64Add.
assertEquals(maxAdditiveSafeInteger + 1, foo(maxAdditiveSafeInteger));
assertOptimized(foo);
// Don't deopt with doubles.
assertEquals(2.5, foo(1.5));
assertOptimized(foo);
})();
// Deopt if input is a double.
(function() {
function foo(a) {
return a + 1;
}
%PrepareFunctionForOptimization(foo);
assertEquals(1231234567891, foo(1231234567890));
assertEquals(1231234567891, foo(1231234567890));
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567891, foo(1231234567890));
assertOptimized(foo);
// Deopt if input is a double.
assertEquals(2.5, foo(1.5));
assertUnoptimized(foo);
// Optimize again.
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567891, foo(1231234567890));
assertOptimized(foo);
// Don't deopt with doubles anymore.
assertEquals(2.5, foo(1.5));
assertOptimized(foo);
})();
// If we don't know statically that one of the inputs is
// in the additive safe range, we fallback to float64 add.
(function() {
function foo(a, b) {
return a + b;
}
%PrepareFunctionForOptimization(foo);
assertEquals(1231234567891, foo(1231234567890, 1));
assertEquals(1231234567891, foo(1231234567890, 1));
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567891, foo(1231234567890, 1));
assertOptimized(foo);
// We don't deopt in overflow.
assertEquals(maxAdditiveSafeInteger + 1, foo(maxAdditiveSafeInteger, 1));
assertOptimized(foo);
// Don't deopt with doubles.
assertEquals(2.5, foo(1.5, 1));
assertOptimized(foo);
})();
// We deopt in minus zero.
(function() {
function foo(a) {
return a + 1;
}
%PrepareFunctionForOptimization(foo);
assertEquals(1231234567891, foo(1231234567890));
assertEquals(1231234567891, foo(1231234567890));
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567891, foo(1231234567890));
assertOptimized(foo);
// TODO(victorgomes): If we know one of the inputs is
// definitely not minus zero, we could avoid deopting here.
assertEquals(1, foo(-0));
assertUnoptimized(foo);
// Optimize again.
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567891, foo(1231234567890));
assertOptimized(foo);
// Don't deopt with doubles.
assertEquals(2.5, foo(1.5));
assertOptimized(foo);
})();
// Optimize to AdditiveSafeInteger if one of the inputs is truncated, since it is
// in the safe integer range.
(function() {
function foo(a, b) {
return a + (b | 0);
}
%PrepareFunctionForOptimization(foo);
assertEquals(1231234567891, foo(1231234567890, 1));
assertEquals(1231234567891, foo(1231234567890, 1));
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567891, foo(1231234567890, 1));
assertOptimized(foo);
// We don't deopt in the maximum value.
assertEquals(maxAdditiveSafeInteger, foo(maxAdditiveSafeInteger - 1, 1));
assertOptimized(foo);
// Nor in the minimum.
assertEquals(minAdditiveSafeInteger + 1, foo(minAdditiveSafeInteger, 1));
assertOptimized(foo);
// We deopt if we overflow!
assertEquals(maxAdditiveSafeInteger + 1, foo(maxAdditiveSafeInteger, 1));
assertUnoptimized(foo);
// Optimize again.
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567891, foo(1231234567890, 1));
assertOptimized(foo);
// This time we don't overflow, since we use
// Float64Add.
assertEquals(maxAdditiveSafeInteger + 1, foo(maxAdditiveSafeInteger, 1));
assertOptimized(foo);
// Don't deopt with doubles.
assertEquals(2.5, foo(1.5, 1));
assertOptimized(foo);
})();
// Optimize when the result is pass to another add, since we know the result of
// the add is in the safe integer range.
(function() {
function foo(a, b) {
return 1 + a + b;
}
%PrepareFunctionForOptimization(foo);
assertEquals(1231234567892, foo(1231234567890, 1));
assertEquals(1231234567892, foo(1231234567890, 1));
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567892, foo(1231234567890, 1));
assertOptimized(foo);
// We don't deopt in the maximum value.
assertEquals(maxAdditiveSafeInteger, foo(maxAdditiveSafeInteger - 2, 1));
assertOptimized(foo);
// Nor in the minimum.
assertEquals(minAdditiveSafeInteger + 2, foo(minAdditiveSafeInteger, 1));
assertOptimized(foo);
// Overflow the second add to ensure that we optimize with additive safe int add.
assertEquals(maxAdditiveSafeInteger + 1, foo(maxAdditiveSafeInteger - 1, 1));
assertUnoptimized(foo);
// Optimize again.
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567892, foo(1231234567890, 1));
assertOptimized(foo);
// This time we don't overflow, since we useFloat64Add.
assertEquals(maxAdditiveSafeInteger + 1, foo(maxAdditiveSafeInteger - 1, 1));
assertOptimized(foo);
// But we can still deopt the first add.
assertEquals(maxAdditiveSafeInteger + 2, foo(maxAdditiveSafeInteger, 1));
assertUnoptimized(foo);
// Optimize again.
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567892, foo(1231234567890, 1));
assertOptimized(foo);
// This time no deopts.
assertEquals(maxAdditiveSafeInteger + 1, foo(maxAdditiveSafeInteger - 1, 1));
assertEquals(maxAdditiveSafeInteger + 2, foo(maxAdditiveSafeInteger, 1));
assertOptimized(foo);
// Don't deopt with doubles.
assertEquals(3.5, foo(1.5, 1));
assertOptimized(foo);
})();
// This also works if the known input is the middle one.
(function() {
function foo(a, b) {
return a + 1 + b;
}
%PrepareFunctionForOptimization(foo);
assertEquals(1231234567892, foo(1231234567890, 1));
assertEquals(1231234567892, foo(1231234567890, 1));
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567892, foo(1231234567890, 1));
assertOptimized(foo);
// We don't deopt in the maximum value.
assertEquals(maxAdditiveSafeInteger, foo(maxAdditiveSafeInteger - 2, 1));
assertOptimized(foo);
// Nor in the minimum.
assertEquals(minAdditiveSafeInteger + 2, foo(minAdditiveSafeInteger, 1));
assertOptimized(foo);
// Overflow the second add to ensure that we optimize with additive safe int add.
assertEquals(maxAdditiveSafeInteger + 1, foo(maxAdditiveSafeInteger - 1, 1));
assertUnoptimized(foo);
// Optimize again.
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567892, foo(1231234567890, 1));
assertOptimized(foo);
// This time we don't overflow, since we useFloat64Add.
assertEquals(maxAdditiveSafeInteger + 1, foo(maxAdditiveSafeInteger - 1, 1));
assertOptimized(foo);
// But we can still deopt the first add.
assertEquals(maxAdditiveSafeInteger + 2, foo(maxAdditiveSafeInteger, 1));
assertUnoptimized(foo);
// Optimize again.
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567892, foo(1231234567890, 1));
assertOptimized(foo);
// This time no deopts.
assertEquals(maxAdditiveSafeInteger + 1, foo(maxAdditiveSafeInteger - 1, 1));
assertEquals(maxAdditiveSafeInteger + 2, foo(maxAdditiveSafeInteger, 1));
assertOptimized(foo);
// Don't deopt with doubles.
assertEquals(3.5, foo(1.5, 1));
assertOptimized(foo);
})();
// Since add is not an associative operation in JS, unfortunately, we don't
// optimize the first add.
(function() {
function foo(a, b) {
return a + b + 1;
}
%PrepareFunctionForOptimization(foo);
assertEquals(1231234567892, foo(1231234567890, 1));
assertEquals(1231234567892, foo(1231234567890, 1));
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567892, foo(1231234567890, 1));
assertOptimized(foo);
// Overflow the second add to ensure that we optimize with additive safe int add.
assertEquals(maxAdditiveSafeInteger + 1, foo(maxAdditiveSafeInteger - 1, 1));
assertUnoptimized(foo);
// Optimize again.
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567892, foo(1231234567890, 1));
assertOptimized(foo);
// This time we don't overflow, since we use Float64Add.
assertEquals(maxAdditiveSafeInteger + 1, foo(maxAdditiveSafeInteger - 1, 1));
assertOptimized(foo);
// And we cannot deopt by overflowing the first one.
assertEquals(maxAdditiveSafeInteger + 2, foo(maxAdditiveSafeInteger, 1));
assertOptimized(foo);
// Don't deopt with doubles.
assertEquals(3.5, foo(1.5, 1));
assertOptimized(foo);
})();
// Don't need to deopt in overflow if we are truncating the result.
(function() {
function foo(a) {
return a + 1 | 0;
}
%PrepareFunctionForOptimization(foo);
assertEquals(1231234567891 | 0, foo(1231234567890, 1));
assertEquals(1231234567891 | 0, foo(1231234567890, 1));
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567891 | 0, foo(1231234567890, 1));
assertOptimized(foo);
// We don't deopt in the maximum value.
assertEquals(maxAdditiveSafeInteger | 0, foo(maxAdditiveSafeInteger - 1));
assertOptimized(foo);
// Nor in the minimum.
assertEquals(minAdditiveSafeInteger + 1 | 0, foo(minAdditiveSafeInteger));
assertOptimized(foo);
// We don't deopt in overflow!
assertEquals(maxAdditiveSafeInteger + 1 | 0, foo(maxAdditiveSafeInteger));
assertOptimized(foo);
// Deopt with doubles.
assertEquals(2, foo(1.5));
assertUnoptimized(foo);
// Optimize again.
%OptimizeFunctionOnNextCall(foo);
assertEquals(1231234567891 | 0, foo(1231234567890, 1));
assertOptimized(foo);
// Don't deopt with doubles anymore.
assertEquals(2, foo(1.5, 1));
assertOptimized(foo);
})();