| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * 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/FloatingPoint.h" |
| #include "mozilla/MathAlgorithms.h" |
| |
| #include "jit/IonAnalysis.h" |
| #include "jit/MIR.h" |
| |
| // windows.h defines those, which messes with the definitions below. |
| #undef min |
| #undef max |
| |
| namespace js { |
| namespace jit { |
| |
| class MBasicBlock; |
| class MIRGraph; |
| |
| // 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 |
| { |
| // Loop for which this bound applies. |
| MBasicBlock* header; |
| |
| // 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. |
| MTest* test; |
| |
| // Symbolic bound computed for the number of backedge executions. The terms |
| // in this bound are all loop invariant. |
| LinearSum boundSum; |
| |
| // Linear sum for the number of iterations already executed, at the start |
| // of the loop header. This will use loop invariant terms and header phis. |
| LinearSum currentSum; |
| |
| LoopIterationBound(MBasicBlock* header, MTest* test, LinearSum boundSum, LinearSum currentSum) |
| : header(header), test(test), |
| boundSum(boundSum), currentSum(currentSum) |
| { |
| } |
| }; |
| |
| typedef Vector<LoopIterationBound*, 0, SystemAllocPolicy> LoopIterationBoundVector; |
| |
| // A symbolic upper or lower bound computed for a term. |
| struct SymbolicBound : public TempObject |
| { |
| private: |
| SymbolicBound(LoopIterationBound* loop, 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. |
| LoopIterationBound* loop; |
| |
| static SymbolicBound* New(TempAllocator& alloc, LoopIterationBound* loop, 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 |
| { |
| protected: |
| bool blockDominates(MBasicBlock* b, MBasicBlock* b2); |
| void replaceDominatedUsesWith(MDefinition* orig, MDefinition* dom, |
| MBasicBlock* block); |
| |
| protected: |
| MIRGenerator* mir; |
| MIRGraph& graph_; |
| |
| TempAllocator& alloc() const; |
| |
| public: |
| RangeAnalysis(MIRGenerator* mir, MIRGraph& graph) : |
| mir(mir), graph_(graph) {} |
| bool addBetaNodes(); |
| bool analyze(); |
| bool addRangeAssertions(); |
| bool removeBetaNodes(); |
| bool prepareForUCE(bool* shouldRemoveDeadCode); |
| bool tryRemovingGuards(); |
| bool truncate(); |
| |
| // Any iteration bounds discovered for loops in the graph. |
| LoopIterationBoundVector loopIterationBounds; |
| |
| private: |
| bool analyzeLoop(MBasicBlock* header); |
| LoopIterationBound* analyzeLoopIterationCount(MBasicBlock* header, |
| MTest* test, BranchDirection direction); |
| void analyzeLoopPhi(MBasicBlock* header, LoopIterationBound* loopBound, MPhi* phi); |
| bool tryHoistBoundsCheck(MBasicBlock* header, 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 { |
| ExcludesFractionalParts = false, |
| IncludesFractionalParts = true |
| }; |
| enum NegativeZeroFlag { |
| 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. |
| |
| int32_t lower_; |
| int32_t upper_; |
| |
| bool hasInt32LowerBound_; |
| bool hasInt32UpperBound_; |
| |
| FractionalPartFlag canHaveFractionalPart_ : 1; |
| NegativeZeroFlag canBeNegativeZero_ : 1; |
| 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 = 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 = Min(*h, limit); |
| *l = 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 (mozilla::IsNaN(l) && mozilla::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 (mozilla::IsNaN(d)) |
| return nullptr; |
| |
| Range* r = new(alloc) Range(); |
| r->setDoubleSingleton(d); |
| return r; |
| } |
| |
| void dump(GenericPrinter& out) const; |
| void dump() const; |
| 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 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_ = 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_ = 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 */ |