[turbofan] Validate computed induction variable phi type

Bug: chromium:1051017
Change-Id: I1729c059f4bc4fc75615fa0aa8dacf44dc56dad4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2066968
Commit-Queue: Georg Neis <neis@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: Michael Stanton <mvstanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66526}
diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc
index eb17a5f..7542dea 100644
--- a/src/compiler/typer.cc
+++ b/src/compiler/typer.cc
@@ -136,6 +136,9 @@
 
   Type TypeConstant(Handle<Object> value);
 
+  bool InductionVariablePhiTypeIsPrefixedPoint(
+      InductionVariable* induction_var);
+
  private:
   Typer* typer_;
   LoopVariableOptimizer* induction_vars_;
@@ -383,6 +386,14 @@
   graph_reducer.ReduceGraph();
 
   if (induction_vars != nullptr) {
+    // Validate the types computed by TypeInductionVariablePhi.
+    for (auto entry : induction_vars->induction_variables()) {
+      InductionVariable* induction_var = entry.second;
+      if (induction_var->phi()->opcode() == IrOpcode::kInductionVariablePhi) {
+        CHECK(visitor.InductionVariablePhiTypeIsPrefixedPoint(induction_var));
+      }
+    }
+
     induction_vars->ChangeToPhisAndInsertGuards();
   }
 }
@@ -810,11 +821,13 @@
   Type initial_type = Operand(node, 0);
   Type increment_type = Operand(node, 2);
 
-  // Fallback to normal phi typing in a variety of cases: when the induction
-  // variable is not initially of type Integer (we want to work with ranges
-  // below), when the increment is zero (in that case, phi typing will generally
-  // yield a better type), and when the induction variable can become NaN
-  // (through addition/subtraction of opposing infinities).
+  // Fallback to normal phi typing in a variety of cases:
+  // - when the induction variable is not initially of type Integer, because we
+  //   want to work with ranges in the algorithm below.
+  // - when the increment is zero, because in that case normal phi typing will
+  //   generally yield a more precise type.
+  // - when the induction variable can become NaN (through addition/subtraction
+  //   of opposing infinities), because the code below can't handle that case.
   if (initial_type.IsNone() ||
       increment_type.Is(typer_->cache_->kSingletonZero) ||
       !initial_type.Is(typer_->cache_->kInteger) ||
@@ -833,9 +846,8 @@
     return type;
   }
 
-  // Now process the bounds.
   auto res = induction_vars_->induction_variables().find(node->id());
-  DCHECK(res != induction_vars_->induction_variables().end());
+  DCHECK_NE(res, induction_vars_->induction_variables().end());
   InductionVariable* induction_var = res->second;
   InductionVariable::ArithmeticType arithmetic_type = induction_var->Type();
 
@@ -848,13 +860,13 @@
     increment_min = increment_type.Min();
     increment_max = increment_type.Max();
   } else {
-    DCHECK_EQ(InductionVariable::ArithmeticType::kSubtraction, arithmetic_type);
+    DCHECK_EQ(arithmetic_type, InductionVariable::ArithmeticType::kSubtraction);
     increment_min = -increment_type.Max();
     increment_max = -increment_type.Min();
   }
 
   if (increment_min >= 0) {
-    // increasing sequence
+    // Increasing sequence.
     min = initial_type.Min();
     for (auto bound : induction_var->upper_bounds()) {
       Type bound_type = TypeOrNone(bound.bound);
@@ -874,7 +886,7 @@
     // The upper bound must be at least the initial value's upper bound.
     max = std::max(max, initial_type.Max());
   } else if (increment_max <= 0) {
-    // decreasing sequence
+    // Decreasing sequence.
     max = initial_type.Max();
     for (auto bound : induction_var->lower_bounds()) {
       Type bound_type = TypeOrNone(bound.bound);
@@ -895,9 +907,12 @@
     min = std::min(min, initial_type.Min());
   } else {
     // If the increment can be both positive and negative, the variable can go
-    // arbitrarily far.
-    return typer_->cache_->kInteger;
+    // arbitrarily far. Use the maximal range in that case. Note that this may
+    // be less precise than what ordinary typing would produce.
+    min = -V8_INFINITY;
+    max = +V8_INFINITY;
   }
+
   if (FLAG_trace_turbo_loop) {
     StdoutStream{} << std::setprecision(10) << "Loop ("
                    << NodeProperties::GetControlInput(node)->id()
@@ -909,9 +924,68 @@
                    << " for phi " << node->id() << ": (" << min << ", " << max
                    << ")\n";
   }
+
   return Type::Range(min, max, typer_->zone());
 }
 
+bool Typer::Visitor::InductionVariablePhiTypeIsPrefixedPoint(
+    InductionVariable* induction_var) {
+  Node* node = induction_var->phi();
+  DCHECK_EQ(node->opcode(), IrOpcode::kInductionVariablePhi);
+  Type type = NodeProperties::GetType(node);
+  Type initial_type = Operand(node, 0);
+  Node* arith = node->InputAt(1);
+  Type increment_type = Operand(node, 2);
+
+  // Intersect {type} with useful bounds.
+  for (auto bound : induction_var->upper_bounds()) {
+    Type bound_type = TypeOrNone(bound.bound);
+    if (!bound_type.Is(typer_->cache_->kInteger)) continue;
+    if (!bound_type.IsNone()) {
+      bound_type = Type::Range(
+          -V8_INFINITY,
+          bound_type.Max() - (bound.kind == InductionVariable::kStrict),
+          zone());
+    }
+    type = Type::Intersect(type, bound_type, typer_->zone());
+  }
+  for (auto bound : induction_var->lower_bounds()) {
+    Type bound_type = TypeOrNone(bound.bound);
+    if (!bound_type.Is(typer_->cache_->kInteger)) continue;
+    if (!bound_type.IsNone()) {
+      bound_type = Type::Range(
+          bound_type.Min() + (bound.kind == InductionVariable::kStrict),
+          +V8_INFINITY, typer_->zone());
+    }
+    type = Type::Intersect(type, bound_type, typer_->zone());
+  }
+
+  // Apply ordinary typing to the "increment" operation.
+  // clang-format off
+  switch (arith->opcode()) {
+#define CASE(x)                             \
+    case IrOpcode::k##x:                    \
+      type = Type##x(type, increment_type); \
+      break;
+    CASE(JSAdd)
+    CASE(JSSubtract)
+    CASE(NumberAdd)
+    CASE(NumberSubtract)
+    CASE(SpeculativeNumberAdd)
+    CASE(SpeculativeNumberSubtract)
+    CASE(SpeculativeSafeIntegerAdd)
+    CASE(SpeculativeSafeIntegerSubtract)
+#undef CASE
+    default:
+      UNREACHABLE();
+  }
+  // clang-format on
+
+  type = Type::Union(initial_type, type, typer_->zone());
+
+  return type.Is(NodeProperties::GetType(node));
+}
+
 Type Typer::Visitor::TypeEffectPhi(Node* node) { UNREACHABLE(); }
 
 Type Typer::Visitor::TypeLoopExit(Node* node) { UNREACHABLE(); }