diff --git a/js/src/ion/MIR.cpp b/js/src/ion/MIR.cpp index 73bf18563f8..beb502e1466 100644 --- a/js/src/ion/MIR.cpp +++ b/js/src/ion/MIR.cpp @@ -1545,6 +1545,26 @@ MustBeUInt32(MDefinition *def, MDefinition **pwrapped) return false; } +bool +MBinaryInstruction::tryUseUnsignedOperands() +{ + MDefinition *newlhs, *newrhs; + if (MustBeUInt32(getOperand(0), &newlhs) && MustBeUInt32(getOperand(1), &newrhs)) { + if (newlhs->type() != MIRType_Int32 || newrhs->type() != MIRType_Int32) + return false; + if (newlhs != getOperand(0)) { + getOperand(0)->setFoldedUnchecked(); + replaceOperand(0, newlhs); + } + if (newrhs != getOperand(1)) { + getOperand(1)->setFoldedUnchecked(); + replaceOperand(1, newrhs); + } + return true; + } + return false; +} + void MCompare::infer(JSContext *cx, BaselineInspector *inspector, jsbytecode *pc) { @@ -1560,19 +1580,9 @@ MCompare::infer(JSContext *cx, BaselineInspector *inspector, jsbytecode *pc) bool strictEq = jsop() == JSOP_STRICTEQ || jsop() == JSOP_STRICTNE; bool relationalEq = !(looseEq || strictEq); - // Comparisons on unsigned integers may be treated as UInt32. Skip any (x >>> 0) - // operation coercing the operands to uint32. The type policy will make sure the - // now unwrapped operand is an int32. + // Comparisons on unsigned integers may be treated as UInt32. MDefinition *newlhs, *newrhs; - if (MustBeUInt32(getOperand(0), &newlhs) && MustBeUInt32(getOperand(1), &newrhs)) { - if (newlhs != getOperand(0)) { - getOperand(0)->setFoldedUnchecked(); - replaceOperand(0, newlhs); - } - if (newrhs != getOperand(1)) { - getOperand(1)->setFoldedUnchecked(); - replaceOperand(1, newrhs); - } + if (tryUseUnsignedOperands()) { compareType_ = Compare_UInt32; return; } diff --git a/js/src/ion/MIR.h b/js/src/ion/MIR.h index d3f8b1a7205..3ad2362e2a0 100644 --- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -1706,6 +1706,11 @@ class MBinaryInstruction : public MAryInstruction<2> return (left->valueNumber() == insLeft->valueNumber()) && (right->valueNumber() == insRight->valueNumber()); } + + // Return true if the operands to this instruction are both unsigned, + // in which case any wrapping operands were replaced with the underlying + // int32 operands. + bool tryUseUnsignedOperands(); }; class MTernaryInstruction : public MAryInstruction<3> @@ -3388,12 +3393,14 @@ class MDiv : public MBinaryArithInstruction bool canBeNegativeZero_; bool canBeNegativeOverflow_; bool canBeDivideByZero_; + bool unsigned_; MDiv(MDefinition *left, MDefinition *right, MIRType type) : MBinaryArithInstruction(left, right), canBeNegativeZero_(true), canBeNegativeOverflow_(true), - canBeDivideByZero_(true) + canBeDivideByZero_(true), + unsigned_(false) { if (type != MIRType_Value) specialization_ = type; @@ -3438,14 +3445,21 @@ class MDiv : public MBinaryArithInstruction return canBeDivideByZero_; } + bool isUnsigned() { + return unsigned_; + } + bool fallible(); bool truncate(); }; class MMod : public MBinaryArithInstruction { + bool unsigned_; + MMod(MDefinition *left, MDefinition *right, MIRType type) - : MBinaryArithInstruction(left, right) + : MBinaryArithInstruction(left, right), + unsigned_(false) { if (type != MIRType_Value) specialization_ = type; @@ -3474,6 +3488,10 @@ class MMod : public MBinaryArithInstruction bool canBeDivideByZero() const; bool canBePowerOfTwoDivisor() const; + bool isUnsigned() { + return unsigned_; + } + bool fallible(); void computeRange(); diff --git a/js/src/ion/RangeAnalysis.cpp b/js/src/ion/RangeAnalysis.cpp index 98c562f500b..434461144cf 100644 --- a/js/src/ion/RangeAnalysis.cpp +++ b/js/src/ion/RangeAnalysis.cpp @@ -1586,6 +1586,13 @@ MDiv::truncate() // Remember analysis, needed to remove negative zero checks. setTruncated(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 true; + } + // No modifications. return false; } @@ -1596,6 +1603,12 @@ MMod::truncate() // Remember analysis, needed to remove negative zero checks. setTruncated(true); + // As for division, handle unsigned modulus with a truncated result. + if (specialization() == MIRType_Int32 && tryUseUnsignedOperands()) { + unsigned_ = true; + return true; + } + // No modifications. return false; } @@ -1759,7 +1772,14 @@ RangeAnalysis::truncate() // Set truncated flag if range analysis ensure that it has no // rounding errors and no fractional part. const Range *r = iter->range(); - if (!r || r->hasRoundingErrors()) + bool hasRoundingErrors = !r || r->hasRoundingErrors(); + + // Special case integer division: the result of a/b can be infinite + // but cannot actually have rounding errors induced by truncation. + if (iter->isDiv() && iter->toDiv()->specialization() == MIRType_Int32) + hasRoundingErrors = false; + + if (hasRoundingErrors) continue; // Ensure all observable uses are truncated. diff --git a/js/src/ion/arm/CodeGenerator-arm.cpp b/js/src/ion/arm/CodeGenerator-arm.cpp index fc70d5cf38f..febe10fc172 100644 --- a/js/src/ion/arm/CodeGenerator-arm.cpp +++ b/js/src/ion/arm/CodeGenerator-arm.cpp @@ -1806,7 +1806,7 @@ CodeGeneratorARM::visitAsmJSPassStackArg(LAsmJSPassStackArg *ins) bool -CodeGeneratorARM::visitAsmJSDivOrMod(LAsmJSDivOrMod *ins) +CodeGeneratorARM::visitUDivOrMod(LUDivOrMod *ins) { //Register remainder = ToRegister(ins->remainder()); Register lhs = ToRegister(ins->lhs()); diff --git a/js/src/ion/arm/CodeGenerator-arm.h b/js/src/ion/arm/CodeGenerator-arm.h index ae016c4c90b..8a9a880a15e 100644 --- a/js/src/ion/arm/CodeGenerator-arm.h +++ b/js/src/ion/arm/CodeGenerator-arm.h @@ -171,7 +171,7 @@ class CodeGeneratorARM : public CodeGeneratorShared } bool visitEffectiveAddress(LEffectiveAddress *ins); - bool visitAsmJSDivOrMod(LAsmJSDivOrMod *ins); + bool visitUDivOrMod(LUDivOrMod *ins); }; typedef CodeGeneratorARM CodeGeneratorSpecific; diff --git a/js/src/ion/arm/LIR-arm.h b/js/src/ion/arm/LIR-arm.h index 58c2ff4d4c2..73244cb8218 100644 --- a/js/src/ion/arm/LIR-arm.h +++ b/js/src/ion/arm/LIR-arm.h @@ -343,12 +343,12 @@ class LMulI : public LBinaryMath<0> // This class performs a simple x86 'div', yielding either a quotient or remainder depending on // whether this instruction is defined to output eax (quotient) or edx (remainder). -class LAsmJSDivOrMod : public LBinaryMath<2> +class LUDivOrMod : public LBinaryMath<2> { public: - LIR_HEADER(AsmJSDivOrMod); + LIR_HEADER(UDivOrMod); - LAsmJSDivOrMod(const LAllocation &lhs, const LAllocation &rhs, const LDefinition &temp1, const LDefinition &temp2) { + LUDivOrMod(const LAllocation &lhs, const LAllocation &rhs, const LDefinition &temp1, const LDefinition &temp2) { setOperand(0, lhs); setOperand(1, rhs); setTemp(0, temp1); diff --git a/js/src/ion/arm/LOpcodes-arm.h b/js/src/ion/arm/LOpcodes-arm.h index e0ac2aceb84..a7f2cc12e4c 100644 --- a/js/src/ion/arm/LOpcodes-arm.h +++ b/js/src/ion/arm/LOpcodes-arm.h @@ -20,7 +20,7 @@ _(ModMaskI) \ _(PowHalfD) \ _(UInt32ToDouble) \ - _(AsmJSDivOrMod) \ + _(UDivOrMod) \ _(AsmJSLoadFuncPtr) diff --git a/js/src/ion/arm/Lowering-arm.cpp b/js/src/ion/arm/Lowering-arm.cpp index e0d43961fc0..e045ae9d67a 100644 --- a/js/src/ion/arm/Lowering-arm.cpp +++ b/js/src/ion/arm/Lowering-arm.cpp @@ -228,6 +228,9 @@ LIRGeneratorARM::lowerForShift(LInstructionHelper<1, 2, 0> *ins, MDefinition *mi bool LIRGeneratorARM::lowerDivI(MDiv *div) { + if (div->isUnsigned()) + return lowerUDiv(div); + // Division instructions are slow. Division by constant denominators can be // rewritten to use other instructions. if (div->rhs()->isConstant()) { @@ -273,6 +276,9 @@ LIRGeneratorARM::lowerMulI(MMul *mul, MDefinition *lhs, MDefinition *rhs) bool LIRGeneratorARM::lowerModI(MMod *mod) { + if (mod->isUnsigned()) + return lowerUMod(mod); + if (mod->rhs()->isConstant()) { int32_t rhs = mod->rhs()->toConstant()->value().toInt32(); int32_t shift; @@ -430,22 +436,35 @@ LIRGeneratorARM::visitAsmJSNeg(MAsmJSNeg *ins) JS_ASSERT(ins->type() == MIRType_Double); return define(new LNegD(useRegisterAtStart(ins->input())), ins); } + +bool +LIRGeneratorARM::lowerUDiv(MInstruction *div) +{ + LUDivOrMod *lir = new LUDivOrMod(useFixed(div->getOperand(0), r0), + useFixed(div->getOperand(1), r1), + tempFixed(r2), tempFixed(r3)); + return defineFixed(lir, div, LAllocation(AnyRegister(r0))); +} + bool LIRGeneratorARM::visitAsmJSUDiv(MAsmJSUDiv *div) { - LAsmJSDivOrMod *lir = new LAsmJSDivOrMod(useFixed(div->lhs(), r0), - useFixed(div->rhs(), r1), - tempFixed(r2), tempFixed(r3)); - return defineFixed(lir, div, LAllocation(AnyRegister(r0))); + return lowerUDiv(div); +} + +bool +LIRGeneratorARM::lowerUMod(MInstruction *mod) +{ + LUDivOrMod *lir = new LUDivOrMod(useFixed(mod->getOperand(0), r0), + useFixed(mod->getOperand(1), r1), + tempFixed(r2), tempFixed(r3)); + return defineFixed(lir, mod, LAllocation(AnyRegister(r1))); } bool LIRGeneratorARM::visitAsmJSUMod(MAsmJSUMod *mod) { - LAsmJSDivOrMod *lir = new LAsmJSDivOrMod(useFixed(mod->lhs(), r0), - useFixed(mod->rhs(), r1), - tempFixed(r2), tempFixed(r3)); - return defineFixed(lir, mod, LAllocation(AnyRegister(r1))); + return lowerUMod(mod); } bool diff --git a/js/src/ion/arm/Lowering-arm.h b/js/src/ion/arm/Lowering-arm.h index 6b780766ebb..5f9c1fb3330 100644 --- a/js/src/ion/arm/Lowering-arm.h +++ b/js/src/ion/arm/Lowering-arm.h @@ -52,6 +52,8 @@ class LIRGeneratorARM : public LIRGeneratorShared bool lowerDivI(MDiv *div); bool lowerModI(MMod *mod); bool lowerMulI(MMul *mul, MDefinition *lhs, MDefinition *rhs); + bool lowerUDiv(MInstruction *div); + bool lowerUMod(MInstruction *mod); bool visitPowHalf(MPowHalf *ins); bool visitAsmJSNeg(MAsmJSNeg *ins); bool visitAsmJSUDiv(MAsmJSUDiv *ins); diff --git a/js/src/ion/shared/CodeGenerator-x86-shared.cpp b/js/src/ion/shared/CodeGenerator-x86-shared.cpp index e74d6db8e1a..064a87cfc81 100644 --- a/js/src/ion/shared/CodeGenerator-x86-shared.cpp +++ b/js/src/ion/shared/CodeGenerator-x86-shared.cpp @@ -625,7 +625,7 @@ CodeGeneratorX86Shared::visitMulI(LMulI *ins) } bool -CodeGeneratorX86Shared::visitAsmJSDivOrMod(LAsmJSDivOrMod *ins) +CodeGeneratorX86Shared::visitUDivOrMod(LUDivOrMod *ins) { JS_ASSERT(ToRegister(ins->lhs()) == eax); Register rhs = ToRegister(ins->rhs()); diff --git a/js/src/ion/shared/CodeGenerator-x86-shared.h b/js/src/ion/shared/CodeGenerator-x86-shared.h index 09d83d1e44b..8871170aa55 100644 --- a/js/src/ion/shared/CodeGenerator-x86-shared.h +++ b/js/src/ion/shared/CodeGenerator-x86-shared.h @@ -106,7 +106,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared virtual bool visitGuardObjectType(LGuardObjectType *guard); virtual bool visitGuardClass(LGuardClass *guard); virtual bool visitEffectiveAddress(LEffectiveAddress *ins); - virtual bool visitAsmJSDivOrMod(LAsmJSDivOrMod *ins); + virtual bool visitUDivOrMod(LUDivOrMod *ins); virtual bool visitAsmJSPassStackArg(LAsmJSPassStackArg *ins); bool visitNegI(LNegI *lir); diff --git a/js/src/ion/shared/LIR-x86-shared.h b/js/src/ion/shared/LIR-x86-shared.h index 6856de9d37e..3dcddfcc096 100644 --- a/js/src/ion/shared/LIR-x86-shared.h +++ b/js/src/ion/shared/LIR-x86-shared.h @@ -97,12 +97,12 @@ class LModI : public LBinaryMath<1> // This class performs a simple x86 'div', yielding either a quotient or remainder depending on // whether this instruction is defined to output eax (quotient) or edx (remainder). -class LAsmJSDivOrMod : public LBinaryMath<1> +class LUDivOrMod : public LBinaryMath<1> { public: - LIR_HEADER(AsmJSDivOrMod); + LIR_HEADER(UDivOrMod); - LAsmJSDivOrMod(const LAllocation &lhs, const LAllocation &rhs, const LDefinition &temp) { + LUDivOrMod(const LAllocation &lhs, const LAllocation &rhs, const LDefinition &temp) { setOperand(0, lhs); setOperand(1, rhs); setTemp(0, temp); diff --git a/js/src/ion/shared/Lowering-x86-shared.cpp b/js/src/ion/shared/Lowering-x86-shared.cpp index 23b67e9642d..0b9ebfe3ed9 100644 --- a/js/src/ion/shared/Lowering-x86-shared.cpp +++ b/js/src/ion/shared/Lowering-x86-shared.cpp @@ -125,6 +125,9 @@ LIRGeneratorX86Shared::lowerMulI(MMul *mul, MDefinition *lhs, MDefinition *rhs) bool LIRGeneratorX86Shared::lowerDivI(MDiv *div) { + if (div->isUnsigned()) + return lowerUDiv(div); + // Division instructions are slow. Division by constant denominators can be // rewritten to use other instructions. if (div->rhs()->isConstant()) { @@ -154,6 +157,9 @@ LIRGeneratorX86Shared::lowerDivI(MDiv *div) bool LIRGeneratorX86Shared::lowerModI(MMod *mod) { + if (mod->isUnsigned()) + return lowerUMod(mod); + if (mod->rhs()->isConstant()) { int32_t rhs = mod->rhs()->toConstant()->value().toInt32(); int32_t shift; @@ -181,22 +187,34 @@ LIRGeneratorX86Shared::visitAsmJSNeg(MAsmJSNeg *ins) return defineReuseInput(new LNegD(useRegisterAtStart(ins->input())), ins, 0); } +bool +LIRGeneratorX86Shared::lowerUDiv(MInstruction *div) +{ + LUDivOrMod *lir = new LUDivOrMod(useFixed(div->getOperand(0), eax), + useRegister(div->getOperand(1)), + tempFixed(edx)); + return defineFixed(lir, div, LAllocation(AnyRegister(eax))); +} + bool LIRGeneratorX86Shared::visitAsmJSUDiv(MAsmJSUDiv *div) { - LAsmJSDivOrMod *lir = new LAsmJSDivOrMod(useFixed(div->lhs(), eax), - useRegister(div->rhs()), - tempFixed(edx)); - return defineFixed(lir, div, LAllocation(AnyRegister(eax))); + return lowerUDiv(div); +} + +bool +LIRGeneratorX86Shared::lowerUMod(MInstruction *mod) +{ + LUDivOrMod *lir = new LUDivOrMod(useFixed(mod->getOperand(0), eax), + useRegister(mod->getOperand(1)), + LDefinition::BogusTemp()); + return defineFixed(lir, mod, LAllocation(AnyRegister(edx))); } bool LIRGeneratorX86Shared::visitAsmJSUMod(MAsmJSUMod *mod) { - LAsmJSDivOrMod *lir = new LAsmJSDivOrMod(useFixed(mod->lhs(), eax), - useRegister(mod->rhs()), - LDefinition::BogusTemp()); - return defineFixed(lir, mod, LAllocation(AnyRegister(edx))); + return lowerUMod(mod); } bool diff --git a/js/src/ion/shared/Lowering-x86-shared.h b/js/src/ion/shared/Lowering-x86-shared.h index 194d73edd14..ac0fb4f467d 100644 --- a/js/src/ion/shared/Lowering-x86-shared.h +++ b/js/src/ion/shared/Lowering-x86-shared.h @@ -41,6 +41,8 @@ class LIRGeneratorX86Shared : public LIRGeneratorShared bool lowerMulI(MMul *mul, MDefinition *lhs, MDefinition *rhs); bool lowerDivI(MDiv *div); bool lowerModI(MMod *mod); + bool lowerUDiv(MInstruction *div); + bool lowerUMod(MInstruction *mod); bool lowerUrshD(MUrsh *mir); bool lowerConstantDouble(double d, MInstruction *ins); bool lowerTruncateDToInt32(MTruncateToInt32 *ins); diff --git a/js/src/ion/x64/LOpcodes-x64.h b/js/src/ion/x64/LOpcodes-x64.h index ccf003ae731..9907c36f79f 100644 --- a/js/src/ion/x64/LOpcodes-x64.h +++ b/js/src/ion/x64/LOpcodes-x64.h @@ -18,6 +18,6 @@ _(PowHalfD) \ _(UInt32ToDouble) \ _(AsmJSLoadFuncPtr) \ - _(AsmJSDivOrMod) + _(UDivOrMod) #endif /* ion_x64_LOpcodes_x64_h */ diff --git a/js/src/ion/x86/LOpcodes-x86.h b/js/src/ion/x86/LOpcodes-x86.h index a3ecf8e20e6..b2410688911 100644 --- a/js/src/ion/x86/LOpcodes-x86.h +++ b/js/src/ion/x86/LOpcodes-x86.h @@ -19,6 +19,6 @@ _(PowHalfD) \ _(UInt32ToDouble) \ _(AsmJSLoadFuncPtr) \ - _(AsmJSDivOrMod) + _(UDivOrMod) #endif /* ion_x86_LOpcodes_x86_h */