diff options
Diffstat (limited to 'deps/v8/test/mjsunit/maglev/phi-untagging-conversions.js')
-rw-r--r-- | deps/v8/test/mjsunit/maglev/phi-untagging-conversions.js | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/deps/v8/test/mjsunit/maglev/phi-untagging-conversions.js b/deps/v8/test/mjsunit/maglev/phi-untagging-conversions.js new file mode 100644 index 0000000000..6c9a19aa0e --- /dev/null +++ b/deps/v8/test/mjsunit/maglev/phi-untagging-conversions.js @@ -0,0 +1,135 @@ +// Copyright 2023 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: --maglev --allow-natives-syntax --no-always-turbofan + + +// In this example, the final block of the graph will be something like: +// +// 1: Phi(#42, y) +// 2: CheckedSmiUntag(1) +// 3: Return(1) +// +// Note how the truncation is unused (but still required, as the graph builder +// doesn't know that all of the inputs of the Phi are Smis). After Phi +// untagging, the phi will be a Int32, and the truncation is thus not needed +// anymore. It's important that it's removed during Phi untagging, because its +// input is not tagged anymore (since the phi was untagged), and it's not +// necessary to replace it by something else. + +function unused_unneeded_CheckedSmiUntag(x) { + let y = x + 1; + y = x ? 42 : y; + return y | 0; +} + +%PrepareFunctionForOptimization(unused_unneeded_CheckedSmiUntag); +unused_unneeded_CheckedSmiUntag(1); +unused_unneeded_CheckedSmiUntag(0); +%OptimizeMaglevOnNextCall(unused_unneeded_CheckedSmiUntag); +unused_unneeded_CheckedSmiUntag(1); + +// This example is similar as the previous one, except that the graph will +// contain a CheckedTruncateNumberToInt32 instead of the CheckedSmiUntag: +// +// 1: Phi(c1, c2) +// 2: CheckedTruncateNumberToInt32(1) +// 3: Return(undefined) +// +// The conversion only fails when its input is not a number. Since the Phi was +// untagged to a Float64, the conversion can never fail anymore, and should thus +// be omitted. +// +// Note that if the result of the CheckedTruncateNumberToInt32 would have been +// used, the it would have been automatically changed into a +// TruncateFloat64ToInt32. +// +// A side-effect of have this truncation is that it ensures that its input is a +// boxed Float64, which means that subsequent untagging can use +// UncheckedNumberToFloat64 instead of CheckedNumberToFloat64. This is what is +// used here when doing `let as_double = phi + 0.6`. This +// UncheckedNumberToFloat64 will just be dropped, since the input (the phi) is +// already an unboxed Float64. + +function unused_unneeded_CheckedTruncateNumberToInt32(x) { + let c1 = x + 0.5; + let c2 = x + 1.5; + let phi = x ? c1 : c2; + let as_int = phi | 0; + let as_double = phi + 0.6; +} + +%PrepareFunctionForOptimization(unused_unneeded_CheckedTruncateNumberToInt32); +unused_unneeded_CheckedTruncateNumberToInt32(1.5); +%OptimizeMaglevOnNextCall(unused_unneeded_CheckedTruncateNumberToInt32); +unused_unneeded_CheckedTruncateNumberToInt32(1.5); + + + +// In this example, during feedback collection, we provide inputs that cause +// `phi` to always be a Smi, which means that `phi | 0` will have Smi +// feedback. The graph will thus be: +// +// 1: Phi(d, #42) +// 2: CheckedSmiUntag(1) +// 3: Return(1) +// +// Except that Phi untagging will realize that `phi` could be a Float64, and +// will thus decide that it should be a Float64 Phi. In this case, the +// conversion should not be dropped, because if the Phi is not a Int32, then it +// should not be returned directly, but instead be truncated. In practice +// though, we'll rather replace it by a CheckedTruncateFloat64ToInt32, which +// will fail if the Float64 isn't a Int32, causing a deopt, and the reoptimized +// code will have a better feedback (this is easier than to try to patch the +// `Return(1)` to return something else). +function unused_required_CheckedSmiUntag(x) { + x = x + 0.5; // ensuring Float64 alternative + let d = x + 2.53; + let phi = x ? d : 42; + return phi | 0; +} + +%PrepareFunctionForOptimization(unused_required_CheckedSmiUntag); +unused_required_CheckedSmiUntag(-0.5); +%OptimizeMaglevOnNextCall(unused_required_CheckedSmiUntag); +assertEquals(42, unused_required_CheckedSmiUntag(-0.5)); +assertOptimized(unused_required_CheckedSmiUntag); +// If the CheckedSmiUntag is dropped, then the truncation won't be done, and the +// non-truncated float (3.53) will be returned. Instead, if the conversion is +// changed to CheckedTruncateFloat64ToInt32, then it will deopt, and we'll get +// the correct result of 3. +assertEquals(3, unused_required_CheckedSmiUntag(0.5)); +assertUnoptimized(unused_required_CheckedSmiUntag); + + + +// Finally, int this example, during feedback collection, `phi` will always be a +// Smi, which means that `phi + 2` will be a Int32AddWithOverflow, preceeded by +// a CheckedSmiUntag(Phi): +// +// 1: Phi(d, #42) +// 2: CheckedSmiUntag(1) +// 3: Int32AddWithOverflow(2, #2) +// +// However, Phi untagging will detect that `phi` should be a Float64 phi. In +// that case, it's important that the CheckedSmiUntag conversion isn't dropped, +// and becomes a deopting Float64->Int32 conversion that deopts when its input +// cannot be converted to Int32 without loss of precision (ie, it should become +// a CheckedTruncateFloat64ToInt32 rather than a TruncateFloat64ToInt32). Then, +// when the Phi turns out to be a non-Smi Float64, the function should deopt. +function used_required_deopting_Float64ToInt32(x) { + x = x + 0.5; // ensuring Float64 alternative + let d = x + 2.53; + let phi = x ? d : 42; + return phi + 2; +} +%PrepareFunctionForOptimization(used_required_deopting_Float64ToInt32); +used_required_deopting_Float64ToInt32(-0.5); +%OptimizeMaglevOnNextCall(used_required_deopting_Float64ToInt32); +assertEquals(44, used_required_deopting_Float64ToInt32(-0.5)); +// The next call should cause a deopt, since `phi` will be 4.53, which shouldn't +// be truncated to go into the Int32AddWithOverflow but should instead cause a +// deopt to allow the `phi + 2` to be computed on double values. +assertEquals(1.5+0.5+2.53+2, used_required_deopting_Float64ToInt32(1.5)); +assertUnoptimized(used_required_deopting_Float64ToInt32); |