// 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: --allow-natives-syntax --maglev // This is a fairly complex tests that tries to combine the following things: // // - compile a function in a garbage-initialized zone (ie, a zone that isn't // initialized with 0s). // // - have a loop phi whose input count get reduced (because the graph builder // realizes that one of its predecessor is dead) // // - have this loop phi have a Phi as backedge. // // A bit of context: in an optimization for Phi untagging, I was iterating Phi // inputs on RecordUseHint, but I didn't want to visit the backedge if it wasn't // bound. To know if it was bound or not, I would set it to nullptr when // creating the loop phi, and I could thus check if the last input was nullptr // or not. However, this doesn't work when phi inputs get shrunk, as the last // input (the backedge) could then contain garbage before it's bound (but this // garbage wouldn't be nullptr, so it would look like a regular valid // pointer). This didn't show any bug in mjsunit; but this test reproduces the // error. function f(a) { let x = 2; let i = 0; if (a) { x += 14; } else { %DeoptimizeNow(); if (a === 0) { // The following addition has no feedback, which will cause Maglev to // replace it with an unconditional deopt, thus removing one of the // predecessors of the while-loop header. x += 25; } } // The following loop has a Phi for `x`, which initially is seen by the graph // builder as having 3 predecessors. And its backedge is a Phi (that's // important). One of its predecessor is removed during graph building because // the `x += 25` is replaced by a Deopt. while (i < 5) { // We now have a Int32 use of `x`. It should be recorded as such, and should // be propagated upwards in the Phi inputs of `x`. However, the backedge // hasn't been bound yet, so we should skip it. Note that the backedge is at // offset 1 rather than 2 because we've reduced the number of predecessors // of `x`. x ^ 2; x = a ? i - 2 : 8; i++; } } %PrepareFunctionForOptimization(f); f(1); %OptimizeMaglevOnNextCall(f); f(1); // We now cause `f` to deopt so that it can be re-optimized, ideally using the // same zone as before (which is thus not 0-initialized anymore but rather // filled with old garbage in release mode, and filled with non-zero invalid // pointers in debug mode (something like 0xcdcdcdcdcdcdcdcd). f(); %OptimizeMaglevOnNextCall(f); f(1);