diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 9bb99a44c2ec..79587451e6e4 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -1621,7 +1621,7 @@ MAdd::fallible() const { // the add is fallible if range analysis does not say that it is finite, AND // either the truncation analysis shows that there are non-truncated uses. - if (isTruncated()) + if (truncateKind() >= IndirectTruncate) return false; if (range() && range()->hasInt32Bounds()) return false; @@ -1632,7 +1632,7 @@ bool MSub::fallible() const { // see comment in MAdd::fallible() - if (isTruncated()) + if (truncateKind() >= IndirectTruncate) return false; if (range() && range()->hasInt32Bounds()) return false; diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 1a434b88e42f..3326084c3a20 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -412,8 +412,44 @@ class MDefinition : public MNode virtual void analyzeEdgeCasesForward(); virtual void analyzeEdgeCasesBackward(); - virtual bool truncate(); - virtual bool isOperandTruncated(size_t index) const; + // When a floating-point value is used by nodes which would prefer to + // recieve integer inputs, we may be able to help by computing our result + // into an integer directly. + // + // A value can be truncated in 4 differents ways: + // 1. Ignore Infinities (x / 0 --> 0). + // 2. Ignore overflow (INT_MIN / -1 == (INT_MAX + 1) --> INT_MIN) + // 3. Ignore negative zeros. (-0 --> 0) + // 4. Ignore remainder. (3 / 4 --> 0) + // + // Indirect truncation is used to represent that we are interested in the + // truncated result, but only if it can safely flow into operations which + // are computed modulo 2^32, such as (2) and (3). Infinities are not safe, + // as they would have absorbed other math operations. Remainders are not + // safe, as fractions can be scaled up by multiplication. + // + // Division is a particularly interesting node here because it covers all 4 + // cases even when its own operands are integers. + // + // Note that these enum values are ordered from least value-modifying to + // most value-modifying, and code relies on this ordering. + enum TruncateKind { + // No correction. + NoTruncate = 0, + // An integer is desired, but we can't skip bailout checks. + TruncateAfterBailouts = 1, + // The value will be truncated after some arithmetic (see above). + IndirectTruncate = 2, + // Direct and infallible truncation to int32. + Truncate = 3 + }; + + // Apply the given truncate to this node itself. + virtual bool truncate(TruncateKind kind); + + // Determine what kind of truncate this node prefers for the operand at the + // given index. + virtual TruncateKind operandTruncateKind(size_t index) const; // Compute an absolute or symbolic range for the value of this node. virtual void computeRange(TempAllocator &alloc) { @@ -977,7 +1013,7 @@ class MConstant : public MNullaryInstruction } void computeRange(TempAllocator &alloc); - bool truncate(); + bool truncate(TruncateKind kind); bool canProduceFloat32() const; }; @@ -2375,8 +2411,8 @@ class MCompare void trySpecializeFloat32(TempAllocator &alloc); bool isFloat32Commutative() const { return true; } - bool truncate(); - bool isOperandTruncated(size_t index) const; + bool truncate(TruncateKind kind); + TruncateKind operandTruncateKind(size_t index) const; # ifdef DEBUG bool isConsistentFloat32Use(MUse *use) const { @@ -2905,8 +2941,10 @@ class MToDouble private: ConversionKind conversion_; + TruncateKind implicitTruncate_; + MToDouble(MDefinition *def, ConversionKind conversion = NonStringPrimitives) - : MUnaryInstruction(def), conversion_(conversion) + : MUnaryInstruction(def), conversion_(conversion), implicitTruncate_(NoTruncate) { setResultType(MIRType_Double); setMovable(); @@ -2946,12 +2984,19 @@ class MToDouble } void computeRange(TempAllocator &alloc); - bool truncate(); - bool isOperandTruncated(size_t index) const; + bool truncate(TruncateKind kind); + TruncateKind operandTruncateKind(size_t index) const; #ifdef DEBUG bool isConsistentFloat32Use(MUse *use) const { return true; } #endif + + TruncateKind truncateKind() const { + return implicitTruncate_; + } + void setTruncateKind(TruncateKind kind) { + implicitTruncate_ = Max(implicitTruncate_, kind); + } }; // Converts a primitive (either typed or untyped) to a float32. If the input is @@ -3171,7 +3216,7 @@ class MTruncateToInt32 : public MUnaryInstruction } void computeRange(TempAllocator &alloc); - bool isOperandTruncated(size_t index) const; + TruncateKind operandTruncateKind(size_t index) const; # ifdef DEBUG bool isConsistentFloat32Use(MUse *use) const { return true; @@ -3349,7 +3394,7 @@ class MBinaryBitwiseInstruction return AliasSet::None(); } - bool isOperandTruncated(size_t index) const; + TruncateKind operandTruncateKind(size_t index) const; }; class MBitAnd : public MBinaryBitwiseInstruction @@ -3524,14 +3569,14 @@ class MBinaryArithInstruction // This optimization happens when the multiplication cannot be truncated // even if all uses are truncating its result, such as when the range // analysis detect a precision loss in the multiplication. - bool implicitTruncate_; + TruncateKind implicitTruncate_; void inferFallback(BaselineInspector *inspector, jsbytecode *pc); public: MBinaryArithInstruction(MDefinition *left, MDefinition *right) : MBinaryInstruction(left, right), - implicitTruncate_(false) + implicitTruncate_(NoTruncate) { setMovable(); } @@ -3566,10 +3611,13 @@ class MBinaryArithInstruction } bool isTruncated() const { + return implicitTruncate_ == Truncate; + } + TruncateKind truncateKind() const { return implicitTruncate_; } - void setTruncated(bool truncate) { - implicitTruncate_ = truncate; + void setTruncateKind(TruncateKind kind) { + implicitTruncate_ = Max(implicitTruncate_, kind); } }; @@ -4017,7 +4065,7 @@ class MAdd : public MBinaryArithInstruction add->specialization_ = type; add->setResultType(type); if (type == MIRType_Int32) { - add->setTruncated(true); + add->setTruncateKind(Truncate); add->setCommutative(); } return add; @@ -4031,8 +4079,8 @@ class MAdd : public MBinaryArithInstruction bool fallible() const; void computeRange(TempAllocator &alloc); - bool truncate(); - bool isOperandTruncated(size_t index) const; + bool truncate(TruncateKind kind); + TruncateKind operandTruncateKind(size_t index) const; bool writeRecoverData(CompactBufferWriter &writer) const; bool canRecoverOnBailout() const { @@ -4060,7 +4108,7 @@ class MSub : public MBinaryArithInstruction sub->specialization_ = type; sub->setResultType(type); if (type == MIRType_Int32) - sub->setTruncated(true); + sub->setTruncateKind(Truncate); return sub; } @@ -4072,8 +4120,8 @@ class MSub : public MBinaryArithInstruction bool fallible() const; void computeRange(TempAllocator &alloc); - bool truncate(); - bool isOperandTruncated(size_t index) const; + bool truncate(TruncateKind kind); + TruncateKind operandTruncateKind(size_t index) const; }; class MMul : public MBinaryArithInstruction @@ -4100,7 +4148,7 @@ class MMul : public MBinaryArithInstruction // This implements the required behavior for Math.imul, which // can never fail and always truncates its output to int32. canBeNegativeZero_ = false; - setTruncated(true); + setTruncateKind(Truncate); setCommutative(); } JS_ASSERT_IF(mode != Integer, mode == Normal); @@ -4165,8 +4213,8 @@ class MMul : public MBinaryArithInstruction bool isFloat32Commutative() const { return true; } void computeRange(TempAllocator &alloc); - bool truncate(); - bool isOperandTruncated(size_t index) const; + bool truncate(TruncateKind kind); + TruncateKind operandTruncateKind(size_t index) const; Mode mode() const { return mode_; } }; @@ -4179,32 +4227,13 @@ class MDiv : public MBinaryArithInstruction bool canBeNegativeDividend_; bool unsigned_; - // A Division can be truncated in 4 differents ways: - // 1. Ignore Infinities (x / 0 --> 0). - // 2. Ignore overflow (INT_MIN / -1 == (INT_MAX + 1) --> INT_MIN) - // 3. Ignore negative zeros. (-0 --> 0) - // 4. Ignore remainder. (3 / 4 --> 0) - // - // isTruncatedIndirectly is used to represent that we are interested in the - // truncated result, but only if they it can safely flow in operations which - // are computed modulo 2^32, such as (2) and (3). - // - // A division can return either Infinities (1) or a remainder (4) when both - // operands are integers. Infinities are not safe, as they would have - // absorbed other math operations. Remainders are not safe, as multiple can - // add up to integers. This implies that we need to distinguish between a - // division which is truncated directly (isTruncated) or which flow into - // truncated operations (isTruncatedIndirectly). - bool isTruncatedIndirectly_; - MDiv(MDefinition *left, MDefinition *right, MIRType type) : MBinaryArithInstruction(left, right), canBeNegativeZero_(true), canBeNegativeOverflow_(true), canBeDivideByZero_(true), canBeNegativeDividend_(true), - unsigned_(false), - isTruncatedIndirectly_(false) + unsigned_(false) { if (type != MIRType_Value) specialization_ = type; @@ -4225,7 +4254,7 @@ class MDiv : public MBinaryArithInstruction MDiv *div = new(alloc) MDiv(left, right, type); div->unsigned_ = unsignd; if (type == MIRType_Int32) - div->setTruncated(true); + div->setTruncateKind(Truncate); return div; } @@ -4261,10 +4290,7 @@ class MDiv : public MBinaryArithInstruction } bool isTruncatedIndirectly() const { - return isTruncatedIndirectly_; - } - void setTruncatedIndirectly(bool truncate) { - isTruncatedIndirectly_ = truncate; + return truncateKind() >= IndirectTruncate; } bool canTruncateInfinities() const { @@ -4284,7 +4310,7 @@ class MDiv : public MBinaryArithInstruction void computeRange(TempAllocator &alloc); bool fallible() const; - bool truncate(); + bool truncate(TruncateKind kind); void collectRangeInfoPreTrunc(); }; @@ -4314,7 +4340,7 @@ class MMod : public MBinaryArithInstruction MMod *mod = new(alloc) MMod(left, right, type); mod->unsigned_ = unsignd; if (type == MIRType_Int32) - mod->setTruncated(true); + mod->setTruncateKind(Truncate); return mod; } @@ -4338,7 +4364,7 @@ class MMod : public MBinaryArithInstruction bool fallible() const; void computeRange(TempAllocator &alloc); - bool truncate(); + bool truncate(TruncateKind kind); void collectRangeInfoPreTrunc(); }; @@ -6543,7 +6569,7 @@ class MLoadTypedArrayElementStatic } void computeRange(TempAllocator &alloc); - bool truncate(); + bool truncate(TruncateKind kind); bool canProduceFloat32() const { return typedArray_->type() == ScalarTypeDescr::TYPE_FLOAT32; } }; @@ -6608,7 +6634,7 @@ class MStoreTypedArrayElement void setRacy() { racy_ = true; } - bool isOperandTruncated(size_t index) const; + TruncateKind operandTruncateKind(size_t index) const; bool canConsumeFloat32(MUse *use) const { return use->index() == 2 && arrayType_ == ScalarTypeDescr::TYPE_FLOAT32; @@ -6676,7 +6702,7 @@ class MStoreTypedArrayElementHole AliasSet getAliasSet() const { return AliasSet::Store(AliasSet::TypedArrayElement); } - bool isOperandTruncated(size_t index) const; + TruncateKind operandTruncateKind(size_t index) const; bool canConsumeFloat32(MUse *use) const { return use->index() == 3 && arrayType_ == ScalarTypeDescr::TYPE_FLOAT32; @@ -6723,7 +6749,7 @@ class MStoreTypedArrayElementStatic : AliasSet getAliasSet() const { return AliasSet::Store(AliasSet::TypedArrayElement); } - bool isOperandTruncated(size_t index) const; + TruncateKind operandTruncateKind(size_t index) const; bool canConsumeFloat32(MUse *use) const { return use->index() == 1 && typedArray_->type() == ScalarTypeDescr::TYPE_FLOAT32; diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index 965387ca2733..886965be4cf5 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -2136,14 +2136,14 @@ Range::wrapAroundToBoolean() } bool -MDefinition::truncate() +MDefinition::truncate(TruncateKind kind) { // No procedure defined for truncating this instruction. return false; } bool -MConstant::truncate() +MConstant::truncate(TruncateKind kind) { if (!value_.isDouble()) return false; @@ -2158,15 +2158,15 @@ MConstant::truncate() } bool -MAdd::truncate() +MAdd::truncate(TruncateKind kind) { // Remember analysis, needed for fallible checks. - setTruncated(true); + setTruncateKind(kind); if (type() == MIRType_Double || type() == MIRType_Int32) { specialization_ = MIRType_Int32; setResultType(MIRType_Int32); - if (range()) + if (kind >= IndirectTruncate && range()) range()->wrapAroundToInt32(); return true; } @@ -2175,15 +2175,15 @@ MAdd::truncate() } bool -MSub::truncate() +MSub::truncate(TruncateKind kind) { // Remember analysis, needed for fallible checks. - setTruncated(true); + setTruncateKind(kind); if (type() == MIRType_Double || type() == MIRType_Int32) { specialization_ = MIRType_Int32; setResultType(MIRType_Int32); - if (range()) + if (kind >= IndirectTruncate && range()) range()->wrapAroundToInt32(); return true; } @@ -2192,54 +2192,39 @@ MSub::truncate() } bool -MMul::truncate() +MMul::truncate(TruncateKind kind) { // Remember analysis, needed to remove negative zero checks. - setTruncated(true); + setTruncateKind(kind); if (type() == MIRType_Double || type() == MIRType_Int32) { specialization_ = MIRType_Int32; setResultType(MIRType_Int32); - setCanBeNegativeZero(false); - if (range()) - range()->wrapAroundToInt32(); - return true; - } - - return false; -} - -bool -MDiv::truncate() -{ - // Remember analysis, needed to remove negative zero checks. - setTruncatedIndirectly(true); - - // Check if this division only flows in bitwise instructions. - if (!isTruncated()) { - bool allUsesExplictlyTruncate = true; - for (MUseDefIterator use(this); allUsesExplictlyTruncate && use; use++) { - switch (use.def()->op()) { - case MDefinition::Op_BitAnd: - case MDefinition::Op_BitOr: - case MDefinition::Op_BitXor: - case MDefinition::Op_Lsh: - case MDefinition::Op_Rsh: - case MDefinition::Op_Ursh: - break; - default: - allUsesExplictlyTruncate = false; - } + if (kind >= IndirectTruncate) { + setCanBeNegativeZero(false); + if (range()) + range()->wrapAroundToInt32(); } - - if (allUsesExplictlyTruncate) - setTruncated(true); + return true; } - // Divisions where the lhs and rhs are unsigned and the result is - // truncated can be lowered more efficiently. - if (specialization() == MIRType_Int32 && tryUseUnsignedOperands()) { - unsigned_ = true; + return false; +} + +bool +MDiv::truncate(TruncateKind kind) +{ + setTruncateKind(kind); + + if (type() == MIRType_Double || type() == MIRType_Int32) { + specialization_ = MIRType_Int32; + setResultType(MIRType_Int32); + + // Divisions where the lhs and rhs are unsigned and the result is + // truncated can be lowered more efficiently. + if (tryUseUnsignedOperands()) + unsigned_ = true; + return true; } @@ -2248,14 +2233,19 @@ MDiv::truncate() } bool -MMod::truncate() +MMod::truncate(TruncateKind kind) { // Remember analysis, needed to remove negative zero checks. - setTruncated(true); + setTruncateKind(kind); // As for division, handle unsigned modulus with a truncated result. - if (specialization() == MIRType_Int32 && tryUseUnsignedOperands()) { - unsigned_ = true; + if (type() == MIRType_Double || type() == MIRType_Int32) { + specialization_ = MIRType_Int32; + setResultType(MIRType_Int32); + + if (tryUseUnsignedOperands()) + unsigned_ = true; + return true; } @@ -2264,90 +2254,103 @@ MMod::truncate() } bool -MToDouble::truncate() +MToDouble::truncate(TruncateKind kind) { JS_ASSERT(type() == MIRType_Double); + 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 (range()) - range()->wrapAroundToInt32(); + if (kind >= IndirectTruncate) { + if (range()) + range()->wrapAroundToInt32(); + } return true; } bool -MLoadTypedArrayElementStatic::truncate() +MLoadTypedArrayElementStatic::truncate(TruncateKind kind) { setInfallible(); return false; } -bool -MDefinition::isOperandTruncated(size_t index) const +MDefinition::TruncateKind +MDefinition::operandTruncateKind(size_t index) const { - return false; + // Generic routine: We don't know anything. + return NoTruncate; +} + +MDefinition::TruncateKind +MTruncateToInt32::operandTruncateKind(size_t index) const +{ + // This operator is an explicit truncate to int32. + return Truncate; +} + +MDefinition::TruncateKind +MBinaryBitwiseInstruction::operandTruncateKind(size_t index) const +{ + // The bitwise operators truncate to int32. + return Truncate; +} + +MDefinition::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 Min(truncateKind(), IndirectTruncate); +} + +MDefinition::TruncateKind +MSub::operandTruncateKind(size_t index) const +{ + // See the comment in MAdd::operandTruncateKind. + return Min(truncateKind(), IndirectTruncate); +} + +MDefinition::TruncateKind +MMul::operandTruncateKind(size_t index) const +{ + // See the comment in MAdd::operandTruncateKind. + return Min(truncateKind(), IndirectTruncate); +} + +MDefinition::TruncateKind +MToDouble::operandTruncateKind(size_t index) const +{ + // MToDouble propagates its truncate kind to its operand. + return truncateKind(); +} + +MDefinition::TruncateKind +MStoreTypedArrayElement::operandTruncateKind(size_t index) const +{ + // An integer store truncates the stored value. + return index == 2 && !isFloatArray() ? Truncate : NoTruncate; +} + +MDefinition::TruncateKind +MStoreTypedArrayElementHole::operandTruncateKind(size_t index) const +{ + // An integer store truncates the stored value. + return index == 3 && !isFloatArray() ? Truncate : NoTruncate; +} + +MDefinition::TruncateKind +MStoreTypedArrayElementStatic::operandTruncateKind(size_t index) const +{ + // An integer store truncates the stored value. + return index == 1 && !isFloatArray() ? Truncate : NoTruncate; } bool -MTruncateToInt32::isOperandTruncated(size_t index) const -{ - return true; -} - -bool -MBinaryBitwiseInstruction::isOperandTruncated(size_t index) const -{ - return true; -} - -bool -MAdd::isOperandTruncated(size_t index) const -{ - return isTruncated(); -} - -bool -MSub::isOperandTruncated(size_t index) const -{ - return isTruncated(); -} - -bool -MMul::isOperandTruncated(size_t index) const -{ - return isTruncated(); -} - -bool -MToDouble::isOperandTruncated(size_t index) const -{ - // The return type is used to flag that we are replacing this Double by a - // Truncate of its operand if needed. - return type() == MIRType_Int32; -} - -bool -MStoreTypedArrayElement::isOperandTruncated(size_t index) const -{ - return index == 2 && !isFloatArray(); -} - -bool -MStoreTypedArrayElementHole::isOperandTruncated(size_t index) const -{ - return index == 3 && !isFloatArray(); -} - -bool -MStoreTypedArrayElementStatic::isOperandTruncated(size_t index) const -{ - return index == 1 && !isFloatArray(); -} - -bool -MCompare::truncate() +MCompare::truncate(TruncateKind kind) { if (!isDoubleComparison()) return false; @@ -2359,32 +2362,34 @@ MCompare::truncate() compareType_ = Compare_Int32; - // Truncating the operands won't change their value, but it will change - // their type, which we need because we now expect integer inputs. + // 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; return true; } -bool -MCompare::isOperandTruncated(size_t index) const +MDefinition::TruncateKind +MCompare::operandTruncateKind(size_t index) const { // If we're doing an int32 comparison on operands which were previously // floating-point, convert them! JS_ASSERT_IF(truncateOperands_, isInt32Comparison()); - return truncateOperands_; + return truncateOperands_ ? TruncateAfterBailouts : NoTruncate; } -// Ensure that all observables uses can work with a truncated -// version of the |candidate|'s result. -static bool -AllUsesTruncate(MInstruction *candidate) +// Examine all the users of |candidate| and determine the most aggressive +// truncate kind that satisfies all of them. +static MDefinition::TruncateKind +ComputeRequestedTruncateKind(MInstruction *candidate) { // 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(); + MDefinition::TruncateKind kind = MDefinition::Truncate; for (MUseIterator use(candidate->usesBegin()); use != candidate->usesEnd(); use++) { if (!use->consumer()->isDefinition()) { // We can only skip testing resume points, if all original uses are @@ -2393,24 +2398,27 @@ AllUsesTruncate(MInstruction *candidate) // value, and any bailout with a truncated value might lead an // incorrect value. if (candidate->isUseRemoved() && needsConversion) - return false; + kind = Min(kind, MDefinition::TruncateAfterBailouts); continue; } - if (!use->consumer()->toDefinition()->isOperandTruncated(use->index())) - return false; + MDefinition *consumer = use->consumer()->toDefinition(); + MDefinition::TruncateKind consumerKind = consumer->operandTruncateKind(use->index()); + kind = Min(kind, consumerKind); + if (kind == MDefinition::NoTruncate) + break; } - return true; + return kind; } -static bool -CanTruncate(MInstruction *candidate) +static MDefinition::TruncateKind +ComputeTruncateKind(MInstruction *candidate) { // 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 true; + return MDefinition::TruncateAfterBailouts; // Set truncated flag if range analysis ensure that it has no // rounding errors and no fractional part. Note that we can't use @@ -2425,10 +2433,10 @@ CanTruncate(MInstruction *candidate) canHaveRoundingErrors = false; if (canHaveRoundingErrors) - return false; + return MDefinition::NoTruncate; // Ensure all observable uses are truncated. - return AllUsesTruncate(candidate); + return ComputeRequestedTruncateKind(candidate); } static void @@ -2455,7 +2463,7 @@ AdjustTruncatedInputs(TempAllocator &alloc, MInstruction *truncated) { MBasicBlock *block = truncated->block(); for (size_t i = 0, e = truncated->numOperands(); i < e; i++) { - if (!truncated->isOperandTruncated(i)) + if (truncated->operandTruncateKind(i) == MDefinition::NoTruncate) continue; MDefinition *input = truncated->getOperand(i); @@ -2515,11 +2523,12 @@ RangeAnalysis::truncate() default:; } - if (!CanTruncate(*iter)) + MDefinition::TruncateKind kind = ComputeTruncateKind(*iter); + if (kind == MDefinition::NoTruncate) continue; // Truncate this instruction if possible. - if (!iter->truncate()) + if (!iter->truncate(kind)) continue; // Delay updates of inputs/outputs to avoid creating node which