Bug 891534 - Use asm.js ops for truncated unsigned div/mod, allow div ops to be marked as truncated, r=jandem.

This commit is contained in:
Brian Hackett 2013-07-11 17:11:04 -06:00
parent e5d69cbd84
commit e1af6a5691
16 changed files with 133 additions and 44 deletions

View File

@ -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;
}

View File

@ -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();

View File

@ -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.

View File

@ -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());

View File

@ -171,7 +171,7 @@ class CodeGeneratorARM : public CodeGeneratorShared
}
bool visitEffectiveAddress(LEffectiveAddress *ins);
bool visitAsmJSDivOrMod(LAsmJSDivOrMod *ins);
bool visitUDivOrMod(LUDivOrMod *ins);
};
typedef CodeGeneratorARM CodeGeneratorSpecific;

View File

@ -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);

View File

@ -20,7 +20,7 @@
_(ModMaskI) \
_(PowHalfD) \
_(UInt32ToDouble) \
_(AsmJSDivOrMod) \
_(UDivOrMod) \
_(AsmJSLoadFuncPtr)

View File

@ -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

View File

@ -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);

View File

@ -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());

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -18,6 +18,6 @@
_(PowHalfD) \
_(UInt32ToDouble) \
_(AsmJSLoadFuncPtr) \
_(AsmJSDivOrMod)
_(UDivOrMod)
#endif /* ion_x64_LOpcodes_x64_h */

View File

@ -19,6 +19,6 @@
_(PowHalfD) \
_(UInt32ToDouble) \
_(AsmJSLoadFuncPtr) \
_(AsmJSDivOrMod)
_(UDivOrMod)
#endif /* ion_x86_LOpcodes_x86_h */