Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/js/src/jit/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 117 kB image not shown  

Quelle  RangeAnalysis.cpp

  Sprache: C
 

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "jit/RangeAnalysis.h"

#include "mozilla/MathAlgorithms.h"

#include <algorithm>

#include "jsmath.h"

#include "jit/CompileInfo.h"
#include "jit/IonAnalysis.h"
#include "jit/JitSpewer.h"
#include "jit/MIR-wasm.h"
#include "jit/MIR.h"
#include "jit/MIRGenerator.h"
#include "jit/MIRGraph.h"
#include "js/Conversions.h"
#include "js/ScalarType.h"  // js::Scalar::Type
#include "util/CheckedArithmetic.h"
#include "util/Unicode.h"
#include "vm/ArgumentsObject.h"
#include "vm/Float16.h"
#include "vm/TypedArrayObject.h"
#include "vm/Uint8Clamped.h"

#include "vm/BytecodeUtil-inl.h"

using namespace js;
using namespace js::jit;

using JS::GenericNaN;
using JS::ToInt32;
using mozilla::Abs;
using mozilla::CountLeadingZeroes32;
using mozilla::ExponentComponent;
using mozilla::FloorLog2;
using mozilla::IsNegativeZero;
using mozilla::NegativeInfinity;
using mozilla::NumberEqualsInt32;
using mozilla::PositiveInfinity;

// [SMDOC] IonMonkey Range Analysis
//
// This algorithm is based on the paper "Eliminating Range Checks Using
// Static Single Assignment Form" by Gough and Klaren.
//
// We associate a range object with each SSA name, and the ranges are consulted
// in order to determine whether overflow is possible for arithmetic
// computations.
//
// An important source of range information that requires care to take
// advantage of is conditional control flow. Consider the code below:
//
// if (x < 0) {
//   y = x + 2000000000;
// } else {
//   if (x < 1000000000) {
//     y = x * 2;
//   } else {
//     y = x - 3000000000;
//   }
// }
//
// The arithmetic operations in this code cannot overflow, but it is not
// sufficient to simply associate each name with a range, since the information
// differs between basic blocks. The traditional dataflow approach would be
// associate ranges with (name, basic block) pairs. This solution is not
// satisfying, since we lose the benefit of SSA form: in SSA form, each
// definition has a unique name, so there is no need to track information about
// the control flow of the program.
//
// The approach used here is to add a new form of pseudo operation called a
// beta node, which associates range information with a value. These beta
// instructions take one argument and additionally have an auxiliary constant
// range associated with them. Operationally, beta nodes are just copies, but
// the invariant expressed by beta node copies is that the output will fall
// inside the range given by the beta node.  Gough and Klaeren refer to SSA
// extended with these beta nodes as XSA form. The following shows the example
// code transformed into XSA form:
//
// if (x < 0) {
//   x1 = Beta(x, [INT_MIN, -1]);
//   y1 = x1 + 2000000000;
// } else {
//   x2 = Beta(x, [0, INT_MAX]);
//   if (x2 < 1000000000) {
//     x3 = Beta(x2, [INT_MIN, 999999999]);
//     y2 = x3*2;
//   } else {
//     x4 = Beta(x2, [1000000000, INT_MAX]);
//     y3 = x4 - 3000000000;
//   }
//   y4 = Phi(y2, y3);
// }
// y = Phi(y1, y4);
//
// We insert beta nodes for the purposes of range analysis (they might also be
// usefully used for other forms of bounds check elimination) and remove them
// after range analysis is performed. The remaining compiler phases do not ever
// encounter beta nodes.

static bool IsDominatedUse(const MBasicBlock* block, const MUse* use) {
  MNode* n = use->consumer();
  bool isPhi = n->isDefinition() && n->toDefinition()->isPhi();

  if (isPhi) {
    MPhi* phi = n->toDefinition()->toPhi();
    return block->dominates(phi->block()->getPredecessor(phi->indexOf(use)));
  }

  return block->dominates(n->block());
}

static inline void SpewRange(const MDefinition* def) {
#ifdef JS_JITSPEW
  if (JitSpewEnabled(JitSpew_Range) && def->type() != MIRType::None &&
      def->range()) {
    JitSpewHeader(JitSpew_Range);
    Fprinter& out = JitSpewPrinter();
    out.printf("  ");
    def->printName(out);
    out.printf(" has range ");
    def->range()->dump(out);
    out.printf("\n");
  }
#endif
}

#ifdef JS_JITSPEW
static const char* TruncateKindString(TruncateKind kind) {
  switch (kind) {
    case TruncateKind::NoTruncate:
      return "NoTruncate";
    case TruncateKind::TruncateAfterBailouts:
      return "TruncateAfterBailouts";
    case TruncateKind::IndirectTruncate:
      return "IndirectTruncate";
    case TruncateKind::Truncate:
      return "Truncate";
    default:
      MOZ_CRASH("Unknown truncate kind.");
  }
}

static inline void SpewTruncate(const MDefinition* def, TruncateKind kind,
                                bool shouldClone) {
  if (JitSpewEnabled(JitSpew_Range)) {
    JitSpewHeader(JitSpew_Range);
    Fprinter& out = JitSpewPrinter();
    out.printf("  ");
    out.printf("truncating ");
    def->printName(out);
    out.printf(" (kind: %s, clone: %d)\n", TruncateKindString(kind),
               shouldClone);
  }
}
#else
static inline void SpewTruncate(MDefinition* def, TruncateKind kind,
                                bool shouldClone) {}
#endif

TempAllocator& RangeAnalysis::alloc() const { return graph_.alloc(); }

static void ReplaceDominatedUsesWith(const MDefinition* orig, MDefinition* dom,
                                     const MBasicBlock* block) {
  for (MUseIterator i(orig->usesBegin()); i != orig->usesEnd();) {
    MUse* use = *i++;
    if (use->consumer() != dom && IsDominatedUse(block, use)) {
      use->replaceProducer(dom);
    }
  }
}

bool RangeAnalysis::addBetaNodes() {
  JitSpew(JitSpew_Range, "Adding beta nodes");

  for (PostorderIterator i(graph_.poBegin()); i != graph_.poEnd(); i++) {
    MBasicBlock* block = *i;
    JitSpew(JitSpew_Range, "Looking at block %u", block->id());

    BranchDirection branch_dir;
    MTest* test = block->immediateDominatorBranch(&branch_dir);

    if (!test || !test->getOperand(0)->isCompare()) {
      continue;
    }

    MCompare* compare = test->getOperand(0)->toCompare();

    if (!compare->isNumericComparison()) {
      continue;
    }

    // TODO: support unsigned comparisons
    if (compare->compareType() == MCompare::Compare_UInt32) {
      continue;
    }

    // isNumericComparison should return false for (U)IntPtr.
    MOZ_ASSERT(compare->compareType() != MCompare::Compare_IntPtr &&
               compare->compareType() != MCompare::Compare_UIntPtr);

    MDefinition* left = compare->getOperand(0);
    MDefinition* right = compare->getOperand(1);
    double bound;
    double conservativeLower = NegativeInfinity<double>();
    double conservativeUpper = PositiveInfinity<double>();
    MDefinition* val = nullptr;

    JSOp jsop = compare->jsop();

    if (branch_dir == FALSE_BRANCH) {
      jsop = NegateCompareOp(jsop);
      conservativeLower = GenericNaN();
      conservativeUpper = GenericNaN();
    }

    MConstant* leftConst = left->maybeConstantValue();
    MConstant* rightConst = right->maybeConstantValue();
    if (leftConst && leftConst->isTypeRepresentableAsDouble()) {
      bound = leftConst->numberToDouble();
      val = right;
      jsop = ReverseCompareOp(jsop);
    } else if (rightConst && rightConst->isTypeRepresentableAsDouble()) {
      bound = rightConst->numberToDouble();
      val = left;
    } else if (left->type() == MIRType::Int32 &&
               right->type() == MIRType::Int32) {
      MDefinition* smaller = nullptr;
      MDefinition* greater = nullptr;
      if (jsop == JSOp::Lt) {
        smaller = left;
        greater = right;
      } else if (jsop == JSOp::Gt) {
        smaller = right;
        greater = left;
      }
      if (smaller && greater) {
        if (!alloc().ensureBallast()) {
          return false;
        }

        MBeta* beta;
        beta = MBeta::New(
            alloc(), smaller,
            Range::NewInt32Range(alloc(), JSVAL_INT_MIN, JSVAL_INT_MAX - 1));
        block->insertBefore(*block->begin(), beta);
        ReplaceDominatedUsesWith(smaller, beta, block);
        JitSpew(JitSpew_Range, "  Adding beta node for smaller %u",
                smaller->id());
        beta = MBeta::New(
            alloc(), greater,
            Range::NewInt32Range(alloc(), JSVAL_INT_MIN + 1, JSVAL_INT_MAX));
        block->insertBefore(*block->begin(), beta);
        ReplaceDominatedUsesWith(greater, beta, block);
        JitSpew(JitSpew_Range, "  Adding beta node for greater %u",
                greater->id());
      }
      continue;
    } else {
      continue;
    }

    // At this point, one of the operands if the compare is a constant, and
    // val is the other operand.
    MOZ_ASSERT(val);

    Range comp;
    switch (jsop) {
      case JSOp::Le:
        comp.setDouble(conservativeLower, bound);
        break;
      case JSOp::Lt:
        // For integers, if x < c, the upper bound of x is c-1.
        if (val->type() == MIRType::Int32) {
          int32_t intbound;
          if (NumberEqualsInt32(bound, &intbound) &&
              SafeSub(intbound, 1, &intbound)) {
            bound = intbound;
          }
        }
        comp.setDouble(conservativeLower, bound);

        // Negative zero is not less than zero.
        if (bound == 0) {
          comp.refineToExcludeNegativeZero();
        }
        break;
      case JSOp::Ge:
        comp.setDouble(bound, conservativeUpper);
        break;
      case JSOp::Gt:
        // For integers, if x > c, the lower bound of x is c+1.
        if (val->type() == MIRType::Int32) {
          int32_t intbound;
          if (NumberEqualsInt32(bound, &intbound) &&
              SafeAdd(intbound, 1, &intbound)) {
            bound = intbound;
          }
        }
        comp.setDouble(bound, conservativeUpper);

        // Negative zero is not greater than zero.
        if (bound == 0) {
          comp.refineToExcludeNegativeZero();
        }
        break;
      case JSOp::StrictEq:
      case JSOp::Eq:
        comp.setDouble(bound, bound);
        break;
      case JSOp::StrictNe:
      case JSOp::Ne:
        // Negative zero is not not-equal to zero.
        if (bound == 0) {
          comp.refineToExcludeNegativeZero();
          break;
        }
        continue;  // well, we could have
                   // [-\inf, bound-1] U [bound+1, \inf] but we only use
                   // contiguous ranges.
      default:
        continue;
    }

    if (JitSpewEnabled(JitSpew_Range)) {
      JitSpewHeader(JitSpew_Range);
      Fprinter& out = JitSpewPrinter();
      out.printf("  Adding beta node for %u with range ", val->id());
      comp.dump(out);
      out.printf("\n");
    }

    if (!alloc().ensureBallast()) {
      return false;
    }

    MBeta* beta = MBeta::New(alloc(), val, new (alloc()) Range(comp));
    block->insertBefore(*block->begin(), beta);
    ReplaceDominatedUsesWith(val, beta, block);
  }

  return true;
}

bool RangeAnalysis::removeBetaNodes() {
  JitSpew(JitSpew_Range, "Removing beta nodes");

  for (PostorderIterator i(graph_.poBegin()); i != graph_.poEnd(); i++) {
    MBasicBlock* block = *i;
    for (MDefinitionIterator iter(*i); iter;) {
      MDefinition* def = *iter++;
      if (def->isBeta()) {
        auto* beta = def->toBeta();
        MDefinition* op = beta->input();
        JitSpew(JitSpew_Range, "  Removing beta node %u for %u", beta->id(),
                op->id());
        beta->justReplaceAllUsesWith(op);
        block->discard(beta);
      } else {
        // We only place Beta nodes at the beginning of basic
        // blocks, so if we see something else, we can move on
        // to the next block.
        break;
      }
    }
  }
  return true;
}

void SymbolicBound::dump(GenericPrinter& out) const {
  if (loop) {
    out.printf("[loop] ");
  }
  sum.dump(out);
}

void SymbolicBound::dump() const {
  Fprinter out(stderr);
  dump(out);
  out.printf("\n");
  out.finish();
}

// Test whether the given range's exponent tells us anything that its lower
// and upper bound values don't.
static bool IsExponentInteresting(const Range* r) {
  // If it lacks either a lower or upper bound, the exponent is interesting.
  if (!r->hasInt32Bounds()) {
    return true;
  }

  // Otherwise if there's no fractional part, the lower and upper bounds,
  // which are integers, are perfectly precise.
  if (!r->canHaveFractionalPart()) {
    return false;
  }

  // Otherwise, if the bounds are conservatively rounded across a power-of-two
  // boundary, the exponent may imply a tighter range.
  return FloorLog2(std::max(Abs(r->lower()), Abs(r->upper()))) > r->exponent();
}

void Range::dump(GenericPrinter& out) const {
  assertInvariants();

  // Floating-point or Integer subset.
  if (canHaveFractionalPart_) {
    out.printf("F");
  } else {
    out.printf("I");
  }

  out.printf("[");

  if (!hasInt32LowerBound_) {
    out.printf("?");
  } else {
    out.printf("%d", lower_);
  }
  if (symbolicLower_) {
    out.printf(" {");
    symbolicLower_->dump(out);
    out.printf("}");
  }

  out.printf(", ");

  if (!hasInt32UpperBound_) {
    out.printf("?");
  } else {
    out.printf("%d", upper_);
  }
  if (symbolicUpper_) {
    out.printf(" {");
    symbolicUpper_->dump(out);
    out.printf("}");
  }

  out.printf("]");

  bool includesNaN = max_exponent_ == IncludesInfinityAndNaN;
  bool includesNegativeInfinity =
      max_exponent_ >= IncludesInfinity && !hasInt32LowerBound_;
  bool includesPositiveInfinity =
      max_exponent_ >= IncludesInfinity && !hasInt32UpperBound_;
  bool includesNegativeZero = canBeNegativeZero_;

  if (includesNaN || includesNegativeInfinity || includesPositiveInfinity ||
      includesNegativeZero) {
    out.printf(" (");
    bool first = true;
    if (includesNaN) {
      if (first) {
        first = false;
      } else {
        out.printf(" ");
      }
      out.printf("U NaN");
    }
    if (includesNegativeInfinity) {
      if (first) {
        first = false;
      } else {
        out.printf(" ");
      }
      out.printf("U -Infinity");
    }
    if (includesPositiveInfinity) {
      if (first) {
        first = false;
      } else {
        out.printf(" ");
      }
      out.printf("U Infinity");
    }
    if (includesNegativeZero) {
      if (first) {
        first = false;
      } else {
        out.printf(" ");
      }
      out.printf("U -0");
    }
    out.printf(")");
  }
  if (max_exponent_ < IncludesInfinity && IsExponentInteresting(this)) {
    out.printf(" (< pow(2, %d+1))", max_exponent_);
  }
}

void Range::dump() const {
  Fprinter out(stderr);
  dump(out);
  out.printf("\n");
  out.finish();
}

Range* Range::intersect(TempAllocator& alloc, const Range* lhs,
                        const Range* rhs, bool* emptyRange) {
  *emptyRange = false;

  if (!lhs && !rhs) {
    return nullptr;
  }

  if (!lhs) {
    return new (alloc) Range(*rhs);
  }
  if (!rhs) {
    return new (alloc) Range(*lhs);
  }

  int32_t newLower = std::max(lhs->lower_, rhs->lower_);
  int32_t newUpper = std::min(lhs->upper_, rhs->upper_);

  // If upper < lower, then we have conflicting constraints. Consider:
  //
  // if (x < 0) {
  //   if (x > 0) {
  //     [Some code.]
  //   }
  // }
  //
  // In this case, the block is unreachable.
  if (newUpper < newLower) {
    // If both ranges can be NaN, the result can still be NaN.
    if (!lhs->canBeNaN() || !rhs->canBeNaN()) {
      *emptyRange = true;
    }
    return nullptr;
  }

  bool newHasInt32LowerBound =
      lhs->hasInt32LowerBound_ || rhs->hasInt32LowerBound_;
  bool newHasInt32UpperBound =
      lhs->hasInt32UpperBound_ || rhs->hasInt32UpperBound_;

  FractionalPartFlag newCanHaveFractionalPart = FractionalPartFlag(
      lhs->canHaveFractionalPart_ && rhs->canHaveFractionalPart_);
  NegativeZeroFlag newMayIncludeNegativeZero =
      NegativeZeroFlag(lhs->canBeNegativeZero_ && rhs->canBeNegativeZero_);

  uint16_t newExponent = std::min(lhs->max_exponent_, rhs->max_exponent_);

  // NaN is a special value which is neither greater than infinity or less than
  // negative infinity. When we intersect two ranges like [?, 0] and [0, ?], we
  // can end up thinking we have both a lower and upper bound, even though NaN
  // is still possible. In this case, just be conservative, since any case where
  // we can have NaN is not especially interesting.
  if (newHasInt32LowerBound && newHasInt32UpperBound &&
      newExponent == IncludesInfinityAndNaN) {
    return nullptr;
  }

  // If one of the ranges has a fractional part and the other doesn't, it's
  // possible that we will have computed a newExponent that's more precise
  // than our newLower and newUpper. This is unusual, so we handle it here
  // instead of in optimize().
  //
  // For example, consider the range F[0,1.5]. Range analysis represents the
  // lower and upper bound as integers, so we'd actually have
  // F[0,2] (< pow(2, 0+1)). In this case, the exponent gives us a slightly
  // more precise upper bound than the integer upper bound.
  //
  // When intersecting such a range with an integer range, the fractional part
  // of the range is dropped. The max exponent of 0 remains valid, so the
  // upper bound needs to be adjusted to 1.
  //
  // When intersecting F[0,2] (< pow(2, 0+1)) with a range like F[2,4],
  // the naive intersection is I[2,2], but since the max exponent tells us
  // that the value is always less than 2, the intersection is actually empty.
  if (lhs->canHaveFractionalPart() != rhs->canHaveFractionalPart() ||
      (lhs->canHaveFractionalPart() && newHasInt32LowerBound &&
       newHasInt32UpperBound && newLower == newUpper)) {
    refineInt32BoundsByExponent(newExponent, &newLower, &newHasInt32LowerBound,
                                &newUpper, &newHasInt32UpperBound);

    // If we're intersecting two ranges that don't overlap, this could also
    // push the bounds past each other, since the actual intersection is
    // the empty set.
    if (newLower > newUpper) {
      *emptyRange = true;
      return nullptr;
    }
  }

  return new (alloc)
      Range(newLower, newHasInt32LowerBound, newUpper, newHasInt32UpperBound,
            newCanHaveFractionalPart, newMayIncludeNegativeZero, newExponent);
}

void Range::unionWith(const Range* other) {
  int32_t newLower = std::min(lower_, other->lower_);
  int32_t newUpper = std::max(upper_, other->upper_);

  bool newHasInt32LowerBound =
      hasInt32LowerBound_ && other->hasInt32LowerBound_;
  bool newHasInt32UpperBound =
      hasInt32UpperBound_ && other->hasInt32UpperBound_;

  FractionalPartFlag newCanHaveFractionalPart = FractionalPartFlag(
      canHaveFractionalPart_ || other->canHaveFractionalPart_);
  NegativeZeroFlag newMayIncludeNegativeZero =
      NegativeZeroFlag(canBeNegativeZero_ || other->canBeNegativeZero_);

  uint16_t newExponent = std::max(max_exponent_, other->max_exponent_);

  rawInitialize(newLower, newHasInt32LowerBound, newUpper,
                newHasInt32UpperBound, newCanHaveFractionalPart,
                newMayIncludeNegativeZero, newExponent);
}

Range::Range(const MDefinition* def)
    : symbolicLower_(nullptr), symbolicUpper_(nullptr) {
  if (const Range* other = def->range()) {
    // The instruction has range information; use it.
    *this = *other;

    // Simulate the effect of converting the value to its type.
    // Note: we cannot clamp here, since ranges aren't allowed to shrink
    // and truncation can increase range again. So doing wrapAround to
    // mimick a possible truncation.
    switch (def->type()) {
      case MIRType::Int32:
        // MToNumberInt32 cannot truncate. So we can safely clamp.
        if (def->isToNumberInt32()) {
          clampToInt32();
        } else {
          wrapAroundToInt32();
        }
        break;
      case MIRType::Boolean:
        wrapAroundToBoolean();
        break;
      case MIRType::None:
        MOZ_CRASH("Asking for the range of an instruction with no value");
      default:
        break;
    }
  } else {
    // Otherwise just use type information. We can trust the type here
    // because we don't care what value the instruction actually produces,
    // but what value we might get after we get past the bailouts.
    switch (def->type()) {
      case MIRType::Int32:
        setInt32(JSVAL_INT_MIN, JSVAL_INT_MAX);
        break;
      case MIRType::Boolean:
        setInt32(0, 1);
        break;
      case MIRType::None:
        MOZ_CRASH("Asking for the range of an instruction with no value");
      default:
        setUnknown();
        break;
    }
  }

  // As a special case, MUrsh is permitted to claim a result type of
  // MIRType::Int32 while actually returning values in [0,UINT32_MAX] without
  // bailouts. If range analysis hasn't ruled out values in
  // (INT32_MAX,UINT32_MAX], set the range to be conservatively correct for
  // use as either a uint32 or an int32.
  if (!hasInt32UpperBound() && def->isUrsh() &&
      def->toUrsh()->bailoutsDisabled() && def->type() != MIRType::Int64) {
    lower_ = INT32_MIN;
  }

  assertInvariants();
}

static uint16_t ExponentImpliedByDouble(double d) {
  // Handle the special values.
  if (std::isnan(d)) {
    return Range::IncludesInfinityAndNaN;
  }
  if (std::isinf(d)) {
    return Range::IncludesInfinity;
  }

  // Otherwise take the exponent part and clamp it at zero, since the Range
  // class doesn't track fractional ranges.
  return uint16_t(std::max(int_fast16_t(0), ExponentComponent(d)));
}

void Range::setDouble(double l, double h) {
  MOZ_ASSERT(!(l > h));

  // Infer lower_, upper_, hasInt32LowerBound_, and hasInt32UpperBound_.
  if (l >= INT32_MIN && l <= INT32_MAX) {
    lower_ = int32_t(::floor(l));
    hasInt32LowerBound_ = true;
  } else if (l >= INT32_MAX) {
    lower_ = INT32_MAX;
    hasInt32LowerBound_ = true;
  } else {
    lower_ = INT32_MIN;
    hasInt32LowerBound_ = false;
  }
  if (h >= INT32_MIN && h <= INT32_MAX) {
    upper_ = int32_t(::ceil(h));
    hasInt32UpperBound_ = true;
  } else if (h <= INT32_MIN) {
    upper_ = INT32_MIN;
    hasInt32UpperBound_ = true;
  } else {
    upper_ = INT32_MAX;
    hasInt32UpperBound_ = false;
  }

  // Infer max_exponent_.
  uint16_t lExp = ExponentImpliedByDouble(l);
  uint16_t hExp = ExponentImpliedByDouble(h);
  max_exponent_ = std::max(lExp, hExp);

  canHaveFractionalPart_ = ExcludesFractionalParts;
  canBeNegativeZero_ = ExcludesNegativeZero;

  // Infer the canHaveFractionalPart_ setting. We can have a
  // fractional part if the range crosses through the neighborhood of zero. We
  // won't have a fractional value if the value is always beyond the point at
  // which double precision can't represent fractional values.
  uint16_t minExp = std::min(lExp, hExp);
  bool includesNegative = std::isnan(l) || l < 0;
  bool includesPositive = std::isnan(h) || h > 0;
  bool crossesZero = includesNegative && includesPositive;
  if (crossesZero || minExp < MaxTruncatableExponent) {
    canHaveFractionalPart_ = IncludesFractionalParts;
  }

  // Infer the canBeNegativeZero_ setting. We can have a negative zero if
  // either bound is zero.
  if (!(l > 0) && !(h < 0)) {
    canBeNegativeZero_ = IncludesNegativeZero;
  }

  optimize();
}

void Range::setDoubleSingleton(double d) {
  setDouble(d, d);

  // The above setDouble call is for comparisons, and treats negative zero
  // as equal to zero. We're aiming for a minimum range, so we can clear the
  // negative zero flag if the value isn't actually negative zero.
  if (!IsNegativeZero(d)) {
    canBeNegativeZero_ = ExcludesNegativeZero;
  }

  assertInvariants();
}

static inline bool MissingAnyInt32Bounds(const Range* lhs, const Range* rhs) {
  return !lhs->hasInt32Bounds() || !rhs->hasInt32Bounds();
}

Range* Range::add(TempAllocator& alloc, const Range* lhs, const Range* rhs) {
  int64_t l = (int64_t)lhs->lower_ + (int64_t)rhs->lower_;
  if (!lhs->hasInt32LowerBound() || !rhs->hasInt32LowerBound()) {
    l = NoInt32LowerBound;
  }

  int64_t h = (int64_t)lhs->upper_ + (int64_t)rhs->upper_;
  if (!lhs->hasInt32UpperBound() || !rhs->hasInt32UpperBound()) {
    h = NoInt32UpperBound;
  }

  // The exponent is at most one greater than the greater of the operands'
  // exponents, except for NaN and infinity cases.
  uint16_t e = std::max(lhs->max_exponent_, rhs->max_exponent_);
  if (e <= Range::MaxFiniteExponent) {
    ++e;
  }

  // Infinity + -Infinity is NaN.
  if (lhs->canBeInfiniteOrNaN() && rhs->canBeInfiniteOrNaN()) {
    e = Range::IncludesInfinityAndNaN;
  }

  return new (alloc) Range(
      l, h,
      FractionalPartFlag(lhs->canHaveFractionalPart() ||
                         rhs->canHaveFractionalPart()),
      NegativeZeroFlag(lhs->canBeNegativeZero() && rhs->canBeNegativeZero()),
      e);
}

Range* Range::sub(TempAllocator& alloc, const Range* lhs, const Range* rhs) {
  int64_t l = (int64_t)lhs->lower_ - (int64_t)rhs->upper_;
  if (!lhs->hasInt32LowerBound() || !rhs->hasInt32UpperBound()) {
    l = NoInt32LowerBound;
  }

  int64_t h = (int64_t)lhs->upper_ - (int64_t)rhs->lower_;
  if (!lhs->hasInt32UpperBound() || !rhs->hasInt32LowerBound()) {
    h = NoInt32UpperBound;
  }

  // The exponent is at most one greater than the greater of the operands'
  // exponents, except for NaN and infinity cases.
  uint16_t e = std::max(lhs->max_exponent_, rhs->max_exponent_);
  if (e <= Range::MaxFiniteExponent) {
    ++e;
  }

  // Infinity - Infinity is NaN.
  if (lhs->canBeInfiniteOrNaN() && rhs->canBeInfiniteOrNaN()) {
    e = Range::IncludesInfinityAndNaN;
  }

  return new (alloc)
      Range(l, h,
            FractionalPartFlag(lhs->canHaveFractionalPart() ||
                               rhs->canHaveFractionalPart()),
            NegativeZeroFlag(lhs->canBeNegativeZero() && rhs->canBeZero()), e);
}

Range* Range::and_(TempAllocator& alloc, const Range* lhs, const Range* rhs) {
  MOZ_ASSERT(lhs->isInt32());
  MOZ_ASSERT(rhs->isInt32());

  // If both numbers can be negative, result can be negative in the whole range
  if (lhs->lower() < 0 && rhs->lower() < 0) {
    return Range::NewInt32Range(alloc, INT32_MIN,
                                std::max(lhs->upper(), rhs->upper()));
  }

  // Only one of both numbers can be negative.
  // - result can't be negative
  // - Upper bound is minimum of both upper range,
  int32_t lower = 0;
  int32_t upper = std::min(lhs->upper(), rhs->upper());

  // EXCEPT when upper bound of non negative number is max value,
  // because negative value can return the whole max value.
  // -1 & 5 = 5
  if (lhs->lower() < 0) {
    upper = rhs->upper();
  }
  if (rhs->lower() < 0) {
    upper = lhs->upper();
  }

  return Range::NewInt32Range(alloc, lower, upper);
}

Range* Range::or_(TempAllocator& alloc, const Range* lhs, const Range* rhs) {
  MOZ_ASSERT(lhs->isInt32());
  MOZ_ASSERT(rhs->isInt32());
  // When one operand is always 0 or always -1, it's a special case where we
  // can compute a fully precise result. Handling these up front also
  // protects the code below from calling CountLeadingZeroes32 with a zero
  // operand or from shifting an int32_t by 32.
  if (lhs->lower() == lhs->upper()) {
    if (lhs->lower() == 0) {
      return new (alloc) Range(*rhs);
    }
    if (lhs->lower() == -1) {
      return new (alloc) Range(*lhs);
    }
  }
  if (rhs->lower() == rhs->upper()) {
    if (rhs->lower() == 0) {
      return new (alloc) Range(*lhs);
    }
    if (rhs->lower() == -1) {
      return new (alloc) Range(*rhs);
    }
  }

  // The code below uses CountLeadingZeroes32, which has undefined behavior
  // if its operand is 0. We rely on the code above to protect it.
  MOZ_ASSERT_IF(lhs->lower() >= 0, lhs->upper() != 0);
  MOZ_ASSERT_IF(rhs->lower() >= 0, rhs->upper() != 0);
  MOZ_ASSERT_IF(lhs->upper() < 0, lhs->lower() != -1);
  MOZ_ASSERT_IF(rhs->upper() < 0, rhs->lower() != -1);

  int32_t lower = INT32_MIN;
  int32_t upper = INT32_MAX;

  if (lhs->lower() >= 0 && rhs->lower() >= 0) {
    // Both operands are non-negative, so the result won't be less than either.
    lower = std::max(lhs->lower(), rhs->lower());
    // The result will have leading zeros where both operands have leading
    // zeros. CountLeadingZeroes32 of a non-negative int32 will at least be 1 to
    // account for the bit of sign.
    upper = int32_t(UINT32_MAX >> std::min(CountLeadingZeroes32(lhs->upper()),
                                           CountLeadingZeroes32(rhs->upper())));
  } else {
    // The result will have leading ones where either operand has leading ones.
    if (lhs->upper() < 0) {
      unsigned leadingOnes = CountLeadingZeroes32(~lhs->lower());
      lower = std::max(lower, ~int32_t(UINT32_MAX >> leadingOnes));
      upper = -1;
    }
    if (rhs->upper() < 0) {
      unsigned leadingOnes = CountLeadingZeroes32(~rhs->lower());
      lower = std::max(lower, ~int32_t(UINT32_MAX >> leadingOnes));
      upper = -1;
    }
  }

  return Range::NewInt32Range(alloc, lower, upper);
}

Range* Range::xor_(TempAllocator& alloc, const Range* lhs, const Range* rhs) {
  MOZ_ASSERT(lhs->isInt32());
  MOZ_ASSERT(rhs->isInt32());
  int32_t lhsLower = lhs->lower();
  int32_t lhsUpper = lhs->upper();
  int32_t rhsLower = rhs->lower();
  int32_t rhsUpper = rhs->upper();
  bool invertAfter = false;

  // If either operand is negative, bitwise-negate it, and arrange to negate
  // the result; ~((~x)^y) == x^y. If both are negative the negations on the
  // result cancel each other out; effectively this is (~x)^(~y) == x^y.
  // These transformations reduce the number of cases we have to handle below.
  if (lhsUpper < 0) {
    lhsLower = ~lhsLower;
    lhsUpper = ~lhsUpper;
    std::swap(lhsLower, lhsUpper);
    invertAfter = !invertAfter;
  }
  if (rhsUpper < 0) {
    rhsLower = ~rhsLower;
    rhsUpper = ~rhsUpper;
    std::swap(rhsLower, rhsUpper);
    invertAfter = !invertAfter;
  }

  // Handle cases where lhs or rhs is always zero specially, because they're
  // easy cases where we can be perfectly precise, and because it protects the
  // CountLeadingZeroes32 calls below from seeing 0 operands, which would be
  // undefined behavior.
  int32_t lower = INT32_MIN;
  int32_t upper = INT32_MAX;
  if (lhsLower == 0 && lhsUpper == 0) {
    upper = rhsUpper;
    lower = rhsLower;
  } else if (rhsLower == 0 && rhsUpper == 0) {
    upper = lhsUpper;
    lower = lhsLower;
  } else if (lhsLower >= 0 && rhsLower >= 0) {
    // Both operands are non-negative. The result will be non-negative.
    lower = 0;
    // To compute the upper value, take each operand's upper value and
    // set all bits that don't correspond to leading zero bits in the
    // other to one. For each one, this gives an upper bound for the
    // result, so we can take the minimum between the two.
    unsigned lhsLeadingZeros = CountLeadingZeroes32(lhsUpper);
    unsigned rhsLeadingZeros = CountLeadingZeroes32(rhsUpper);
    upper = std::min(rhsUpper | int32_t(UINT32_MAX >> lhsLeadingZeros),
                     lhsUpper | int32_t(UINT32_MAX >> rhsLeadingZeros));
  }

  // If we bitwise-negated one (but not both) of the operands above, apply the
  // bitwise-negate to the result, completing ~((~x)^y) == x^y.
  if (invertAfter) {
    lower = ~lower;
    upper = ~upper;
    std::swap(lower, upper);
  }

  return Range::NewInt32Range(alloc, lower, upper);
}

Range* Range::not_(TempAllocator& alloc, const Range* op) {
  MOZ_ASSERT(op->isInt32());
  return Range::NewInt32Range(alloc, ~op->upper(), ~op->lower());
}

Range* Range::mul(TempAllocator& alloc, const Range* lhs, const Range* rhs) {
  FractionalPartFlag newCanHaveFractionalPart = FractionalPartFlag(
      lhs->canHaveFractionalPart_ || rhs->canHaveFractionalPart_);

  NegativeZeroFlag newMayIncludeNegativeZero = NegativeZeroFlag(
      (lhs->canHaveSignBitSet() && rhs->canBeFiniteNonNegative()) ||
      (rhs->canHaveSignBitSet() && lhs->canBeFiniteNonNegative()));

  uint16_t exponent;
  if (!lhs->canBeInfiniteOrNaN() && !rhs->canBeInfiniteOrNaN()) {
    // Two finite values.
    exponent = lhs->numBits() + rhs->numBits() - 1;
    if (exponent > Range::MaxFiniteExponent) {
      exponent = Range::IncludesInfinity;
    }
  } else if (!lhs->canBeNaN() && !rhs->canBeNaN() &&
             !(lhs->canBeZero() && rhs->canBeInfiniteOrNaN()) &&
             !(rhs->canBeZero() && lhs->canBeInfiniteOrNaN())) {
    // Two values that multiplied together won't produce a NaN.
    exponent = Range::IncludesInfinity;
  } else {
    // Could be anything.
    exponent = Range::IncludesInfinityAndNaN;
  }

  if (MissingAnyInt32Bounds(lhs, rhs)) {
    return new (alloc)
        Range(NoInt32LowerBound, NoInt32UpperBound, newCanHaveFractionalPart,
              newMayIncludeNegativeZero, exponent);
  }
  int64_t a = (int64_t)lhs->lower() * (int64_t)rhs->lower();
  int64_t b = (int64_t)lhs->lower() * (int64_t)rhs->upper();
  int64_t c = (int64_t)lhs->upper() * (int64_t)rhs->lower();
  int64_t d = (int64_t)lhs->upper() * (int64_t)rhs->upper();
  return new (alloc)
      Range(std::min(std::min(a, b), std::min(c, d)),
            std::max(std::max(a, b), std::max(c, d)), newCanHaveFractionalPart,
            newMayIncludeNegativeZero, exponent);
}

Range* Range::lsh(TempAllocator& alloc, const Range* lhs, int32_t c) {
  MOZ_ASSERT(lhs->isInt32());
  int32_t shift = c & 0x1f;

  // If the shift doesn't loose bits or shift bits into the sign bit, we
  // can simply compute the correct range by shifting.
  if ((int32_t)((uint32_t)lhs->lower() << shift << 1 >> shift >> 1) ==
          lhs->lower() &&
      (int32_t)((uint32_t)lhs->upper() << shift << 1 >> shift >> 1) ==
          lhs->upper()) {
    return Range::NewInt32Range(alloc, uint32_t(lhs->lower()) << shift,
                                uint32_t(lhs->upper()) << shift);
  }

  return Range::NewInt32Range(alloc, INT32_MIN, INT32_MAX);
}

Range* Range::rsh(TempAllocator& alloc, const Range* lhs, int32_t c) {
  MOZ_ASSERT(lhs->isInt32());
  int32_t shift = c & 0x1f;
  return Range::NewInt32Range(alloc, lhs->lower() >> shift,
                              lhs->upper() >> shift);
}

Range* Range::ursh(TempAllocator& alloc, const Range* lhs, int32_t c) {
  // ursh's left operand is uint32, not int32, but for range analysis we
  // currently approximate it as int32. We assume here that the range has
  // already been adjusted accordingly by our callers.
  MOZ_ASSERT(lhs->isInt32());

  int32_t shift = c & 0x1f;

  // If the value is always non-negative or always negative, we can simply
  // compute the correct range by shifting.
  if (lhs->isFiniteNonNegative() || lhs->isFiniteNegative()) {
    return Range::NewUInt32Range(alloc, uint32_t(lhs->lower()) >> shift,
                                 uint32_t(lhs->upper()) >> shift);
  }

  // Otherwise return the most general range after the shift.
  return Range::NewUInt32Range(alloc, 0, UINT32_MAX >> shift);
}

Range* Range::lsh(TempAllocator& alloc, const Range* lhs, const Range* rhs) {
  MOZ_ASSERT(lhs->isInt32());
  MOZ_ASSERT(rhs->isInt32());
  return Range::NewInt32Range(alloc, INT32_MIN, INT32_MAX);
}

Range* Range::rsh(TempAllocator& alloc, const Range* lhs, const Range* rhs) {
  MOZ_ASSERT(lhs->isInt32());
  MOZ_ASSERT(rhs->isInt32());

  // Canonicalize the shift range to 0 to 31.
  int32_t shiftLower = rhs->lower();
  int32_t shiftUpper = rhs->upper();
  if ((int64_t(shiftUpper) - int64_t(shiftLower)) >= 31) {
    shiftLower = 0;
    shiftUpper = 31;
  } else {
    shiftLower &= 0x1f;
    shiftUpper &= 0x1f;
    if (shiftLower > shiftUpper) {
      shiftLower = 0;
      shiftUpper = 31;
    }
  }
  MOZ_ASSERT(shiftLower >= 0 && shiftUpper <= 31);

  // The lhs bounds are signed, thus the minimum is either the lower bound
  // shift by the smallest shift if negative or the lower bound shifted by the
  // biggest shift otherwise.  And the opposite for the maximum.
  int32_t lhsLower = lhs->lower();
  int32_t min = lhsLower < 0 ? lhsLower >> shiftLower : lhsLower >> shiftUpper;
  int32_t lhsUpper = lhs->upper();
  int32_t max = lhsUpper >= 0 ? lhsUpper >> shiftLower : lhsUpper >> shiftUpper;

  return Range::NewInt32Range(alloc, min, max);
}

Range* Range::ursh(TempAllocator& alloc, const Range* lhs, const Range* rhs) {
  // ursh's left operand is uint32, not int32, but for range analysis we
  // currently approximate it as int32. We assume here that the range has
  // already been adjusted accordingly by our callers.
  MOZ_ASSERT(lhs->isInt32());
  MOZ_ASSERT(rhs->isInt32());
  return Range::NewUInt32Range(
      alloc, 0, lhs->isFiniteNonNegative() ? lhs->upper() : UINT32_MAX);
}

Range* Range::abs(TempAllocator& alloc, const Range* op) {
  int32_t l = op->lower_;
  int32_t u = op->upper_;
  FractionalPartFlag canHaveFractionalPart = op->canHaveFractionalPart_;

  // Abs never produces a negative zero.
  NegativeZeroFlag canBeNegativeZero = ExcludesNegativeZero;

  return new (alloc) Range(
      std::max(std::max(int32_t(0), l), u == INT32_MIN ? INT32_MAX : -u), true,
      std::max(std::max(int32_t(0), u), l == INT32_MIN ? INT32_MAX : -l),
      op->hasInt32Bounds() && l != INT32_MIN, canHaveFractionalPart,
      canBeNegativeZero, op->max_exponent_);
}

Range* Range::min(TempAllocator& alloc, const Range* lhs, const Range* rhs) {
  // If either operand is NaN, the result is NaN.
  if (lhs->canBeNaN() || rhs->canBeNaN()) {
    return nullptr;
  }

  FractionalPartFlag newCanHaveFractionalPart = FractionalPartFlag(
      lhs->canHaveFractionalPart_ || rhs->canHaveFractionalPart_);
  NegativeZeroFlag newMayIncludeNegativeZero =
      NegativeZeroFlag(lhs->canBeNegativeZero_ || rhs->canBeNegativeZero_);

  return new (alloc) Range(std::min(lhs->lower_, rhs->lower_),
                           lhs->hasInt32LowerBound_ && rhs->hasInt32LowerBound_,
                           std::min(lhs->upper_, rhs->upper_),
                           lhs->hasInt32UpperBound_ || rhs->hasInt32UpperBound_,
                           newCanHaveFractionalPart, newMayIncludeNegativeZero,
                           std::max(lhs->max_exponent_, rhs->max_exponent_));
}

Range* Range::max(TempAllocator& alloc, const Range* lhs, const Range* rhs) {
  // If either operand is NaN, the result is NaN.
  if (lhs->canBeNaN() || rhs->canBeNaN()) {
    return nullptr;
  }

  FractionalPartFlag newCanHaveFractionalPart = FractionalPartFlag(
      lhs->canHaveFractionalPart_ || rhs->canHaveFractionalPart_);
  NegativeZeroFlag newMayIncludeNegativeZero =
      NegativeZeroFlag(lhs->canBeNegativeZero_ || rhs->canBeNegativeZero_);

  return new (alloc) Range(std::max(lhs->lower_, rhs->lower_),
                           lhs->hasInt32LowerBound_ || rhs->hasInt32LowerBound_,
                           std::max(lhs->upper_, rhs->upper_),
                           lhs->hasInt32UpperBound_ && rhs->hasInt32UpperBound_,
                           newCanHaveFractionalPart, newMayIncludeNegativeZero,
                           std::max(lhs->max_exponent_, rhs->max_exponent_));
}

Range* Range::floor(TempAllocator& alloc, const Range* op) {
  Range* copy = new (alloc) Range(*op);
  // Decrement lower bound of copy range if op have a factional part and lower
  // bound is Int32 defined. Also we avoid to decrement when op have a
  // fractional part but lower_ >= JSVAL_INT_MAX.
  if (op->canHaveFractionalPart() && op->hasInt32LowerBound()) {
    copy->setLowerInit(int64_t(copy->lower_) - 1);
  }

  // Also refine max_exponent_ because floor may have decremented int value
  // If we've got int32 defined bounds, just deduce it using defined bounds.
  // But, if we don't have those, value's max_exponent_ may have changed.
  // Because we're looking to maintain an over estimation, if we can,
  // we increment it.
  if (copy->hasInt32Bounds())
    copy->max_exponent_ = copy->exponentImpliedByInt32Bounds();
  else if (copy->max_exponent_ < MaxFiniteExponent)
    copy->max_exponent_++;

  copy->canHaveFractionalPart_ = ExcludesFractionalParts;
  copy->assertInvariants();
  return copy;
}

Range* Range::ceil(TempAllocator& alloc, const Range* op) {
  Range* copy = new (alloc) Range(*op);

  // We need to refine max_exponent_ because ceil may have incremented the int
  // value. If we have got int32 bounds defined, just deduce it using the
  // defined bounds. Else we can just increment its value, as we are looking to
  // maintain an over estimation.
  if (copy->hasInt32Bounds()) {
    copy->max_exponent_ = copy->exponentImpliedByInt32Bounds();
  } else if (copy->max_exponent_ < MaxFiniteExponent) {
    copy->max_exponent_++;
  }

  // If the range is definitely above 0 or below -1, we don't need to include
  // -0; otherwise we do.

  copy->canBeNegativeZero_ = ((copy->lower_ > 0) || (copy->upper_ <= -1))
                                 ? copy->canBeNegativeZero_
                                 : IncludesNegativeZero;

  copy->canHaveFractionalPart_ = ExcludesFractionalParts;
  copy->assertInvariants();
  return copy;
}

Range* Range::sign(TempAllocator& alloc, const Range* op) {
  if (op->canBeNaN()) {
    return nullptr;
  }

  return new (alloc)
      Range(std::clamp(op->lower_, -1, 1), std::clamp(op->upper_, -1, 1),
            Range::ExcludesFractionalParts,
            NegativeZeroFlag(op->canBeNegativeZero()), 0);
}

Range* Range::NaNToZero(TempAllocator& alloc, const Range* op) {
  Range* copy = new (alloc) Range(*op);
  if (copy->canBeNaN()) {
    copy->max_exponent_ = Range::IncludesInfinity;
    if (!copy->canBeZero()) {
      Range zero;
      zero.setDoubleSingleton(0);
      copy->unionWith(&zero);
    }
  }
  copy->refineToExcludeNegativeZero();
  return copy;
}

bool Range::negativeZeroMul(const Range* lhs, const Range* rhs) {
  // The result can only be negative zero if both sides are finite and they
  // have differing signs.
  return (lhs->canHaveSignBitSet() && rhs->canBeFiniteNonNegative()) ||
         (rhs->canHaveSignBitSet() && lhs->canBeFiniteNonNegative());
}

bool Range::update(const Range* other) {
  bool changed = lower_ != other->lower_ ||
                 hasInt32LowerBound_ != other->hasInt32LowerBound_ ||
                 upper_ != other->upper_ ||
                 hasInt32UpperBound_ != other->hasInt32UpperBound_ ||
                 canHaveFractionalPart_ != other->canHaveFractionalPart_ ||
                 canBeNegativeZero_ != other->canBeNegativeZero_ ||
                 max_exponent_ != other->max_exponent_;
  if (changed) {
    lower_ = other->lower_;
    hasInt32LowerBound_ = other->hasInt32LowerBound_;
    upper_ = other->upper_;
    hasInt32UpperBound_ = other->hasInt32UpperBound_;
    canHaveFractionalPart_ = other->canHaveFractionalPart_;
    canBeNegativeZero_ = other->canBeNegativeZero_;
    max_exponent_ = other->max_exponent_;
    assertInvariants();
  }

  return changed;
}

///////////////////////////////////////////////////////////////////////////////
// Range Computation for MIR Nodes
///////////////////////////////////////////////////////////////////////////////

void MPhi::computeRange(TempAllocator& alloc) {
  if (type() != MIRType::Int32 && type() != MIRType::Double) {
    return;
  }

  Range* range = nullptr;
  for (size_t i = 0, e = numOperands(); i < e; i++) {
    if (getOperand(i)->block()->unreachable()) {
      JitSpew(JitSpew_Range, "Ignoring unreachable input %u",
              getOperand(i)->id());
      continue;
    }

    // Peek at the pre-bailout range so we can take a short-cut; if any of
    // the operands has an unknown range, this phi has an unknown range.
    if (!getOperand(i)->range()) {
      return;
    }

    Range input(getOperand(i));

    if (range) {
      range->unionWith(&input);
    } else {
      range = new (alloc) Range(input);
    }
  }

  setRange(range);
}

void MBeta::computeRange(TempAllocator& alloc) {
  bool emptyRange = false;

  Range opRange(getOperand(0));
  Range* range = Range::intersect(alloc, &opRange, comparison_, &emptyRange);
  if (emptyRange) {
    JitSpew(JitSpew_Range, "Marking block for inst %u unreachable", id());
    block()->setUnreachableUnchecked();
  } else {
    setRange(range);
  }
}

void MConstant::computeRange(TempAllocator& alloc) {
  if (isTypeRepresentableAsDouble()) {
    double d = numberToDouble();
    setRange(Range::NewDoubleSingletonRange(alloc, d));
  } else if (type() == MIRType::Boolean) {
    bool b = toBoolean();
    setRange(Range::NewInt32Range(alloc, b, b));
  }
}

void MCharCodeAt::computeRange(TempAllocator& alloc) {
  // ECMA 262 says that the integer will be non-negative and at most 65535.
  setRange(Range::NewInt32Range(alloc, 0, unicode::UTF16Max));
}

void MCodePointAt::computeRange(TempAllocator& alloc) {
  setRange(Range::NewInt32Range(alloc, 0, unicode::NonBMPMax));
}

void MClampToUint8::computeRange(TempAllocator& alloc) {
  setRange(Range::NewUInt32Range(alloc, 0, 255));
}

void MBitAnd::computeRange(TempAllocator& alloc) {
  if (type() != MIRType::Int32) {
    return;
  }

  Range left(getOperand(0));
  Range right(getOperand(1));
  left.wrapAroundToInt32();
  right.wrapAroundToInt32();

  setRange(Range::and_(alloc, &left, &right));
}

void MBitOr::computeRange(TempAllocator& alloc) {
  if (type() != MIRType::Int32) {
    return;
  }

  Range left(getOperand(0));
  Range right(getOperand(1));
  left.wrapAroundToInt32();
  right.wrapAroundToInt32();

  setRange(Range::or_(alloc, &left, &right));
}

void MBitXor::computeRange(TempAllocator& alloc) {
  if (type() != MIRType::Int32) {
    return;
  }

  Range left(getOperand(0));
  Range right(getOperand(1));
  left.wrapAroundToInt32();
  right.wrapAroundToInt32();

  setRange(Range::xor_(alloc, &left, &right));
}

void MBitNot::computeRange(TempAllocator& alloc) {
  if (type() == MIRType::Int64) {
    return;
  }
  MOZ_ASSERT(type() == MIRType::Int32);

  Range op(getOperand(0));
  op.wrapAroundToInt32();

  setRange(Range::not_(alloc, &op));
}

void MLsh::computeRange(TempAllocator& alloc) {
  if (type() != MIRType::Int32) {
    return;
  }

  Range left(getOperand(0));
  Range right(getOperand(1));
  left.wrapAroundToInt32();

  MConstant* rhsConst = getOperand(1)->maybeConstantValue();
  if (rhsConst && rhsConst->type() == MIRType::Int32) {
    int32_t c = rhsConst->toInt32();
    setRange(Range::lsh(alloc, &left, c));
    return;
  }

  right.wrapAroundToShiftCount();
  setRange(Range::lsh(alloc, &left, &right));
}

void MRsh::computeRange(TempAllocator& alloc) {
  if (type() != MIRType::Int32) {
    return;
  }

  Range left(getOperand(0));
  Range right(getOperand(1));
  left.wrapAroundToInt32();

  MConstant* rhsConst = getOperand(1)->maybeConstantValue();
  if (rhsConst && rhsConst->type() == MIRType::Int32) {
    int32_t c = rhsConst->toInt32();
    setRange(Range::rsh(alloc, &left, c));
    return;
  }

  right.wrapAroundToShiftCount();
  setRange(Range::rsh(alloc, &left, &right));
}

void MUrsh::computeRange(TempAllocator& alloc) {
  if (type() != MIRType::Int32) {
    return;
  }

  Range left(getOperand(0));
  Range right(getOperand(1));

  // ursh can be thought of as converting its left operand to uint32, or it
  // can be thought of as converting its left operand to int32, and then
  // reinterpreting the int32 bits as a uint32 value. Both approaches yield
  // the same result. Since we lack support for full uint32 ranges, we use
  // the second interpretation, though it does cause us to be conservative.
  left.wrapAroundToInt32();
  right.wrapAroundToShiftCount();

  MConstant* rhsConst = getOperand(1)->maybeConstantValue();
  if (rhsConst && rhsConst->type() == MIRType::Int32) {
    int32_t c = rhsConst->toInt32();
    setRange(Range::ursh(alloc, &left, c));
  } else {
    setRange(Range::ursh(alloc, &left, &right));
  }

  MOZ_ASSERT(range()->lower() >= 0);
}

void MAbs::computeRange(TempAllocator& alloc) {
  if (type() != MIRType::Int32 && type() != MIRType::Double) {
    return;
  }

  Range other(getOperand(0));
  Range* next = Range::abs(alloc, &other);
  if (implicitTruncate_) {
    next->wrapAroundToInt32();
  }
  setRange(next);
}

void MFloor::computeRange(TempAllocator& alloc) {
  Range other(getOperand(0));
  setRange(Range::floor(alloc, &other));
}

void MCeil::computeRange(TempAllocator& alloc) {
  Range other(getOperand(0));
  setRange(Range::ceil(alloc, &other));
}

void MClz::computeRange(TempAllocator& alloc) {
  if (type() != MIRType::Int32) {
    return;
  }
  setRange(Range::NewUInt32Range(alloc, 0, 32));
}

void MCtz::computeRange(TempAllocator& alloc) {
  if (type() != MIRType::Int32) {
    return;
  }
  setRange(Range::NewUInt32Range(alloc, 0, 32));
}

void MPopcnt::computeRange(TempAllocator& alloc) {
  if (type() != MIRType::Int32) {
    return;
  }
  setRange(Range::NewUInt32Range(alloc, 0, 32));
}

void MMinMax::computeRange(TempAllocator& alloc) {
  if (type() != MIRType::Int32 && type() != MIRType::Double) {
    return;
  }

  Range left(getOperand(0));
  Range right(getOperand(1));
  setRange(isMax() ? Range::max(alloc, &left, &right)
                   : Range::min(alloc, &left, &right));
}

void MAdd::computeRange(TempAllocator& alloc) {
  if (type() != MIRType::Int32 && type() != MIRType::Double) {
    return;
  }
  Range left(getOperand(0));
  Range right(getOperand(1));
  Range* next = Range::add(alloc, &left, &right);
  if (isTruncated()) {
    next->wrapAroundToInt32();
  }
  setRange(next);
}

void MSub::computeRange(TempAllocator& alloc) {
  if (type() != MIRType::Int32 && type() != MIRType::Double) {
    return;
  }
  Range left(getOperand(0));
  Range right(getOperand(1));
  Range* next = Range::sub(alloc, &left, &right);
  if (isTruncated()) {
    next->wrapAroundToInt32();
  }
  setRange(next);
}

void MMul::computeRange(TempAllocator& alloc) {
  if (type() != MIRType::Int32 && type() != MIRType::Double) {
    return;
  }
  Range left(getOperand(0));
  Range right(getOperand(1));
  if (canBeNegativeZero()) {
    canBeNegativeZero_ = Range::negativeZeroMul(&left, &right);
  }
  Range* next = Range::mul(alloc, &left, &right);
  if (!next->canBeNegativeZero()) {
    canBeNegativeZero_ = false;
  }
  // Truncated multiplications could overflow in both directions
  if (isTruncated()) {
    next->wrapAroundToInt32();
  }
  setRange(next);
}

void MMod::computeRange(TempAllocator& alloc) {
  if (type() != MIRType::Int32 && type() != MIRType::Double) {
    return;
  }
  Range lhs(getOperand(0));
  Range rhs(getOperand(1));

  // If either operand is a NaN, the result is NaN. This also conservatively
  // handles Infinity cases.
  if (!lhs.hasInt32Bounds() || !rhs.hasInt32Bounds()) {
    return;
  }

  // If RHS can be zero, the result can be NaN.
  if (rhs.lower() <= 0 && rhs.upper() >= 0) {
    return;
  }

  // If both operands are non-negative integers, we can optimize this to an
  // unsigned mod.
  if (type() == MIRType::Int32 && rhs.lower() > 0) {
    bool hasDoubles = lhs.lower() < 0 || lhs.canHaveFractionalPart() ||
                      rhs.canHaveFractionalPart();
    // It is not possible to check that lhs.lower() >= 0, since the range
    // of a ursh with rhs a 0 constant is wrapped around the int32 range in
    // Range::Range(). However, IsUint32Type() will only return true for
    // nodes that lie in the range [0, UINT32_MAX].
    bool hasUint32s =
        IsUint32Type(getOperand(0)) &&
        getOperand(1)->type() == MIRType::Int32 &&
        (IsUint32Type(getOperand(1)) || getOperand(1)->isConstant());
    if (!hasDoubles || hasUint32s) {
      unsigned_ = true;
    }
  }

  // For unsigned mod, we have to convert both operands to unsigned.
  // Note that we handled the case of a zero rhs above.
  if (unsigned_) {
    // The result of an unsigned mod will never be unsigned-greater than
    // either operand.
    uint32_t lhsBound = std::max<uint32_t>(lhs.lower(), lhs.upper());
    uint32_t rhsBound = std::max<uint32_t>(rhs.lower(), rhs.upper());

    // If either range crosses through -1 as a signed value, it could be
    // the maximum unsigned value when interpreted as unsigned. If the range
    // doesn't include -1, then the simple max value we computed above is
    // correct.
    if (lhs.lower() <= -1 && lhs.upper() >= -1) {
      lhsBound = UINT32_MAX;
    }
    if (rhs.lower() <= -1 && rhs.upper() >= -1) {
      rhsBound = UINT32_MAX;
    }

    // The result will never be equal to the rhs, and we shouldn't have
    // any rounding to worry about.
    MOZ_ASSERT(!lhs.canHaveFractionalPart() && !rhs.canHaveFractionalPart());
    --rhsBound;

    // This gives us two upper bounds, so we can take the best one.
    setRange(Range::NewUInt32Range(alloc, 0, std::min(lhsBound, rhsBound)));
    return;
  }

  // Math.abs(lhs % rhs) == Math.abs(lhs) % Math.abs(rhs).
  // First, the absolute value of the result will always be less than the
  // absolute value of rhs. (And if rhs is zero, the result is NaN).
  int64_t a = Abs<int64_t>(rhs.lower());
  int64_t b = Abs<int64_t>(rhs.upper());
  if (a == 0 && b == 0) {
    return;
  }
  int64_t rhsAbsBound = std::max(a, b);

  // If the value is known to be integer, less-than abs(rhs) is equivalent
  // to less-than-or-equal abs(rhs)-1. This is important for being able to
  // say that the result of x%256 is an 8-bit unsigned number.
  if (!lhs.canHaveFractionalPart() && !rhs.canHaveFractionalPart()) {
    --rhsAbsBound;
  }

  // Next, the absolute value of the result will never be greater than the
  // absolute value of lhs.
  int64_t lhsAbsBound =
      std::max(Abs<int64_t>(lhs.lower()), Abs<int64_t>(lhs.upper()));

  // This gives us two upper bounds, so we can take the best one.
  int64_t absBound = std::min(lhsAbsBound, rhsAbsBound);

  // Now consider the sign of the result.
  // If lhs is non-negative, the result will be non-negative.
  // If lhs is non-positive, the result will be non-positive.
  int64_t lower = lhs.lower() >= 0 ? 0 : -absBound;
  int64_t upper = lhs.upper() <= 0 ? 0 : absBound;

  Range::FractionalPartFlag newCanHaveFractionalPart =
      Range::FractionalPartFlag(lhs.canHaveFractionalPart() ||
                                rhs.canHaveFractionalPart());

  // If the lhs can have the sign bit set and we can return a zero, it'll be a
  // negative zero.
  Range::NegativeZeroFlag newMayIncludeNegativeZero =
      Range::NegativeZeroFlag(lhs.canHaveSignBitSet());

  setRange(new (alloc) Range(lower, upper, newCanHaveFractionalPart,
                             newMayIncludeNegativeZero,
                             std::min(lhs.exponent(), rhs.exponent())));
}

void MDiv::computeRange(TempAllocator& alloc) {
  if (type() != MIRType::Int32 && type() != MIRType::Double) {
    return;
  }
  Range lhs(getOperand(0));
  Range rhs(getOperand(1));

  // If either operand is a NaN, the result is NaN. This also conservatively
  // handles Infinity cases.
  if (!lhs.hasInt32Bounds() || !rhs.hasInt32Bounds()) {
    return;
  }

  // Something simple for now: When dividing by a positive rhs, the result
  // won't be further from zero than lhs.
  if (lhs.lower() >= 0 && rhs.lower() >= 1) {
    setRange(new (alloc) Range(0, lhs.upper(), Range::IncludesFractionalParts,
                               Range::IncludesNegativeZero, lhs.exponent()));
  } else if (unsigned_ && rhs.lower() >= 1) {
    // We shouldn't set the unsigned flag if the inputs can have
    // fractional parts.
    MOZ_ASSERT(!lhs.canHaveFractionalPart() && !rhs.canHaveFractionalPart());
    // We shouldn't set the unsigned flag if the inputs can be
    // negative zero.
    MOZ_ASSERT(!lhs.canBeNegativeZero() && !rhs.canBeNegativeZero());
    // Unsigned division by a non-zero rhs will return a uint32 value.
    setRange(Range::NewUInt32Range(alloc, 0, UINT32_MAX));
  }
}

void MSqrt::computeRange(TempAllocator& alloc) {
  Range input(getOperand(0));

  // If either operand is a NaN, the result is NaN. This also conservatively
  // handles Infinity cases.
  if (!input.hasInt32Bounds()) {
    return;
  }

  // Sqrt of a negative non-zero value is NaN.
  if (input.lower() < 0) {
    return;
  }

  // Something simple for now: When taking the sqrt of a positive value, the
  // result won't be further from zero than the input.
  // And, sqrt of an integer may have a fractional part.
  setRange(new (alloc) Range(0, input.upper(), Range::IncludesFractionalParts,
                             input.canBeNegativeZero(), input.exponent()));
}

void MToDouble::computeRange(TempAllocator& alloc) {
  setRange(new (alloc) Range(getOperand(0)));
}

void MToFloat32::computeRange(TempAllocator& alloc) {}

void MTruncateToInt32::computeRange(TempAllocator& alloc) {
  Range* output = new (alloc) Range(getOperand(0));
  output->wrapAroundToInt32();
  setRange(output);
}

void MToNumberInt32::computeRange(TempAllocator& alloc) {
  // No clamping since this computes the range *before* bailouts.
  setRange(new (alloc) Range(getOperand(0)));
}

void MBooleanToInt32::computeRange(TempAllocator& alloc) {
  setRange(Range::NewUInt32Range(alloc, 0, 1));
}

void MLimitedTruncate::computeRange(TempAllocator& alloc) {
  Range* output = new (alloc) Range(input());
  setRange(output);
}

static Range* GetArrayBufferViewRange(TempAllocator& alloc, Scalar::Type type) {
  switch (type) {
    case Scalar::Uint8Clamped:
    case Scalar::Uint8:
      return Range::NewUInt32Range(alloc, 0, UINT8_MAX);
    case Scalar::Uint16:
      return Range::NewUInt32Range(alloc, 0, UINT16_MAX);
    case Scalar::Uint32:
      return Range::NewUInt32Range(alloc, 0, UINT32_MAX);

    case Scalar::Int8:
      return Range::NewInt32Range(alloc, INT8_MIN, INT8_MAX);
    case Scalar::Int16:
      return Range::NewInt32Range(alloc, INT16_MIN, INT16_MAX);
    case Scalar::Int32:
      return Range::NewInt32Range(alloc, INT32_MIN, INT32_MAX);

    case Scalar::BigInt64:
    case Scalar::BigUint64:
    case Scalar::Int64:
    case Scalar::Simd128:
    case Scalar::Float16:
    case Scalar::Float32:
    case Scalar::Float64:
    case Scalar::MaxTypedArrayViewType:
      break;
  }
  return nullptr;
}

void MLoadUnboxedScalar::computeRange(TempAllocator& alloc) {
  // We have an Int32 type and if this is a UInt32 load it may produce a value
  // outside of our range, but we have a bailout to handle those cases.
  setRange(GetArrayBufferViewRange(alloc, storageType()));
}

void MLoadDataViewElement::computeRange(TempAllocator& alloc) {
  // We have an Int32 type and if this is a UInt32 load it may produce a value
  // outside of our range, but we have a bailout to handle those cases.
  setRange(GetArrayBufferViewRange(alloc, storageType()));
}

void MArrayLength::computeRange(TempAllocator& alloc) {
  // Array lengths can go up to UINT32_MAX. We will bail out if the array
  // length > INT32_MAX.
  MOZ_ASSERT(type() == MIRType::Int32);
  setRange(Range::NewUInt32Range(alloc, 0, INT32_MAX));
}

void MInitializedLength::computeRange(TempAllocator& alloc) {
  setRange(
      Range::NewUInt32Range(alloc, 0, NativeObject::MAX_DENSE_ELEMENTS_COUNT));
}

void MArrayBufferViewLength::computeRange(TempAllocator& alloc) {
  if constexpr (ArrayBufferObject::ByteLengthLimit <= INT32_MAX) {
    setRange(Range::NewUInt32Range(alloc, 0, INT32_MAX));
  }
}

void MArrayBufferViewByteOffset::computeRange(TempAllocator& alloc) {
  if constexpr (ArrayBufferObject::ByteLengthLimit <= INT32_MAX) {
    setRange(Range::NewUInt32Range(alloc, 0, INT32_MAX));
  }
}

void MResizableTypedArrayByteOffsetMaybeOutOfBounds::computeRange(
    TempAllocator& alloc) {
  if constexpr (ArrayBufferObject::ByteLengthLimit <= INT32_MAX) {
    setRange(Range::NewUInt32Range(alloc, 0, INT32_MAX));
  }
}

void MResizableTypedArrayLength::computeRange(TempAllocator& alloc) {
  if constexpr (ArrayBufferObject::ByteLengthLimit <= INT32_MAX) {
    setRange(Range::NewUInt32Range(alloc, 0, INT32_MAX));
  }
}

void MResizableDataViewByteLength::computeRange(TempAllocator& alloc) {
  if constexpr (ArrayBufferObject::ByteLengthLimit <= INT32_MAX) {
    setRange(Range::NewUInt32Range(alloc, 0, INT32_MAX));
  }
}

void MTypedArrayElementSize::computeRange(TempAllocator& alloc) {
  constexpr auto MaxTypedArraySize = sizeof(double);

#define ASSERT_MAX_SIZE(_, T, N)                \
  static_assert(sizeof(T) <= MaxTypedArraySize, \
                "unexpected typed array type exceeding 64-bits storage");
  JS_FOR_EACH_TYPED_ARRAY(ASSERT_MAX_SIZE)
#undef ASSERT_MAX_SIZE

  setRange(Range::NewUInt32Range(alloc, 0, MaxTypedArraySize));
}

void MStringLength::computeRange(TempAllocator& alloc) {
  static_assert(JSString::MAX_LENGTH <= UINT32_MAX,
                "NewUInt32Range requires a uint32 value");
  setRange(Range::NewUInt32Range(alloc, 0, JSString::MAX_LENGTH));
}

void MArgumentsLength::computeRange(TempAllocator& alloc) {
  // This is is a conservative upper bound on what |TooManyActualArguments|
  // checks.  If exceeded, Ion will not be entered in the first place.
  static_assert(ARGS_LENGTH_MAX <= UINT32_MAX,
                "NewUInt32Range requires a uint32 value");
  setRange(Range::NewUInt32Range(alloc, 0, ARGS_LENGTH_MAX));
}

void MBoundsCheck::computeRange(TempAllocator& alloc) {
  // Just transfer the incoming index range to the output. The length() is
  // also interesting, but it is handled as a bailout check, and we're
  // computing a pre-bailout range here.
  setRange(new (alloc) Range(index()));
}

void MSpectreMaskIndex::computeRange(TempAllocator& alloc) {
  // Just transfer the incoming index range to the output for now.
  setRange(new (alloc) Range(index()));
}

void MInt32ToIntPtr::computeRange(TempAllocator& alloc) {
  setRange(new (alloc) Range(input()));
}

void MNonNegativeIntPtrToInt32::computeRange(TempAllocator& alloc) {
  // We will bail out if the IntPtr value > INT32_MAX.
  setRange(Range::NewUInt32Range(alloc, 0, INT32_MAX));
}

void MArrayPush::computeRange(TempAllocator& alloc) {
  // MArrayPush returns the new array length. It bails out if the new length
  // doesn't fit in an Int32.
  MOZ_ASSERT(type() == MIRType::Int32);
  setRange(Range::NewUInt32Range(alloc, 0, INT32_MAX));
}

void MMathFunction::computeRange(TempAllocator& alloc) {
  Range opRange(getOperand(0));
  switch (function()) {
    case UnaryMathFunction::SinNative:
    case UnaryMathFunction::SinFdlibm:
    case UnaryMathFunction::CosNative:
    case UnaryMathFunction::CosFdlibm:
      if (!opRange.canBeInfiniteOrNaN()) {
        setRange(Range::NewDoubleRange(alloc, -1.0, 1.0));
      }
      break;
    default:
      break;
  }
}

void MSign::computeRange(TempAllocator& alloc) {
  Range opRange(getOperand(0));
  setRange(Range::sign(alloc, &opRange));
}

void MRandom::computeRange(TempAllocator& alloc) {
  Range* r = Range::NewDoubleRange(alloc, 0.0, 1.0);

  // Random never returns negative zero.
  r->refineToExcludeNegativeZero();

  setRange(r);
}

void MNaNToZero::computeRange(TempAllocator& alloc) {
  Range other(input());
  setRange(Range::NaNToZero(alloc, &other));
}

///////////////////////////////////////////////////////////////////////////////
// Range Analysis
///////////////////////////////////////////////////////////////////////////////

static BranchDirection NegateBranchDirection(BranchDirection dir) {
  return (dir == FALSE_BRANCH) ? TRUE_BRANCH : FALSE_BRANCH;
}

bool RangeAnalysis::analyzeLoop(const MBasicBlock* header) {
  MOZ_ASSERT(header->hasUniqueBackedge());

  // Try to compute an upper bound on the number of times the loop backedge
  // will be taken. Look for tests that dominate the backedge and which have
  // an edge leaving the loop body.
  MBasicBlock* backedge = header->backedge();

  // Ignore trivial infinite loops.
  if (backedge == header) {
    return true;
  }

  bool canOsr;
  size_t numBlocks = MarkLoopBlocks(graph_, header, &canOsr);

  // Ignore broken loops.
  if (numBlocks == 0) {
    return true;
  }

  LoopIterationBound* iterationBound = nullptr;

  MBasicBlock* block = backedge;
  do {
    BranchDirection direction;
    MTest* branch = block->immediateDominatorBranch(&direction);

    if (block == block->immediateDominator()) {
      break;
    }

    block = block->immediateDominator();

    if (branch) {
      direction = NegateBranchDirection(direction);
      MBasicBlock* otherBlock = branch->branchSuccessor(direction);
      if (!otherBlock->isMarked()) {
        if (!alloc().ensureBallast()) {
          return false;
        }
        iterationBound = analyzeLoopIterationCount(header, branch, direction);
        if (iterationBound) {
          break;
        }
      }
    }
  } while (block != header);

  if (!iterationBound) {
    UnmarkLoopBlocks(graph_, header);
    return true;
  }

  if (!loopIterationBounds.append(iterationBound)) {
    return false;
  }

#ifdef DEBUG
  if (JitSpewEnabled(JitSpew_Range)) {
    Sprinter sp(GetJitContext()->cx);
    if (!sp.init()) {
      return false;
    }
    iterationBound->boundSum.dump(sp);
    JS::UniqueChars str = sp.release();
    if (!str) {
      return false;
    }
    JitSpew(JitSpew_Range, "computed symbolic bound on backedges: %s",
            str.get());
  }
#endif

  // Try to compute symbolic bounds for the phi nodes at the head of this
  // loop, expressed in terms of the iteration bound just computed.

  for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd();
       iter++) {
    analyzeLoopPhi(iterationBound, *iter);
  }

  if (!mir->compilingWasm() && !mir->outerInfo().hadBoundsCheckBailout()) {
    // Try to hoist any bounds checks from the loop using symbolic bounds.

    Vector<MBoundsCheck*, 0, JitAllocPolicy> hoistedChecks(alloc());

    for (ReversePostorderIterator iter(graph_.rpoBegin(header));
         iter != graph_.rpoEnd(); iter++) {
      MBasicBlock* block = *iter;
      if (!block->isMarked()) {
        continue;
      }

      for (MDefinitionIterator iter(block); iter; iter++) {
        MDefinition* def = *iter;
        if (def->isBoundsCheck() && def->isMovable()) {
          if (!alloc().ensureBallast()) {
            return false;
          }
          if (tryHoistBoundsCheck(header, def->toBoundsCheck())) {
            if (!hoistedChecks.append(def->toBoundsCheck())) {
              return false;
            }
          }
        }
      }
    }

    // Note: replace all uses of the original bounds check with the
    // actual index. This is usually done during bounds check elimination,
    // but in this case it's safe to do it here since the load/store is
    // definitely not loop-invariant, so we will never move it before
    // one of the bounds checks we just added.
    for (size_t i = 0; i < hoistedChecks.length(); i++) {
      MBoundsCheck* ins = hoistedChecks[i];
      ins->replaceAllUsesWith(ins->index());
      ins->block()->discard(ins);
    }
  }

  UnmarkLoopBlocks(graph_, header);
  return true;
}

// Unbox beta nodes in order to hoist instruction properly, and not be limited
// by the beta nodes which are added after each branch.
static inline MDefinition* DefinitionOrBetaInputDefinition(MDefinition* ins) {
  while (ins->isBeta()) {
    ins = ins->toBeta()->input();
  }
  return ins;
}

LoopIterationBound* RangeAnalysis::analyzeLoopIterationCount(
    const MBasicBlock* header, const MTest* test, BranchDirection direction) {
  SimpleLinearSum lhs(nullptr, 0);
  MDefinition* rhs;
  bool lessEqual;
  if (!ExtractLinearInequality(test, direction, &lhs, &rhs, &lessEqual)) {
    return nullptr;
  }

  // Ensure the rhs is a loop invariant term.
  if (rhs && rhs->block()->isMarked()) {
    if (lhs.term && lhs.term->block()->isMarked()) {
      return nullptr;
    }
    MDefinition* temp = lhs.term;
    lhs.term = rhs;
    rhs = temp;
    if (!SafeSub(0, lhs.constant, &lhs.constant)) {
      return nullptr;
    }
    lessEqual = !lessEqual;
  }

  MOZ_ASSERT_IF(rhs, !rhs->block()->isMarked());

  // Ensure the lhs is a phi node from the start of the loop body.
  if (!lhs.term || !lhs.term->isPhi() || lhs.term->block() != header) {
    return nullptr;
  }

  // Check that the value of the lhs changes by a constant amount with each
  // loop iteration. This requires that the lhs be written in every loop
  // iteration with a value that is a constant difference from its value at
  // the start of the iteration.

  if (lhs.term->toPhi()->numOperands() != 2) {
    return nullptr;
  }

  // The first operand of the phi should be the lhs' value at the start of
  // the first executed iteration, and not a value written which could
  // replace the second operand below during the middle of execution.
  MDefinition* lhsInitial = lhs.term->toPhi()->getLoopPredecessorOperand();
  if (lhsInitial->block()->isMarked()) {
    return nullptr;
  }

  // The second operand of the phi should be a value written by an add/sub
  // in every loop iteration, i.e. in a block which dominates the backedge.
  MDefinition* lhsWrite = DefinitionOrBetaInputDefinition(
      lhs.term->toPhi()->getLoopBackedgeOperand());
  if (!lhsWrite->isAdd() && !lhsWrite->isSub()) {
    return nullptr;
  }
  if (!lhsWrite->block()->isMarked()) {
    return nullptr;
  }
  MBasicBlock* bb = header->backedge();
  for (; bb != lhsWrite->block() && bb != header;
       bb = bb->immediateDominator()) {
  }
  if (bb != lhsWrite->block()) {
    return nullptr;
  }

  SimpleLinearSum lhsModified = ExtractLinearSum(lhsWrite);

  // Check that the value of the lhs at the backedge is of the form
  // 'old(lhs) + N'. We can be sure that old(lhs) is the value at the start
  // of the iteration, and not that written to lhs in a previous iteration,
  // as such a previous value could not appear directly in the addition:
  // it could not be stored in lhs as the lhs add/sub executes in every
  // iteration, and if it were stored in another variable its use here would
  // be as an operand to a phi node for that variable.
  if (lhsModified.term != lhs.term) {
    return nullptr;
  }

  LinearSum iterationBound(alloc());

  if (lhsModified.constant == 1 && !lessEqual) {
    // The value of lhs is 'initial(lhs) + iterCount' and this will end
    // execution of the loop if 'lhs + lhsN >= rhs'. Thus, an upper bound
    // on the number of backedges executed is:
    //
    // initial(lhs) + iterCount + lhsN == rhs
    // iterCount == rhsN - initial(lhs) - lhsN

    if (rhs) {
      if (!iterationBound.add(rhs, 1)) {
        return nullptr;
      }
    }
    if (!iterationBound.add(lhsInitial, -1)) {
      return nullptr;
    }

    int32_t lhsConstant;
    if (!SafeSub(0, lhs.constant, &lhsConstant)) {
      return nullptr;
    }
    if (!iterationBound.add(lhsConstant)) {
      return nullptr;
    }
  } else if (lhsModified.constant == -1 && lessEqual) {
    // The value of lhs is 'initial(lhs) - iterCount'. Similar to the above
    // case, an upper bound on the number of backedges executed is:
    //
    // initial(lhs) - iterCount + lhsN == rhs
    // iterCount == initial(lhs) - rhs + lhsN

    if (!iterationBound.add(lhsInitial, 1)) {
      return nullptr;
    }
    if (rhs) {
      if (!iterationBound.add(rhs, -1)) {
        return nullptr;
      }
    }
    if (!iterationBound.add(lhs.constant)) {
      return nullptr;
    }
  } else {
    return nullptr;
  }

  return new (alloc()) LoopIterationBound(test, iterationBound);
}

void RangeAnalysis::analyzeLoopPhi(const LoopIterationBound* loopBound,
                                   MPhi* phi) {
  // Given a bound on the number of backedges taken, compute an upper and
  // lower bound for a phi node that may change by a constant amount each
  // iteration. Unlike for the case when computing the iteration bound
  // itself, the phi does not need to change the same amount every iteration,
  // but is required to change at most N and be either nondecreasing or
  // nonincreasing.

  MOZ_ASSERT(phi->numOperands() == 2);

  MDefinition* initial = phi->getLoopPredecessorOperand();
  if (initial->block()->isMarked()) {
    return;
  }

  SimpleLinearSum modified =
      ExtractLinearSum(phi->getLoopBackedgeOperand(), MathSpace::Infinite);

  if (modified.term != phi || modified.constant == 0) {
    return;
  }

  if (!phi->range()) {
    phi->setRange(new (alloc()) Range(phi));
  }

  LinearSum initialSum(alloc());
  if (!initialSum.add(initial, 1)) {
    return;
  }

  // The phi may change by N each iteration, and is either nondecreasing or
  // nonincreasing. initial(phi) is either a lower or upper bound for the
  // phi, and initial(phi) + loopBound * N is either an upper or lower bound,
  // at all points within the loop, provided that loopBound >= 0.
  //
  // We are more interested, however, in the bound for phi at points
  // dominated by the loop bound's test; if the test dominates e.g. a bounds
  // check we want to hoist from the loop, using the value of the phi at the
  // head of the loop for this will usually be too imprecise to hoist the
  // check. These points will execute only if the backedge executes at least
  // one more time (as the test passed and the test dominates the backedge),
  // so we know both that loopBound >= 1 and that the phi's value has changed
  // at most loopBound - 1 times. Thus, another upper or lower bound for the
  // phi is initial(phi) + (loopBound - 1) * N, without requiring us to
  // ensure that loopBound >= 0.

  LinearSum limitSum(loopBound->boundSum);
  if (!limitSum.multiply(modified.constant) || !limitSum.add(initialSum)) {
    return;
  }

  int32_t negativeConstant;
  if (!SafeSub(0, modified.constant, &negativeConstant) ||
      !limitSum.add(negativeConstant)) {
    return;
  }

  Range* initRange = initial->range();
  if (modified.constant > 0) {
    if (initRange && initRange->hasInt32LowerBound()) {
      phi->range()->refineLower(initRange->lower());
    }
    phi->range()->setSymbolicLower(
        SymbolicBound::New(alloc(), nullptr, initialSum));
    phi->range()->setSymbolicUpper(
        SymbolicBound::New(alloc(), loopBound, limitSum));
  } else {
    if (initRange && initRange->hasInt32UpperBound()) {
      phi->range()->refineUpper(initRange->upper());
    }
    phi->range()->setSymbolicUpper(
        SymbolicBound::New(alloc(), nullptr, initialSum));
    phi->range()->setSymbolicLower(
        SymbolicBound::New(alloc(), loopBound, limitSum));
  }

  JitSpew(JitSpew_Range, "added symbolic range on %u", phi->id());
  SpewRange(phi);
}

// Whether bound is valid at the specified bounds check instruction in a loop,
// and may be used to hoist ins.
static inline bool SymbolicBoundIsValid(const MBasicBlock* header,
                                        const MBoundsCheck* ins,
                                        const SymbolicBound* bound) {
  if (!bound->loop) {
    return true;
  }
  if (ins->block() == header) {
    return false;
  }
  MBasicBlock* bb = ins->block()->immediateDominator();
  while (bb != header && bb != bound->loop->test->block()) {
    bb = bb->immediateDominator();
  }
  return bb == bound->loop->test->block();
}

bool RangeAnalysis::tryHoistBoundsCheck(const MBasicBlock* header,
                                        const MBoundsCheck* ins) {
  // The bounds check's length must be loop invariant or a constant.
  MDefinition* length = DefinitionOrBetaInputDefinition(ins->length());
  if (length->block()->isMarked() && !length->isConstant()) {
    return false;
  }

  // The bounds check's index should not be loop invariant (else we would
  // already have hoisted it during LICM).
  SimpleLinearSum index = ExtractLinearSum(ins->index());
  if (!index.term || !index.term->block()->isMarked()) {
    return false;
  }

  // Check for a symbolic lower and upper bound on the index. If either
  // condition depends on an iteration bound for the loop, only hoist if
  // the bounds check is dominated by the iteration bound's test.
  if (!index.term->range()) {
    return false;
  }
  const SymbolicBound* lower = index.term->range()->symbolicLower();
  if (!lower || !SymbolicBoundIsValid(header, ins, lower)) {
    return false;
  }
  const SymbolicBound* upper = index.term->range()->symbolicUpper();
  if (!upper || !SymbolicBoundIsValid(header, ins, upper)) {
    return false;
  }

  MBasicBlock* preLoop = header->loopPredecessor();
  MOZ_ASSERT(!preLoop->isMarked());

  MDefinition* lowerTerm = ConvertLinearSum(alloc(), preLoop, lower->sum,
                                            BailoutKind::HoistBoundsCheck);
  if (!lowerTerm) {
    return false;
  }

  MDefinition* upperTerm = ConvertLinearSum(alloc(), preLoop, upper->sum,
                                            BailoutKind::HoistBoundsCheck);
  if (!upperTerm) {
    return false;
  }

  // We are checking that index + indexConstant >= 0, and know that
  // index >= lowerTerm + lowerConstant. Thus, check that:
  //
  // lowerTerm + lowerConstant + indexConstant >= 0
  // lowerTerm >= -lowerConstant - indexConstant

  int32_t lowerConstant = 0;
  if (!SafeSub(lowerConstant, index.constant, &lowerConstant)) {
    return false;
  }
  if (!SafeSub(lowerConstant, lower->sum.constant(), &lowerConstant)) {
    return false;
  }

  // We are checking that index < boundsLength, and know that
  // index <= upperTerm + upperConstant. Thus, check that:
  //
  // upperTerm + upperConstant < boundsLength

  int32_t upperConstant = index.constant;
  if (!SafeAdd(upper->sum.constant(), upperConstant, &upperConstant)) {
    return false;
  }

  // Hoist the loop invariant lower bounds checks.
  MBoundsCheckLower* lowerCheck = MBoundsCheckLower::New(alloc(), lowerTerm);
  lowerCheck->setMinimum(lowerConstant);
  lowerCheck->computeRange(alloc());
  lowerCheck->collectRangeInfoPreTrunc();
  lowerCheck->setBailoutKind(BailoutKind::HoistBoundsCheck);
  preLoop->insertBefore(preLoop->lastIns(), lowerCheck);

  // A common pattern for iterating over typed arrays is this:
  //
  //   for (var i = 0; i < ta.length; i++) {
  //     use ta[i];
  //   }
  //
  // Here |upperTerm| (= ta.length) is a NonNegativeIntPtrToInt32 instruction.
  // Unwrap this if |length| is also an IntPtr so that we don't add an
  // unnecessary bounds check and Int32ToIntPtr below.
  if (upperTerm->isNonNegativeIntPtrToInt32() &&
      length->type() == MIRType::IntPtr) {
    upperTerm = upperTerm->toNonNegativeIntPtrToInt32()->input();
  }

  // Hoist the loop invariant upper bounds checks.
  if (upperTerm != length || upperConstant >= 0) {
    // Hoist the bound check's length if it isn't already loop invariant.
    if (length->block()->isMarked()) {
      MOZ_ASSERT(length->isConstant());
      MInstruction* lengthIns = length->toInstruction();
      lengthIns->block()->moveBefore(preLoop->lastIns(), lengthIns);
    }

    // If the length is IntPtr, convert the upperTerm to that as well for the
    // bounds check.
    if (length->type() == MIRType::IntPtr &&
        upperTerm->type() == MIRType::Int32) {
      upperTerm = MInt32ToIntPtr::New(alloc(), upperTerm);
      upperTerm->computeRange(alloc());
      upperTerm->collectRangeInfoPreTrunc();
      preLoop->insertBefore(preLoop->lastIns(), upperTerm->toInstruction());
    }

    MBoundsCheck* upperCheck = MBoundsCheck::New(alloc(), upperTerm, length);
    upperCheck->setMinimum(upperConstant);
    upperCheck->setMaximum(upperConstant);
    upperCheck->computeRange(alloc());
    upperCheck->collectRangeInfoPreTrunc();
    upperCheck->setBailoutKind(BailoutKind::HoistBoundsCheck);
    preLoop->insertBefore(preLoop->lastIns(), upperCheck);
  }

  return true;
}

bool RangeAnalysis::analyze() {
  JitSpew(JitSpew_Range, "Doing range propagation");

  for (ReversePostorderIterator iter(graph_.rpoBegin());
       iter != graph_.rpoEnd(); iter++) {
    MBasicBlock* block = *iter;
    // No blocks are supposed to be unreachable, except when we have an OSR
    // block, in which case the Value Numbering phase add fixup blocks which
    // are unreachable.
    MOZ_ASSERT(!block->unreachable() || graph_.osrBlock());

    // If the block's immediate dominator is unreachable, the block is
    // unreachable. Iterating in RPO, we'll always see the immediate
    // dominator before the block.
    if (block->immediateDominator()->unreachable()) {
      block->setUnreachableUnchecked();
      continue;
    }

    for (MDefinitionIterator iter(block); iter; iter++) {
      MDefinition* def = *iter;
      if (!alloc().ensureBallast()) {
        return false;
      }

      def->computeRange(alloc());
      JitSpew(JitSpew_Range, "computing range on %u", def->id());
      SpewRange(def);
    }

    // Beta node range analysis may have marked this block unreachable. If
    // so, it's no longer interesting to continue processing it.
    if (block->unreachable()) {
      continue;
    }

    if (block->isLoopHeader()) {
      if (!analyzeLoop(block)) {
        return false;
      }
    }

    // First pass at collecting range info - while the beta nodes are still
    // around and before truncation.
    for (MInstructionIterator iter(block->begin()); iter != block->end();
         iter++) {
      iter->collectRangeInfoPreTrunc();
    }
  }

  return true;
}

bool RangeAnalysis::addRangeAssertions() {
  if (!JitOptions.checkRangeAnalysis) {
    return true;
  }

  // Check the computed range for this instruction, if the option is set. Note
  // that this code is quite invasive; it adds numerous additional
  // instructions for each MInstruction with a computed range, and it uses
  // registers, so it also affects register allocation.
  for (ReversePostorderIterator iter(graph_.rpoBegin());
       iter != graph_.rpoEnd(); iter++) {
    MBasicBlock* block = *iter;

    // Do not add assertions in unreachable blocks.
    if (block->unreachable()) {
      continue;
    }

    for (MDefinitionIterator iter(block); iter; iter++) {
      MDefinition* ins = *iter;

      // Perform range checking for all numeric and numeric-like types.
      if (!IsNumberType(ins->type()) && ins->type() != MIRType::Boolean &&
          ins->type() != MIRType::Value && ins->type() != MIRType::IntPtr) {
        continue;
      }

      // MIsNoIter is fused with the MTest that follows it and emitted as
      // LIsNoIterAndBranch. Similarly, MIteratorHasIndices is fused to
      // become LIteratorHasIndicesAndBranch. Skip them to avoid complicating
      // lowering.
      if (ins->isIsNoIter() || ins->isIteratorHasIndices()) {
        MOZ_ASSERT(ins->hasOneUse());
        continue;
      }

      Range r(ins);

      MOZ_ASSERT_IF(ins->type() == MIRType::Int64, r.isUnknown());

      // Don't insert assertions if there's nothing interesting to assert.
      if (r.isUnknown() ||
          (ins->type() == MIRType::Int32 && r.isUnknownInt32())) {
        continue;
      }

      // Don't add a use to an instruction that is recovered on bailout.
      if (ins->isRecoveredOnBailout()) {
        continue;
      }

      if (!alloc().ensureBallast()) {
        return false;
      }
      MAssertRange* guard =
          MAssertRange::New(alloc(), ins, new (alloc()) Range(r));

      // Beta nodes and interrupt checks are required to be located at the
      // beginnings of basic blocks, so we must insert range assertions
      // after any such instructions.
      MInstruction* insertAt = nullptr;
      if (block->graph().osrBlock() == block) {
        insertAt = ins->toInstruction();
      } else {
        insertAt = block->safeInsertTop(ins);
      }

      if (insertAt == *iter) {
        block->insertAfter(insertAt, guard);
      } else {
        block->insertBefore(insertAt, guard);
      }
    }
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// Range based Truncation
///////////////////////////////////////////////////////////////////////////////

void Range::clampToInt32() {
  if (isInt32()) {
    return;
  }
  int32_t l = hasInt32LowerBound() ? lower() : JSVAL_INT_MIN;
  int32_t h = hasInt32UpperBound() ? upper() : JSVAL_INT_MAX;
  setInt32(l, h);
}

void Range::wrapAroundToInt32() {
  if (!hasInt32Bounds()) {
    setInt32(JSVAL_INT_MIN, JSVAL_INT_MAX);
  } else if (canHaveFractionalPart()) {
    // Clearing the fractional field may provide an opportunity to refine
    // lower_ or upper_.
    canHaveFractionalPart_ = ExcludesFractionalParts;
    canBeNegativeZero_ = ExcludesNegativeZero;
    refineInt32BoundsByExponent(max_exponent_, &lower_, &hasInt32LowerBound_,
                                &upper_, &hasInt32UpperBound_);

    assertInvariants();
  } else {
    // If nothing else, we can clear the negative zero flag.
    canBeNegativeZero_ = ExcludesNegativeZero;
  }
  MOZ_ASSERT(isInt32());
}

void Range::wrapAroundToShiftCount() {
  wrapAroundToInt32();
  if (lower() < 0 || upper() >= 32) {
    setInt32(0, 31);
  }
}

void Range::wrapAroundToBoolean() {
  wrapAroundToInt32();
  if (!isBoolean()) {
    setInt32(0, 1);
  }
  MOZ_ASSERT(isBoolean());
}

bool MDefinition::canTruncate() const {
  // No procedure defined for truncating this instruction.
  return false;
}

void MDefinition::truncate(TruncateKind kind) {
  MOZ_CRASH("No procedure defined for truncating this instruction.");
}

bool MConstant::canTruncate() const { return IsFloatingPointType(type()); }

void MConstant::truncate(TruncateKind kind) {
  MOZ_ASSERT(canTruncate());

  // Truncate the double to int, since all uses truncates it.
  int32_t res = ToInt32(numberToDouble());
  payload_.asBits = 0;
  payload_.i32 = res;
  setResultType(MIRType::Int32);
  if (range()) {
    range()->setInt32(res, res);
  }
}

bool MPhi::canTruncate() const {
  return type() == MIRType::Double || type() == MIRType::Int32;
}

void MPhi::truncate(TruncateKind kind) {
  MOZ_ASSERT(canTruncate());
  truncateKind_ = kind;
  setResultType(MIRType::Int32);
  if (kind >= TruncateKind::IndirectTruncate && range()) {
    range()->wrapAroundToInt32();
  }
}

bool MAdd::canTruncate() const {
  return type() == MIRType::Double || type() == MIRType::Int32;
}

void MAdd::truncate(TruncateKind kind) {
  MOZ_ASSERT(canTruncate());

  // Remember analysis, needed for fallible checks.
  setTruncateKind(kind);

  setSpecialization(MIRType::Int32);
  if (truncateKind() >= TruncateKind::IndirectTruncate && range()) {
    range()->wrapAroundToInt32();
  }
}

bool MSub::canTruncate() const {
  return type() == MIRType::Double || type() == MIRType::Int32;
}

void MSub::truncate(TruncateKind kind) {
  MOZ_ASSERT(canTruncate());

  // Remember analysis, needed for fallible checks.
  setTruncateKind(kind);
  setSpecialization(MIRType::Int32);
  if (truncateKind() >= TruncateKind::IndirectTruncate && range()) {
    range()->wrapAroundToInt32();
  }
}

bool MMul::canTruncate() const {
  return type() == MIRType::Double || type() == MIRType::Int32;
}

void MMul::truncate(TruncateKind kind) {
  MOZ_ASSERT(canTruncate());

  // Remember analysis, needed for fallible checks.
  setTruncateKind(kind);
  setSpecialization(MIRType::Int32);
  if (truncateKind() >= TruncateKind::IndirectTruncate) {
    setCanBeNegativeZero(false);
    if (range()) {
      range()->wrapAroundToInt32();
    }
  }
}

bool MDiv::canTruncate() const {
  return type() == MIRType::Double || type() == MIRType::Int32;
}

void MDiv::truncate(TruncateKind kind) {
  MOZ_ASSERT(canTruncate());

  // Remember analysis, needed for fallible checks.
  setTruncateKind(kind);
  setSpecialization(MIRType::Int32);

  // Divisions where the lhs and rhs are unsigned and the result is
  // truncated can be lowered more efficiently.
  if (unsignedOperands()) {
    replaceWithUnsignedOperands();
    unsigned_ = true;
  }
}

bool MMod::canTruncate() const {
  return type() == MIRType::Double || type() == MIRType::Int32;
}

void MMod::truncate(TruncateKind kind) {
  // As for division, handle unsigned modulus with a truncated result.
  MOZ_ASSERT(canTruncate());

  // Remember analysis, needed for fallible checks.
  setTruncateKind(kind);
  setSpecialization(MIRType::Int32);

  if (unsignedOperands()) {
    replaceWithUnsignedOperands();
    unsigned_ = true;
  }
}

bool MToDouble::canTruncate() const {
  MOZ_ASSERT(type() == MIRType::Double);
  return true;
}

void MToDouble::truncate(TruncateKind kind) {
  MOZ_ASSERT(canTruncate());
  setTruncateKind(kind);

  // We use the return type to flag that this MToDouble should be replaced by
  // a MTruncateToInt32 when modifying the graph.
  setResultType(MIRType::Int32);
  if (truncateKind() >= TruncateKind::IndirectTruncate) {
    if (range()) {
      range()->wrapAroundToInt32();
    }
  }
}

bool MLimitedTruncate::canTruncate() const { return true; }

void MLimitedTruncate::truncate(TruncateKind kind) {
  MOZ_ASSERT(canTruncate());
  setTruncateKind(kind);
  setResultType(MIRType::Int32);
  if (kind >= TruncateKind::IndirectTruncate && range()) {
    range()->wrapAroundToInt32();
  }
}

bool MCompare::canTruncate() const {
  if (!isDoubleComparison()) {
    return false;
  }

  // If both operands are naturally in the int32 range, we can convert from
  // a double comparison to being an int32 comparison.
  if (!Range(lhs()).isInt32() || !Range(rhs()).isInt32()) {
    return false;
  }

  return true;
}

void MCompare::truncate(TruncateKind kind) {
  MOZ_ASSERT(canTruncate());
  compareType_ = Compare_Int32;

  // Truncating the operands won't change their value because we don't force a
  // truncation, but it will change their type, which we need because we
  // now expect integer inputs.
  truncateOperands_ = true;
}

TruncateKind MDefinition::operandTruncateKind(size_t index) const {
  // Generic routine: We don't know anything.
  return TruncateKind::NoTruncate;
}

TruncateKind MPhi::operandTruncateKind(size_t index) const {
  // The truncation applied to a phi is effectively applied to the phi's
  // operands.
  return truncateKind_;
}

TruncateKind MTruncateToInt32::operandTruncateKind(size_t index) const {
  // This operator is an explicit truncate to int32.
  return TruncateKind::Truncate;
}

TruncateKind MBinaryBitwiseInstruction::operandTruncateKind(
    size_t index) const {
  // The bitwise operators truncate to int32.
  return TruncateKind::Truncate;
}

TruncateKind MLimitedTruncate::operandTruncateKind(size_t index) const {
  return std::min(truncateKind(), truncateLimit_);
}

TruncateKind MAdd::operandTruncateKind(size_t index) const {
  // This operator is doing some arithmetic. If its result is truncated,
  // it's an indirect truncate for its operands.
  return std::min(truncateKind(), TruncateKind::IndirectTruncate);
}

TruncateKind MSub::operandTruncateKind(size_t index) const {
  // See the comment in MAdd::operandTruncateKind.
  return std::min(truncateKind(), TruncateKind::IndirectTruncate);
}

TruncateKind MMul::operandTruncateKind(size_t index) const {
  // See the comment in MAdd::operandTruncateKind.
  return std::min(truncateKind(), TruncateKind::IndirectTruncate);
}

TruncateKind MToDouble::operandTruncateKind(size_t index) const {
  // MToDouble propagates its truncate kind to its operand.
  return truncateKind();
}

TruncateKind MStoreUnboxedScalar::operandTruncateKind(size_t index) const {
  // An integer store truncates the stored value.
  return (index == 2 && isIntegerWrite()) ? TruncateKind::Truncate
                                          : TruncateKind::NoTruncate;
}

TruncateKind MStoreDataViewElement::operandTruncateKind(size_t index) const {
  // An integer store truncates the stored value.
  return (index == 2 && isIntegerWrite()) ? TruncateKind::Truncate
                                          : TruncateKind::NoTruncate;
}

TruncateKind MStoreTypedArrayElementHole::operandTruncateKind(
    size_t index) const {
  // An integer store truncates the stored value.
  return (index == 3 && isIntegerWrite()) ? TruncateKind::Truncate
                                          : TruncateKind::NoTruncate;
}

TruncateKind MDiv::operandTruncateKind(size_t index) const {
  return std::min(truncateKind(), TruncateKind::TruncateAfterBailouts);
}

TruncateKind MMod::operandTruncateKind(size_t index) const {
  return std::min(truncateKind(), TruncateKind::TruncateAfterBailouts);
}

TruncateKind MCompare::operandTruncateKind(size_t index) const {
  // If we're doing an int32 comparison on operands which were previously
  // floating-point, convert them!
  MOZ_ASSERT_IF(truncateOperands_, isInt32Comparison());
  return truncateOperands_ ? TruncateKind::TruncateAfterBailouts
                           : TruncateKind::NoTruncate;
}

static bool TruncateTest(TempAllocator& alloc, const MTest* test) {
  // If all possible inputs to the test are either int32 or boolean,
  // convert those inputs to int32 so that an int32 test can be performed.

  if (test->input()->type() != MIRType::Value) {
    return true;
  }

  if (!test->input()->isPhi() || !test->input()->hasOneDefUse() ||
      test->input()->isImplicitlyUsed()) {
    return true;
  }

  MPhi* phi = test->input()->toPhi();
  for (size_t i = 0; i < phi->numOperands(); i++) {
    MDefinition* def = phi->getOperand(i);
    if (!def->isBox()) {
      return true;
    }
    MDefinition* inner = def->getOperand(0);
    if (inner->type() != MIRType::Boolean && inner->type() != MIRType::Int32) {
      return true;
    }
  }

  for (size_t i = 0; i < phi->numOperands(); i++) {
    MDefinition* inner = phi->getOperand(i)->getOperand(0);
    if (inner->type() != MIRType::Int32) {
      if (!alloc.ensureBallast()) {
        return false;
      }
      MBasicBlock* block = inner->block();
      inner = MToNumberInt32::New(alloc, inner);
      block->insertBefore(block->lastIns(), inner->toInstruction());
    }
    MOZ_ASSERT(inner->type() == MIRType::Int32);
    phi->replaceOperand(i, inner);
  }

  phi->setResultType(MIRType::Int32);
  return true;
}

// Truncating instruction result is an optimization which implies
// knowing all uses of an instruction.  This implies that if one of
// the uses got removed, then Range Analysis is not be allowed to do
// any modification which can change the result, especially if the
// result can be observed.
//
// This corner can easily be understood with UCE examples, but it
// might also happen with type inference assumptions.  Note: Type
// inference is implicitly branches where other types might be
// flowing into.
static bool CloneForDeadBranches(TempAllocator& alloc,
                                 MInstruction* candidate) {
  // Compare returns a boolean so it doesn't have to be recovered on bailout
  // because the output would remain correct.
  if (candidate->isCompare()) {
    return true;
  }

  MOZ_ASSERT(candidate->canClone());
  if (!alloc.ensureBallast()) {
    return false;
  }

  MDefinitionVector operands(alloc);
  size_t end = candidate->numOperands();
  if (!operands.reserve(end)) {
    return false;
  }
  for (size_t i = 0; i < end; ++i) {
    operands.infallibleAppend(candidate->getOperand(i));
  }

  MInstruction* clone = candidate->clone(alloc, operands);
  if (!clone) {
    return false;
  }
  clone->setRange(nullptr);

  // Set ImplicitlyUsed flag on the cloned instruction in order to chain recover
  // instruction for the bailout path.
  clone->setImplicitlyUsedUnchecked();

  candidate->block()->insertBefore(candidate, clone);

  if (!candidate->maybeConstantValue()) {
    MOZ_ASSERT(clone->canRecoverOnBailout());
    clone->setRecoveredOnBailout();
  }

  // Replace the candidate by its recovered on bailout clone within recovered
  // instructions and resume points operands.
  for (MUseIterator i(candidate->usesBegin()); i != candidate->usesEnd();) {
    MUse* use = *i++;
    MNode* ins = use->consumer();
    if (ins->isDefinition() && !ins->toDefinition()->isRecoveredOnBailout()) {
      continue;
    }

    use->replaceProducer(clone);
  }

  return true;
}

struct ComputedTruncateKind {
  TruncateKind kind = TruncateKind::NoTruncate;
  bool shouldClone = false;
};

// Examine all the users of |candidate| and determine the most aggressive
// truncate kind that satisfies all of them.
static ComputedTruncateKind ComputeRequestedTruncateKind(
    const MDefinition* candidate) {
  // Don't call this method when truncation isn't supported, because the result
  // isn't used anyway.
  MOZ_ASSERT(candidate->canTruncate());

  bool isCapturedResult =
      false;  // Check if used by a recovered instruction or a resume point.
  bool isObservableResult =
      false;  // Check if it can be read from another frame.
  bool isRecoverableResult = true;  // Check if it can safely be reconstructed.
  bool isImplicitlyUsed = candidate->isImplicitlyUsed();
  bool hasTryBlock = candidate->block()->graph().hasTryBlock();

  TruncateKind kind = TruncateKind::Truncate;
  for (MUseIterator use(candidate->usesBegin()); use != candidate->usesEnd();
       use++) {
    if (use->consumer()->isResumePoint()) {
      // Truncation is a destructive optimization, as such, we need to pay
      // attention to removed branches and prevent optimization
      // destructive optimizations if we have no alternative. (see
      // ImplicitlyUsed flag)
      isCapturedResult = true;
      isObservableResult =
          isObservableResult ||
          use->consumer()->toResumePoint()->isObservableOperand(*use);
      isRecoverableResult =
          isRecoverableResult &&
          use->consumer()->toResumePoint()->isRecoverableOperand(*use);
      continue;
    }

    MDefinition* consumer = use->consumer()->toDefinition();
    if (consumer->isRecoveredOnBailout()) {
      isCapturedResult = true;
      isImplicitlyUsed = isImplicitlyUsed || consumer->isImplicitlyUsed();
      continue;
    }

    TruncateKind consumerKind =
        consumer->operandTruncateKind(consumer->indexOf(*use));
    kind = std::min(kind, consumerKind);
    if (kind == TruncateKind::NoTruncate) {
      break;
    }
  }

  // We cannot do full truncation on guarded instructions.
  if (candidate->isGuard() || candidate->isGuardRangeBailouts()) {
    kind = std::min(kind, TruncateKind::TruncateAfterBailouts);
  }

  // If the value naturally produces an int32 value (before bailout checks)
  // that needs no conversion, we don't have to worry about resume points
  // seeing truncated values.
  bool needsConversion = !candidate->range() || !candidate->range()->isInt32();

  // If the instruction is explicitly truncated (not indirectly) by all its
  // uses and if it is not implicitly used, then we can safely encode its
  // truncated result as part of the resume point operands.  This is safe,
  // because even if we resume with a truncated double, the next baseline
  // instruction operating on this instruction is going to be a no-op.
  //
  // Note, that if the result can be observed from another frame, then this
  // optimization is not safe. Similarly, if this function contains a try
  // block, the result could be observed from a catch block, which we do
  // not compile.
  bool safeToConvert = kind == TruncateKind::Truncate && !isImplicitlyUsed &&
                       !isObservableResult && !hasTryBlock;

  // If the candidate instruction appears as operand of a resume point or a
  // recover instruction, and we have to truncate its result, then we might
  // have to either recover the result during the bailout, or avoid the
  // truncation.
  bool shouldClone = false;
  if (isCapturedResult && needsConversion && !safeToConvert) {
    // If the result can be recovered from all the resume points (not needed
    // for iterating over the inlined frames), and this instruction can be
    // recovered on bailout, then we can clone it and use the cloned
    // instruction to encode the recover instruction.  Otherwise, we should
    // keep the original result and bailout if the value is not in the int32
    // range.
    if (!JitOptions.disableRecoverIns && isRecoverableResult &&
        candidate->canRecoverOnBailout()) {
      shouldClone = true;
    } else {
      kind = std::min(kind, TruncateKind::TruncateAfterBailouts);
    }
  }

  return {kind, shouldClone};
}

static ComputedTruncateKind ComputeTruncateKind(const MDefinition* candidate) {
  // Don't call this method when truncation isn't supported, because the result
  // isn't used anyway.
  MOZ_ASSERT(candidate->canTruncate());

  // Compare operations might coerce its inputs to int32 if the ranges are
  // correct.  So we do not need to check if all uses are coerced.
  if (candidate->isCompare()) {
    return {TruncateKind::TruncateAfterBailouts};
  }

  // Set truncated flag if range analysis ensure that it has no
  // rounding errors and no fractional part. Note that we can't use
  // the MDefinition Range constructor, because we need to know if
  // the value will have rounding errors before any bailout checks.
  const Range* r = candidate->range();
  bool canHaveRoundingErrors = !r || r->canHaveRoundingErrors();

  // Special case integer division and modulo: a/b can be infinite, and a%b
  // can be NaN but cannot actually have rounding errors induced by truncation.
  if ((candidate->isDiv() || candidate->isMod()) &&
      candidate->type() == MIRType::Int32) {
    canHaveRoundingErrors = false;
  }

  if (canHaveRoundingErrors) {
    return {TruncateKind::NoTruncate};
  }

  // Ensure all observable uses are truncated.
  return ComputeRequestedTruncateKind(candidate);
}

static void RemoveTruncatesOnOutput(MDefinition* truncated) {
  // Compare returns a boolean so it doesn't have any output truncates.
  if (truncated->isCompare()) {
    return;
  }

  MOZ_ASSERT(truncated->type() == MIRType::Int32);
  MOZ_ASSERT(Range(truncated).isInt32());

  for (MUseDefIterator use(truncated); use; use++) {
    MDefinition* def = use.def();
    if (!def->isTruncateToInt32() || !def->isToNumberInt32()) {
      continue;
    }

    def->replaceAllUsesWith(truncated);
  }
}

void RangeAnalysis::adjustTruncatedInputs(MDefinition* truncated) {
  MBasicBlock* block = truncated->block();
  for (size_t i = 0, e = truncated->numOperands(); i < e; i++) {
    TruncateKind kind = truncated->operandTruncateKind(i);
    if (kind == TruncateKind::NoTruncate) {
      continue;
    }

    MDefinition* input = truncated->getOperand(i);
    if (input->type() == MIRType::Int32) {
      continue;
    }

    if (input->isToDouble() && input->getOperand(0)->type() == MIRType::Int32) {
      truncated->replaceOperand(i, input->getOperand(0));
    } else {
      MInstruction* op;
      if (kind == TruncateKind::TruncateAfterBailouts) {
        MOZ_ASSERT(!mir->outerInfo().hadEagerTruncationBailout());
        op = MToNumberInt32::New(alloc(), truncated->getOperand(i));
        op->setBailoutKind(BailoutKind::EagerTruncation);
      } else {
        op = MTruncateToInt32::New(alloc(), truncated->getOperand(i));
      }

      if (truncated->isPhi()) {
        MBasicBlock* pred = block->getPredecessor(i);
        pred->insertBefore(pred->lastIns(), op);
      } else {
        block->insertBefore(truncated->toInstruction(), op);
      }
      truncated->replaceOperand(i, op);
    }
  }

  if (truncated->isToDouble()) {
    truncated->replaceAllUsesWith(truncated->toToDouble()->getOperand(0));
    block->discard(truncated->toToDouble());
  }
}

bool RangeAnalysis::canTruncate(const MDefinition* def,
                                TruncateKind kind) const {
  // Don't call this method when truncation isn't supported, because the result
  // isn't used anyway.
  MOZ_ASSERT(def->canTruncate());

  if (kind == TruncateKind::NoTruncate) {
    return false;
  }

  // Range Analysis is sometimes eager to do optimizations, even if we
  // are not able to truncate an instruction. In such case, we
  // speculatively compile the instruction to an int32 instruction
  // while adding a guard. This is what is implied by
  // TruncateAfterBailout.
  //
  // If a previous compilation was invalidated because a speculative
  // truncation bailed out, we no longer attempt to make this kind of
  // eager optimization.
  if (mir->outerInfo().hadEagerTruncationBailout()) {
    if (kind == TruncateKind::TruncateAfterBailouts) {
      return false;
    }
    // MDiv and MMod always require TruncateAfterBailout for their operands.
    // See MDiv::operandTruncateKind and MMod::operandTruncateKind.
    if (def->isDiv() || def->isMod()) {
      return false;
    }
  }

  return true;
}

// Iterate backward on all instruction and attempt to truncate operations for
// each instruction which respect the following list of predicates: Has been
// analyzed by range analysis, the range has no rounding errors, all uses cases
// are truncating the result.
//
// If the truncation of the operation is successful, then the instruction is
// queue for later updating the graph to restore the type correctness by
// converting the operands that need to be truncated.
//
// We iterate backward because it is likely that a truncated operation truncates
// some of its operands.
bool RangeAnalysis::truncate() {
  JitSpew(JitSpew_Range, "Do range-base truncation (backward loop)");

  // Automatic truncation is disabled for wasm because the truncation logic
  // is based on IonMonkey which assumes that we can bailout if the truncation
  // logic fails. As wasm code has no bailout mechanism, it is safer to avoid
  // any automatic truncations.
  MOZ_ASSERT(!mir->compilingWasm());

  Vector<MDefinition*, 16, SystemAllocPolicy> worklist;

  for (PostorderIterator block(graph_.poBegin()); block != graph_.poEnd();
       block++) {
    for (MInstructionReverseIterator iter(block->rbegin());
         iter != block->rend(); iter++) {
      if (iter->isRecoveredOnBailout()) {
        continue;
      }

      if (iter->type() == MIRType::None) {
        if (iter->isTest()) {
          if (!TruncateTest(alloc(), iter->toTest())) {
            return false;
          }
        }
        continue;
      }

      // Remember all bitop instructions for folding after range analysis.
      switch (iter->op()) {
        case MDefinition::Opcode::BitAnd:
        case MDefinition::Opcode::BitOr:
        case MDefinition::Opcode::BitXor:
        case MDefinition::Opcode::Lsh:
        case MDefinition::Opcode::Rsh:
        case MDefinition::Opcode::Ursh:
          if (!bitops.append(static_cast<MBinaryBitwiseInstruction*>(*iter))) {
            return false;
          }
          break;
        default:;
      }

      // Skip instructions which can't be truncated.
      if (!iter->canTruncate()) {
        continue;
      }

      auto [kind, shouldClone] = ComputeTruncateKind(*iter);

      // Truncate this instruction if possible.
      if (!canTruncate(*iter, kind)) {
        continue;
      }

      SpewTruncate(*iter, kind, shouldClone);

      // If needed, clone the current instruction for keeping it for the
      // bailout path.  This give us the ability to truncate instructions
      // even after the removal of branches.
      if (shouldClone && !CloneForDeadBranches(alloc(), *iter)) {
        return false;
      }

      // TruncateAfterBailouts keeps the bailout code as-is and
      // continues with truncated operations, with the expectation
      // that we are unlikely to bail out. If we do bail out, then we
      // will set a flag in FinishBailoutToBaseline to prevent eager
      // truncation when we recompile, to avoid bailout loops.
      if (kind == TruncateKind::TruncateAfterBailouts) {
        iter->setBailoutKind(BailoutKind::EagerTruncation);
      }

      iter->truncate(kind);

      // Delay updates of inputs/outputs to avoid creating node which
      // would be removed by the truncation of the next operations.
      iter->setInWorklist();
      if (!worklist.append(*iter)) {
        return false;
      }
    }
    for (MPhiIterator iter(block->phisBegin()), end(block->phisEnd());
         iter != end; ++iter) {
      // Skip phis which can't be truncated.
      if (!iter->canTruncate()) {
        continue;
      }

      auto [kind, shouldClone] = ComputeTruncateKind(*iter);

      // Truncate this phi if possible.
      if (shouldClone || !canTruncate(*iter, kind)) {
        continue;
      }

      SpewTruncate(*iter, kind, shouldClone);

      iter->truncate(kind);

      // Delay updates of inputs/outputs to avoid creating node which
      // would be removed by the truncation of the next operations.
      iter->setInWorklist();
      if (!worklist.append(*iter)) {
        return false;
      }
    }
  }

  // Update inputs/outputs of truncated instructions.
  JitSpew(JitSpew_Range, "Do graph type fixup (dequeue)");
  while (!worklist.empty()) {
    if (!alloc().ensureBallast()) {
      return false;
    }
    MDefinition* def = worklist.popCopy();
    def->setNotInWorklist();
    RemoveTruncatesOnOutput(def);
    adjustTruncatedInputs(def);
  }

  return true;
}

bool RangeAnalysis::removeUnnecessaryBitops() {
  JitSpew(JitSpew_Range, "Begin (removeUnnecessaryBitops)");
  // Note: This operation change the semantic of the program in a way which
  // uniquely works with Int32, Recover Instructions added by the Sink phase
  // expects the MIR Graph to still have a valid flow as-if they were double
  // operations instead of Int32 operations. Thus, this phase should be
  // executed after the Sink phase, and before DCE.

  // Fold any unnecessary bitops in the graph, such as (x | 0) on an integer
  // input. This is done after range analysis rather than during GVN as the
  // presence of the bitop can change which instructions are truncated.
  for (size_t i = 0; i < bitops.length(); i++) {
    MBinaryBitwiseInstruction* ins = bitops[i];
    if (ins->isRecoveredOnBailout()) {
      continue;
    }

    MDefinition* folded = ins->foldUnnecessaryBitop();
    if (folded != ins) {
      ins->replaceAllLiveUsesWith(folded);
      ins->setRecoveredOnBailout();
    }
  }

  bitops.clear();
  return true;
}

///////////////////////////////////////////////////////////////////////////////
// Collect Range information of operands
///////////////////////////////////////////////////////////////////////////////

void MInArray::collectRangeInfoPreTrunc() {
  Range indexRange(index());
  if (indexRange.isFiniteNonNegative()) {
    needsNegativeIntCheck_ = false;
    setNotGuard();
  }
}

void MLoadElementHole::collectRangeInfoPreTrunc() {
  Range indexRange(index());
  if (indexRange.isFiniteNonNegative()) {
    needsNegativeIntCheck_ = false;
    setNotGuard();
  }
}

void MInt32ToIntPtr::collectRangeInfoPreTrunc() {
  Range inputRange(input());
  if (inputRange.isFiniteNonNegative()) {
    canBeNegative_ = false;
  }
}

void MClz::collectRangeInfoPreTrunc() {
  Range inputRange(input());
  if (!inputRange.canBeZero()) {
    operandIsNeverZero_ = true;
  }
}

void MCtz::collectRangeInfoPreTrunc() {
  Range inputRange(input());
  if (!inputRange.canBeZero()) {
    operandIsNeverZero_ = true;
  }
}

void MDiv::collectRangeInfoPreTrunc() {
  Range lhsRange(lhs());
  Range rhsRange(rhs());

  // Test if Dividend is non-negative.
  if (lhsRange.isFiniteNonNegative()) {
    canBeNegativeDividend_ = false;
  }

  // Try removing divide by zero check.
  if (!rhsRange.canBeZero()) {
    canBeDivideByZero_ = false;
  }

  // If lhsRange does not contain INT32_MIN in its range,
  // negative overflow check can be skipped.
  if (!lhsRange.contains(INT32_MIN)) {
    canBeNegativeOverflow_ = false;
  }

  // If rhsRange does not contain -1 likewise.
  if (!rhsRange.contains(-1)) {
    canBeNegativeOverflow_ = false;
  }

  // If lhsRange does not contain a zero,
  // negative zero check can be skipped.
  if (!lhsRange.canBeZero()) {
    canBeNegativeZero_ = false;
  }

  // If rhsRange >= 0 negative zero check can be skipped.
  if (rhsRange.isFiniteNonNegative()) {
    canBeNegativeZero_ = false;
  }

  if (type() == MIRType::Int32 && fallible()) {
    setGuardRangeBailoutsUnchecked();
  }
}

void MMul::collectRangeInfoPreTrunc() {
  Range lhsRange(lhs());
  Range rhsRange(rhs());

  // If lhsRange contains only positive then we can skip negative zero check.
  if (lhsRange.isFiniteNonNegative() && !lhsRange.canBeZero()) {
    setCanBeNegativeZero(false);
  }

  // Likewise rhsRange.
  if (rhsRange.isFiniteNonNegative() && !rhsRange.canBeZero()) {
    setCanBeNegativeZero(false);
  }

  // If rhsRange and lhsRange contain Non-negative integers only,
  // We skip negative zero check.
  if (rhsRange.isFiniteNonNegative() && lhsRange.isFiniteNonNegative()) {
    setCanBeNegativeZero(false);
  }

  // If rhsRange and lhsRange < 0. Then we skip negative zero check.
  if (rhsRange.isFiniteNegative() && lhsRange.isFiniteNegative()) {
    setCanBeNegativeZero(false);
  }
}

void MMod::collectRangeInfoPreTrunc() {
  Range lhsRange(lhs());
  Range rhsRange(rhs());
  if (lhsRange.isFiniteNonNegative()) {
    canBeNegativeDividend_ = false;
  }
  if (!rhsRange.canBeZero()) {
    canBeDivideByZero_ = false;
  }
  if (type() == MIRType::Int32 && fallible()) {
    setGuardRangeBailoutsUnchecked();
  }
}

void MToNumberInt32::collectRangeInfoPreTrunc() {
  Range inputRange(input());
  if (!inputRange.canBeNegativeZero()) {
    needsNegativeZeroCheck_ = false;
  }
}

void MBoundsCheck::collectRangeInfoPreTrunc() {
  Range indexRange(index());
  Range lengthRange(length());
  if (!indexRange.hasInt32LowerBound() || !indexRange.hasInt32UpperBound()) {
    return;
  }
  if (!lengthRange.hasInt32LowerBound() || lengthRange.canBeNaN()) {
    return;
  }

  int64_t indexLower = indexRange.lower();
  int64_t indexUpper = indexRange.upper();
  int64_t lengthLower = lengthRange.lower();
  int64_t min = minimum();
  int64_t max = maximum();

  if (indexLower + min >= 0 && indexUpper + max < lengthLower) {
    fallible_ = false;
  }
}

void MBoundsCheckLower::collectRangeInfoPreTrunc() {
  Range indexRange(index());
  if (indexRange.hasInt32LowerBound() && indexRange.lower() >= minimum_) {
    fallible_ = false;
  }
}

void MCompare::collectRangeInfoPreTrunc() {
  if (!Range(lhs()).canBeNaN() && !Range(rhs()).canBeNaN()) {
    operandsAreNeverNaN_ = true;
  }
}

void MNot::collectRangeInfoPreTrunc() {
  if (!Range(input()).canBeNaN()) {
    operandIsNeverNaN_ = true;
  }
}

void MPowHalf::collectRangeInfoPreTrunc() {
  Range inputRange(input());
  if (!inputRange.canBeInfiniteOrNaN() || inputRange.hasInt32LowerBound()) {
    operandIsNeverNegativeInfinity_ = true;
  }
  if (!inputRange.canBeNegativeZero()) {
    operandIsNeverNegativeZero_ = true;
  }
  if (!inputRange.canBeNaN()) {
    operandIsNeverNaN_ = true;
  }
}

void MUrsh::collectRangeInfoPreTrunc() {
  if (type() == MIRType::Int64) {
    return;
  }

  Range lhsRange(lhs()), rhsRange(rhs());

  // As in MUrsh::computeRange(), convert the inputs.
  lhsRange.wrapAroundToInt32();
  rhsRange.wrapAroundToShiftCount();

  // If the most significant bit of our result is always going to be zero,
  // we can optimize by disabling bailout checks for enforcing an int32 range.
  if (lhsRange.lower() >= 0 || rhsRange.lower() >= 1) {
    bailoutsDisabled_ = true;
  }
}

static bool DoesMaskMatchRange(int32_t mask, const Range& range) {
  // Check if range is positive, because the bitand operator in `(-3) & 0xff`
  // can't be eliminated.
  if (range.lower() >= 0) {
    MOZ_ASSERT(range.isInt32());
    // Check that the mask value has all bits set given the range upper bound.
    // Note that the upper bound does not have to be exactly the mask value. For
    // example, consider `x & 0xfff` where `x` is a uint8. That expression can
    // still be optimized to `x`.
    int bits = 1 + FloorLog2(range.upper());
    uint32_t maskNeeded = (bits == 32) ? 0xffffffff : (uint32_t(1) << bits) - 1;
    if ((mask & maskNeeded) == maskNeeded) {
      return true;
    }
  }

  return false;
}

void MBinaryBitwiseInstruction::collectRangeInfoPreTrunc() {
  Range lhsRange(lhs());
  Range rhsRange(rhs());

  if (lhs()->isConstant() && lhs()->type() == MIRType::Int32 &&
      DoesMaskMatchRange(lhs()->toConstant()->toInt32(), rhsRange)) {
    maskMatchesRightRange = true;
  }

  if (rhs()->isConstant() && rhs()->type() == MIRType::Int32 &&
      DoesMaskMatchRange(rhs()->toConstant()->toInt32(), lhsRange)) {
    maskMatchesLeftRange = true;
  }
}

void MNaNToZero::collectRangeInfoPreTrunc() {
  Range inputRange(input());

  if (!inputRange.canBeNaN()) {
    operandIsNeverNaN_ = true;
  }
  if (!inputRange.canBeNegativeZero()) {
    operandIsNeverNegativeZero_ = true;
  }
}

bool RangeAnalysis::prepareForUCE(bool* shouldRemoveDeadCode) {
  *shouldRemoveDeadCode = false;

  for (ReversePostorderIterator iter(graph_.rpoBegin());
       iter != graph_.rpoEnd(); iter++) {
    MBasicBlock* block = *iter;

    if (!block->unreachable()) {
      continue;
    }

    // Filter out unreachable fake entries.
    if (block->numPredecessors() == 0) {
      // Ignore fixup blocks added by the Value Numbering phase, in order
      // to keep the dominator tree as-is when we have OSR Block which are
      // no longer reachable from the main entry point of the graph.
      MOZ_ASSERT(graph_.osrBlock());
      continue;
    }

    MControlInstruction* cond = block->getPredecessor(0)->lastIns();
    if (!cond->isTest()) {
      continue;
    }

    // Replace the condition of the test control instruction by a constant
    // chosen based which of the successors has the unreachable flag which is
    // added by MBeta::computeRange on its own block.
    MTest* test = cond->toTest();
    MDefinition* condition = test->input();

    // If the false-branch is unreachable, then the test condition must be true.
    // If the true-branch is unreachable, then the test condition must be false.
    MOZ_ASSERT(block == test->ifTrue() || block == test->ifFalse());
    bool value = block == test->ifFalse();
    MConstant* constant =
        MConstant::New(alloc().fallible(), BooleanValue(value));
    if (!constant) {
      return false;
    }

    condition->setGuardRangeBailoutsUnchecked();

    test->block()->insertBefore(test, constant);

    test->replaceOperand(0, constant);
    JitSpew(JitSpew_Range,
            "Update condition of %u to reflect unreachable branches.",
            test->id());

    *shouldRemoveDeadCode = true;
  }

  return tryRemovingGuards();
}

bool RangeAnalysis::tryRemovingGuards() {
  MDefinitionVector guards(alloc());

  for (ReversePostorderIterator block = graph_.rpoBegin();
       block != graph_.rpoEnd(); block++) {
    for (MDefinitionIterator iter(*block); iter; iter++) {
      if (!iter->isGuardRangeBailouts()) {
        continue;
      }

      iter->setInWorklist();
      if (!guards.append(*iter)) {
        return false;
      }
    }
  }

  // Flag all fallible instructions which were indirectly used in the
  // computation of the condition, such that we do not ignore
  // bailout-paths which are used to shrink the input range of the
  // operands of the condition.
  for (size_t i = 0; i < guards.length(); i++) {
    MDefinition* guard = guards[i];

    // If this ins is a guard even without guardRangeBailouts,
    // there is no reason in trying to hoist the guardRangeBailouts check.
    guard->setNotGuardRangeBailouts();
    if (!DeadIfUnused(guard)) {
      guard->setGuardRangeBailouts();
      continue;
    }
    guard->setGuardRangeBailouts();

    if (!guard->isPhi()) {
      if (!guard->range()) {
        continue;
      }

      // Filter the range of the instruction based on its MIRType.
      Range typeFilteredRange(guard);

      // If the output range is updated by adding the inner range,
      // then the MIRType act as an effectful filter. As we do not know if
      // this filtered Range might change or not the result of the
      // previous comparison, we have to keep this instruction as a guard
      // because it has to bailout in order to restrict the Range to its
      // MIRType.
      if (typeFilteredRange.update(guard->range())) {
        continue;
      }
    }

    guard->setNotGuardRangeBailouts();

    // Propagate the guard to its operands.
    for (size_t op = 0, e = guard->numOperands(); op < e; op++) {
      MDefinition* operand = guard->getOperand(op);

      // Already marked.
      if (operand->isInWorklist()) {
        continue;
      }

      MOZ_ASSERT(!operand->isGuardRangeBailouts());

      operand->setInWorklist();
      operand->setGuardRangeBailouts();
      if (!guards.append(operand)) {
        return false;
      }
    }
  }

  for (size_t i = 0; i < guards.length(); i++) {
    MDefinition* guard = guards[i];
    guard->setNotInWorklist();
  }

  return true;
}

Messung V0.5 in Prozent
C=84 H=95 G=89

¤ Dauer der Verarbeitung: 0.46 Sekunden  (vorverarbeitet am  2026-04-26) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.