/* -*- 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/. */

#ifndef jit_RangeAnalysis_h
#define jit_RangeAnalysis_h

#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/MathAlgorithms.h"

#include <algorithm>
#include <stdint.h>

#include "jit/IonAnalysis.h"
#include "jit/IonTypes.h"
#include "jit/JitAllocPolicy.h"
#include "js/AllocPolicy.h"
#include "js/Value.h"
#include "js/Vector.h"

namespace js {

class JS_PUBLIC_API GenericPrinter;

namespace jit {

class MBasicBlock;
class MBinaryBitwiseInstruction;
class MBoundsCheck;
class MDefinition;
class MIRGenerator;
class MIRGraph;
class MPhi;
class MTest;

enum class TruncateKind;

// An upper bound computed on the number of backedges a loop will take.
// This count only includes backedges taken while running Ion code: for OSR
// loops, this will exclude iterations that executed in the interpreter or in
// baseline compiled code.
struct LoopIterationBound : public TempObject {
  // Test from which this bound was derived; after executing exactly 'bound'
  // times this test will exit the loop. Code in the loop body which this
  // test dominates (will include the backedge) will execute at most 'bound'
  // times. Other code in the loop will execute at most '1 + Max(bound, 0)'
  // times.
  const MTest* test;

  // Symbolic bound computed for the number of backedge executions. The terms
  // in this bound are all loop invariant.
  LinearSum boundSum;

  LoopIterationBound(const MTest* test, const LinearSum& boundSum)
      : test(test), boundSum(boundSum) {}
};

using LoopIterationBoundVector =
    Vector<LoopIterationBound*, 0, SystemAllocPolicy>;

// A symbolic upper or lower bound computed for a term.
struct SymbolicBound : public TempObject {
 private:
  SymbolicBound(const LoopIterationBound* loop, const LinearSum& sum)
      : loop(loop), sum(sum) {}

 public:
  // Any loop iteration bound from which this was derived.
  //
  // If non-nullptr, then 'sum' is only valid within the loop body, at
  // points dominated by the loop bound's test (see LoopIterationBound).
  //
  // If nullptr, then 'sum' is always valid.
  const LoopIterationBound* loop;

  static SymbolicBound* New(TempAllocator& alloc,
                            const LoopIterationBound* loop,
                            const LinearSum& sum) {
    return new (alloc) SymbolicBound(loop, sum);
  }

  // Computed symbolic bound, see above.
  LinearSum sum;

  void dump(GenericPrinter& out) const;
  void dump() const;
};

class RangeAnalysis {
  const MIRGenerator* mir;
  MIRGraph& graph_;
  Vector<MBinaryBitwiseInstruction*, 16, SystemAllocPolicy> bitops;

  TempAllocator& alloc() const;

 public:
  RangeAnalysis(const MIRGenerator* mir, MIRGraph& graph)
      : mir(mir), graph_(graph) {}
  [[nodiscard]] bool addBetaNodes();
  [[nodiscard]] bool analyze();
  [[nodiscard]] bool addRangeAssertions();
  [[nodiscard]] bool removeBetaNodes();
  [[nodiscard]] bool prepareForUCE(bool* shouldRemoveDeadCode);
  [[nodiscard]] bool tryRemovingGuards();
  [[nodiscard]] bool truncate();
  [[nodiscard]] bool removeUnnecessaryBitops();

 private:
  bool canTruncate(const MDefinition* def, TruncateKind kind) const;
  void adjustTruncatedInputs(MDefinition* def);

  // Any iteration bounds discovered for loops in the graph.
  LoopIterationBoundVector loopIterationBounds;

 private:
  [[nodiscard]] bool analyzeLoop(const MBasicBlock* header);
  LoopIterationBound* analyzeLoopIterationCount(const MBasicBlock* header,
                                                const MTest* test,
                                                BranchDirection direction);
  void analyzeLoopPhi(const LoopIterationBound* loopBound, MPhi* phi);
  [[nodiscard]] bool tryHoistBoundsCheck(const MBasicBlock* header,
                                         const MBoundsCheck* ins);
};

class Range : public TempObject {
 public:
  // Int32 are signed. INT32_MAX is pow(2,31)-1 and INT32_MIN is -pow(2,31),
  // so the greatest exponent we need is 31.
  static const uint16_t MaxInt32Exponent = 31;

  // UInt32 are unsigned. UINT32_MAX is pow(2,32)-1, so it's the greatest
  // value that has an exponent of 31.
  static const uint16_t MaxUInt32Exponent = 31;

  // Maximal exponenent under which we have no precission loss on double
  // operations. Double has 52 bits of mantissa, so 2^52+1 cannot be
  // represented without loss.
  static const uint16_t MaxTruncatableExponent =
      mozilla::FloatingPoint<double>::kExponentShift;

  // Maximum exponent for finite values.
  static const uint16_t MaxFiniteExponent =
      mozilla::FloatingPoint<double>::kExponentBias;

  // An special exponent value representing all non-NaN values. This
  // includes finite values and the infinities.
  static const uint16_t IncludesInfinity = MaxFiniteExponent + 1;

  // An special exponent value representing all possible double-precision
  // values. This includes finite values, the infinities, and NaNs.
  static const uint16_t IncludesInfinityAndNaN = UINT16_MAX;

  // This range class uses int32_t ranges, but has several interfaces which
  // use int64_t, which either holds an int32_t value, or one of the following
  // special values which mean a value which is beyond the int32 range,
  // potentially including infinity or NaN. These special values are
  // guaranteed to compare greater, and less than, respectively, any int32_t
  // value.
  static const int64_t NoInt32UpperBound = int64_t(JSVAL_INT_MAX) + 1;
  static const int64_t NoInt32LowerBound = int64_t(JSVAL_INT_MIN) - 1;

  enum FractionalPartFlag : bool {
    ExcludesFractionalParts = false,
    IncludesFractionalParts = true
  };
  enum NegativeZeroFlag : bool {
    ExcludesNegativeZero = false,
    IncludesNegativeZero = true
  };

 private:
  // Absolute ranges.
  //
  // We represent ranges where the endpoints can be in the set:
  // {-infty} U [INT_MIN, INT_MAX] U {infty}.  A bound of +/-
  // infty means that the value may have overflowed in that
  // direction. When computing the range of an integer
  // instruction, the ranges of the operands can be clamped to
  // [INT_MIN, INT_MAX], since if they had overflowed they would
  // no longer be integers. This is important for optimizations
  // and somewhat subtle.
  //
  // N.B.: All of the operations that compute new ranges based
  // on existing ranges will ignore the hasInt32*Bound_ flags of the
  // input ranges; that is, they implicitly clamp the ranges of
  // the inputs to [INT_MIN, INT_MAX]. Therefore, while our range might
  // be unbounded (and could overflow), when using this information to
  // propagate through other ranges, we disregard this fact; if that code
  // executes, then the overflow did not occur, so we may safely assume
  // that the range is [INT_MIN, INT_MAX] instead.
  //
  // To facilitate this trick, we maintain the invariants that:
  // 1) hasInt32LowerBound_ == false implies lower_ == JSVAL_INT_MIN
  // 2) hasInt32UpperBound_ == false implies upper_ == JSVAL_INT_MAX
  //
  // As a second and less precise range analysis, we represent the maximal
  // exponent taken by a value. The exponent is calculated by taking the
  // absolute value and looking at the position of the highest bit.  All
  // exponent computation have to be over-estimations of the actual result. On
  // the Int32 this over approximation is rectified.

  MOZ_INIT_OUTSIDE_CTOR int32_t lower_;
  MOZ_INIT_OUTSIDE_CTOR int32_t upper_;

  MOZ_INIT_OUTSIDE_CTOR bool hasInt32LowerBound_;
  MOZ_INIT_OUTSIDE_CTOR bool hasInt32UpperBound_;

  MOZ_INIT_OUTSIDE_CTOR FractionalPartFlag canHaveFractionalPart_ : 1;
  MOZ_INIT_OUTSIDE_CTOR NegativeZeroFlag canBeNegativeZero_ : 1;
  MOZ_INIT_OUTSIDE_CTOR uint16_t max_exponent_;

  // Any symbolic lower or upper bound computed for this term.
  const SymbolicBound* symbolicLower_;
  const SymbolicBound* symbolicUpper_;

  // This function simply makes several MOZ_ASSERTs to verify the internal
  // consistency of this range.
  void assertInvariants() const {
    // Basic sanity :).
    MOZ_ASSERT(lower_ <= upper_);

    // When hasInt32LowerBound_ or hasInt32UpperBound_ are false, we set
    // lower_ and upper_ to these specific values as it simplifies the
    // implementation in some places.
    MOZ_ASSERT_IF(!hasInt32LowerBound_, lower_ == JSVAL_INT_MIN);
    MOZ_ASSERT_IF(!hasInt32UpperBound_, upper_ == JSVAL_INT_MAX);

    // max_exponent_ must be one of three possible things.
    MOZ_ASSERT(max_exponent_ <= MaxFiniteExponent ||
               max_exponent_ == IncludesInfinity ||
               max_exponent_ == IncludesInfinityAndNaN);

    // Forbid the max_exponent_ field from implying better bounds for
    // lower_/upper_ fields. We have to add 1 to the max_exponent_ when
    // canHaveFractionalPart_ is true in order to accomodate
    // fractional offsets. For example, 2147483647.9 is greater than
    // INT32_MAX, so a range containing that value will have
    // hasInt32UpperBound_ set to false, however that value also has
    // exponent 30, which is strictly less than MaxInt32Exponent. For
    // another example, 1.9 has an exponent of 0 but requires upper_ to be
    // at least 2, which has exponent 1.
    mozilla::DebugOnly<uint32_t> adjustedExponent =
        max_exponent_ + (canHaveFractionalPart_ ? 1 : 0);
    MOZ_ASSERT_IF(!hasInt32LowerBound_ || !hasInt32UpperBound_,
                  adjustedExponent >= MaxInt32Exponent);
    MOZ_ASSERT(adjustedExponent >= mozilla::FloorLog2(mozilla::Abs(upper_)));
    MOZ_ASSERT(adjustedExponent >= mozilla::FloorLog2(mozilla::Abs(lower_)));

    // The following are essentially static assertions, but FloorLog2 isn't
    // trivially suitable for constexpr :(.
    MOZ_ASSERT(mozilla::FloorLog2(JSVAL_INT_MIN) == MaxInt32Exponent);
    MOZ_ASSERT(mozilla::FloorLog2(JSVAL_INT_MAX) == 30);
    MOZ_ASSERT(mozilla::FloorLog2(UINT32_MAX) == MaxUInt32Exponent);
    MOZ_ASSERT(mozilla::FloorLog2(0) == 0);
  }

  // Set the lower_ and hasInt32LowerBound_ values.
  void setLowerInit(int64_t x) {
    if (x > JSVAL_INT_MAX) {
      lower_ = JSVAL_INT_MAX;
      hasInt32LowerBound_ = true;
    } else if (x < JSVAL_INT_MIN) {
      lower_ = JSVAL_INT_MIN;
      hasInt32LowerBound_ = false;
    } else {
      lower_ = int32_t(x);
      hasInt32LowerBound_ = true;
    }
  }
  // Set the upper_ and hasInt32UpperBound_ values.
  void setUpperInit(int64_t x) {
    if (x > JSVAL_INT_MAX) {
      upper_ = JSVAL_INT_MAX;
      hasInt32UpperBound_ = false;
    } else if (x < JSVAL_INT_MIN) {
      upper_ = JSVAL_INT_MIN;
      hasInt32UpperBound_ = true;
    } else {
      upper_ = int32_t(x);
      hasInt32UpperBound_ = true;
    }
  }

  // Compute the least exponent value that would be compatible with the
  // values of lower() and upper().
  //
  // Note:
  //     exponent of JSVAL_INT_MIN == 31
  //     exponent of JSVAL_INT_MAX == 30
  uint16_t exponentImpliedByInt32Bounds() const {
    // The number of bits needed to encode |max| is the power of 2 plus one.
    uint32_t max = std::max(mozilla::Abs(lower()), mozilla::Abs(upper()));
    uint16_t result = mozilla::FloorLog2(max);
    MOZ_ASSERT(result ==
               (max == 0 ? 0 : mozilla::ExponentComponent(double(max))));
    return result;
  }

  // When converting a range which contains fractional values to a range
  // containing only integers, the old max_exponent_ value may imply a better
  // lower and/or upper bound than was previously available, because they no
  // longer need to be conservative about fractional offsets and the ends of
  // the range.
  //
  // Given an exponent value and pointers to the lower and upper bound values,
  // this function refines the lower and upper bound values to the tighest
  // bound for integer values implied by the exponent.
  static void refineInt32BoundsByExponent(uint16_t e, int32_t* l, bool* lb,
                                          int32_t* h, bool* hb) {
    if (e < MaxInt32Exponent) {
      // pow(2, max_exponent_+1)-1 to compute a maximum absolute value.
      int32_t limit = (uint32_t(1) << (e + 1)) - 1;
      *h = std::min(*h, limit);
      *l = std::max(*l, -limit);
      *hb = true;
      *lb = true;
    }
  }

  // If the value of any of the fields implies a stronger possible value for
  // any other field, update that field to the stronger value. The range must
  // be completely valid before and it is guaranteed to be kept valid.
  void optimize() {
    assertInvariants();

    if (hasInt32Bounds()) {
      // Examine lower() and upper(), and if they imply a better exponent
      // bound than max_exponent_, set that value as the new
      // max_exponent_.
      uint16_t newExponent = exponentImpliedByInt32Bounds();
      if (newExponent < max_exponent_) {
        max_exponent_ = newExponent;
        assertInvariants();
      }

      // If we have a completely precise range, the value is an integer,
      // since we can only represent integers.
      if (canHaveFractionalPart_ && lower_ == upper_) {
        canHaveFractionalPart_ = ExcludesFractionalParts;
        assertInvariants();
      }
    }

    // If the range doesn't include zero, it doesn't include negative zero.
    if (canBeNegativeZero_ && !canBeZero()) {
      canBeNegativeZero_ = ExcludesNegativeZero;
      assertInvariants();
    }
  }

  // Set the range fields to the given raw values.
  void rawInitialize(int32_t l, bool lb, int32_t h, bool hb,
                     FractionalPartFlag canHaveFractionalPart,
                     NegativeZeroFlag canBeNegativeZero, uint16_t e) {
    lower_ = l;
    upper_ = h;
    hasInt32LowerBound_ = lb;
    hasInt32UpperBound_ = hb;
    canHaveFractionalPart_ = canHaveFractionalPart;
    canBeNegativeZero_ = canBeNegativeZero;
    max_exponent_ = e;
    optimize();
  }

  // Construct a range from the given raw values.
  Range(int32_t l, bool lb, int32_t h, bool hb,
        FractionalPartFlag canHaveFractionalPart,
        NegativeZeroFlag canBeNegativeZero, uint16_t e)
      : symbolicLower_(nullptr), symbolicUpper_(nullptr) {
    rawInitialize(l, lb, h, hb, canHaveFractionalPart, canBeNegativeZero, e);
  }

 public:
  Range() : symbolicLower_(nullptr), symbolicUpper_(nullptr) { setUnknown(); }

  Range(int64_t l, int64_t h, FractionalPartFlag canHaveFractionalPart,
        NegativeZeroFlag canBeNegativeZero, uint16_t e)
      : symbolicLower_(nullptr), symbolicUpper_(nullptr) {
    set(l, h, canHaveFractionalPart, canBeNegativeZero, e);
  }

  Range(const Range& other)
      : lower_(other.lower_),
        upper_(other.upper_),
        hasInt32LowerBound_(other.hasInt32LowerBound_),
        hasInt32UpperBound_(other.hasInt32UpperBound_),
        canHaveFractionalPart_(other.canHaveFractionalPart_),
        canBeNegativeZero_(other.canBeNegativeZero_),
        max_exponent_(other.max_exponent_),
        symbolicLower_(nullptr),
        symbolicUpper_(nullptr) {
    assertInvariants();
  }

  // Construct a range from the given MDefinition. This differs from the
  // MDefinition's range() method in that it describes the range of values
  // *after* any bailout checks.
  explicit Range(const MDefinition* def);

  static Range* NewInt32Range(TempAllocator& alloc, int32_t l, int32_t h) {
    return new (alloc) Range(l, h, ExcludesFractionalParts,
                             ExcludesNegativeZero, MaxInt32Exponent);
  }

  // Construct an int32 range containing just i. This is just a convenience
  // wrapper around NewInt32Range.
  static Range* NewInt32SingletonRange(TempAllocator& alloc, int32_t i) {
    return NewInt32Range(alloc, i, i);
  }

  static Range* NewUInt32Range(TempAllocator& alloc, uint32_t l, uint32_t h) {
    // For now, just pass them to the constructor as int64_t values.
    // They'll become unbounded if they're not in the int32_t range.
    return new (alloc) Range(l, h, ExcludesFractionalParts,
                             ExcludesNegativeZero, MaxUInt32Exponent);
  }

  // Construct a range containing values >= l and <= h. Note that this
  // function treats negative zero as equal to zero, as >= and <= do. If the
  // range includes zero, it is assumed to include negative zero too.
  static Range* NewDoubleRange(TempAllocator& alloc, double l, double h) {
    if (std::isnan(l) && std::isnan(h)) {
      return nullptr;
    }

    Range* r = new (alloc) Range();
    r->setDouble(l, h);
    return r;
  }

  // Construct the strictest possible range containing d, or null if d is NaN.
  // This function treats negative zero as distinct from zero, since this
  // makes the strictest possible range containin zero a range which
  // contains one value rather than two.
  static Range* NewDoubleSingletonRange(TempAllocator& alloc, double d) {
    if (std::isnan(d)) {
      return nullptr;
    }

    Range* r = new (alloc) Range();
    r->setDoubleSingleton(d);
    return r;
  }

  void dump(GenericPrinter& out) const;
  void dump() const;
  [[nodiscard]] bool update(const Range* other);

  // Unlike the other operations, unionWith is an in-place
  // modification. This is to avoid a bunch of useless extra
  // copying when chaining together unions when handling Phi
  // nodes.
  void unionWith(const Range* other);
  static Range* intersect(TempAllocator& alloc, const Range* lhs,
                          const Range* rhs, bool* emptyRange);
  static Range* add(TempAllocator& alloc, const Range* lhs, const Range* rhs);
  static Range* sub(TempAllocator& alloc, const Range* lhs, const Range* rhs);
  static Range* mul(TempAllocator& alloc, const Range* lhs, const Range* rhs);
  static Range* and_(TempAllocator& alloc, const Range* lhs, const Range* rhs);
  static Range* or_(TempAllocator& alloc, const Range* lhs, const Range* rhs);
  static Range* xor_(TempAllocator& alloc, const Range* lhs, const Range* rhs);
  static Range* not_(TempAllocator& alloc, const Range* op);
  static Range* lsh(TempAllocator& alloc, const Range* lhs, int32_t c);
  static Range* rsh(TempAllocator& alloc, const Range* lhs, int32_t c);
  static Range* ursh(TempAllocator& alloc, const Range* lhs, int32_t c);
  static Range* lsh(TempAllocator& alloc, const Range* lhs, const Range* rhs);
  static Range* rsh(TempAllocator& alloc, const Range* lhs, const Range* rhs);
  static Range* ursh(TempAllocator& alloc, const Range* lhs, const Range* rhs);
  static Range* abs(TempAllocator& alloc, const Range* op);
  static Range* min(TempAllocator& alloc, const Range* lhs, const Range* rhs);
  static Range* max(TempAllocator& alloc, const Range* lhs, const Range* rhs);
  static Range* floor(TempAllocator& alloc, const Range* op);
  static Range* ceil(TempAllocator& alloc, const Range* op);
  static Range* sign(TempAllocator& alloc, const Range* op);
  static Range* NaNToZero(TempAllocator& alloc, const Range* op);

  [[nodiscard]] static bool negativeZeroMul(const Range* lhs, const Range* rhs);

  bool isUnknownInt32() const {
    return isInt32() && lower() == INT32_MIN && upper() == INT32_MAX;
  }

  bool isUnknown() const {
    return !hasInt32LowerBound_ && !hasInt32UpperBound_ &&
           canHaveFractionalPart_ && canBeNegativeZero_ &&
           max_exponent_ == IncludesInfinityAndNaN;
  }

  bool hasInt32LowerBound() const { return hasInt32LowerBound_; }
  bool hasInt32UpperBound() const { return hasInt32UpperBound_; }

  // Test whether the value is known to be within [INT32_MIN,INT32_MAX].
  // Note that this does not necessarily mean the value is an integer.
  bool hasInt32Bounds() const {
    return hasInt32LowerBound() && hasInt32UpperBound();
  }

  // Test whether the value is known to be representable as an int32.
  bool isInt32() const {
    return hasInt32Bounds() && !canHaveFractionalPart_ && !canBeNegativeZero_;
  }

  // Test whether the given value is known to be either 0 or 1.
  bool isBoolean() const {
    return lower() >= 0 && upper() <= 1 && !canHaveFractionalPart_ &&
           !canBeNegativeZero_;
  }

  bool canHaveRoundingErrors() const {
    return canHaveFractionalPart_ || canBeNegativeZero_ ||
           max_exponent_ >= MaxTruncatableExponent;
  }

  // Test if an integer x belongs to the range.
  bool contains(int32_t x) const { return x >= lower_ && x <= upper_; }

  // Test whether the range contains zero (of either sign).
  bool canBeZero() const { return contains(0); }

  // Test whether the range contains NaN values.
  bool canBeNaN() const { return max_exponent_ == IncludesInfinityAndNaN; }

  // Test whether the range contains infinities or NaN values.
  bool canBeInfiniteOrNaN() const { return max_exponent_ >= IncludesInfinity; }

  FractionalPartFlag canHaveFractionalPart() const {
    return canHaveFractionalPart_;
  }

  NegativeZeroFlag canBeNegativeZero() const { return canBeNegativeZero_; }

  uint16_t exponent() const {
    MOZ_ASSERT(!canBeInfiniteOrNaN());
    return max_exponent_;
  }

  uint16_t numBits() const {
    return exponent() + 1;  // 2^0 -> 1
  }

  // Return the lower bound. Asserts that the value has an int32 bound.
  int32_t lower() const {
    MOZ_ASSERT(hasInt32LowerBound());
    return lower_;
  }

  // Return the upper bound. Asserts that the value has an int32 bound.
  int32_t upper() const {
    MOZ_ASSERT(hasInt32UpperBound());
    return upper_;
  }

  // Test whether all values in this range can are finite and negative.
  bool isFiniteNegative() const { return upper_ < 0 && !canBeInfiniteOrNaN(); }

  // Test whether all values in this range can are finite and non-negative.
  bool isFiniteNonNegative() const {
    return lower_ >= 0 && !canBeInfiniteOrNaN();
  }

  // Test whether a value in this range can possibly be a finite
  // negative value. Note that "negative zero" is not considered negative.
  bool canBeFiniteNegative() const { return lower_ < 0; }

  // Test whether a value in this range can possibly be a finite
  // non-negative value.
  bool canBeFiniteNonNegative() const { return upper_ >= 0; }

  // Test whether a value in this range can have the sign bit set (not
  // counting NaN, where the sign bit is meaningless).
  bool canHaveSignBitSet() const {
    return !hasInt32LowerBound() || canBeFiniteNegative() ||
           canBeNegativeZero();
  }

  // Set this range to have a lower bound not less than x.
  void refineLower(int32_t x) {
    assertInvariants();
    hasInt32LowerBound_ = true;
    lower_ = std::max(lower_, x);
    optimize();
  }

  // Set this range to have an upper bound not greater than x.
  void refineUpper(int32_t x) {
    assertInvariants();
    hasInt32UpperBound_ = true;
    upper_ = std::min(upper_, x);
    optimize();
  }

  // Set this range to exclude negative zero.
  void refineToExcludeNegativeZero() {
    assertInvariants();
    canBeNegativeZero_ = ExcludesNegativeZero;
    optimize();
  }

  void setInt32(int32_t l, int32_t h) {
    hasInt32LowerBound_ = true;
    hasInt32UpperBound_ = true;
    lower_ = l;
    upper_ = h;
    canHaveFractionalPart_ = ExcludesFractionalParts;
    canBeNegativeZero_ = ExcludesNegativeZero;
    max_exponent_ = exponentImpliedByInt32Bounds();
    assertInvariants();
  }

  // Set this range to include values >= l and <= h. Note that this
  // function treats negative zero as equal to zero, as >= and <= do. If the
  // range includes zero, it is assumed to include negative zero too.
  void setDouble(double l, double h);

  // Set this range to the narrowest possible range containing d.
  // This function treats negative zero as distinct from zero, since this
  // makes the narrowest possible range containin zero a range which
  // contains one value rather than two.
  void setDoubleSingleton(double d);

  void setUnknown() {
    set(NoInt32LowerBound, NoInt32UpperBound, IncludesFractionalParts,
        IncludesNegativeZero, IncludesInfinityAndNaN);
    MOZ_ASSERT(isUnknown());
  }

  void set(int64_t l, int64_t h, FractionalPartFlag canHaveFractionalPart,
           NegativeZeroFlag canBeNegativeZero, uint16_t e) {
    max_exponent_ = e;
    canHaveFractionalPart_ = canHaveFractionalPart;
    canBeNegativeZero_ = canBeNegativeZero;
    setLowerInit(l);
    setUpperInit(h);
    optimize();
  }

  // Make the lower end of this range at least INT32_MIN, and make
  // the upper end of this range at most INT32_MAX.
  void clampToInt32();

  // If this range exceeds int32_t range, at either or both ends, change
  // it to int32_t range.  Otherwise do nothing.
  void wrapAroundToInt32();

  // If this range exceeds [0, 32) range, at either or both ends, change
  // it to the [0, 32) range.  Otherwise do nothing.
  void wrapAroundToShiftCount();

  // If this range exceeds [0, 1] range, at either or both ends, change
  // it to the [0, 1] range.  Otherwise do nothing.
  void wrapAroundToBoolean();

  const SymbolicBound* symbolicLower() const { return symbolicLower_; }
  const SymbolicBound* symbolicUpper() const { return symbolicUpper_; }

  void setSymbolicLower(SymbolicBound* bound) { symbolicLower_ = bound; }
  void setSymbolicUpper(SymbolicBound* bound) { symbolicUpper_ = bound; }
};

}  // namespace jit
}  // namespace js

#endif /* jit_RangeAnalysis_h */
