Backed out changeset cc7311c09b56 (bug 602397) due to Maemo Talos breakage.

This commit is contained in:
Nicholas Nethercote 2011-03-10 14:31:34 -08:00
parent 76f4d326e3
commit 585033aa98
2 changed files with 191 additions and 142 deletions

View File

@ -1713,6 +1713,51 @@ fcallinfo(LIns *ins)
return ins->isop(LIR_calld) ? ins->callInfo() : NULL; return ins->isop(LIR_calld) ? ins->callInfo() : NULL;
} }
/*
* Determine whether this operand is guaranteed to not overflow the specified
* integer operation.
*/
static void
ChecksRequired(LOpcode op, LIns* op1, LIns* op2,
bool* needsOverflowCheck, bool* needsNegZeroCheck)
{
Interval x = Interval::of(op1, 3);
Interval y = Interval::of(op2, 3);
Interval z(0, 0);
switch (op) {
case LIR_addi:
z = Interval::add(x, y);
*needsNegZeroCheck = false;
break;
case LIR_subi:
z = Interval::sub(x, y);
*needsNegZeroCheck = false;
break;
case LIR_muli: {
z = Interval::mul(x, y);
// A would-be negative zero result can only occur if we have
// mul(0, -n) or mul(-n, 0), where n != 0. In particular, a multiply
// where one operand is a positive immediate cannot result in negative
// zero.
//
// This assumes that -0 cannot be an operand; if one had occurred we
// would have already exited the trace in order to promote the
// computation back to doubles.
*needsNegZeroCheck = (x.canBeZero() && y.canBeNegative()) ||
(y.canBeZero() && x.canBeNegative());
break;
}
default:
JS_NOT_REACHED("needsOverflowCheck");
}
*needsOverflowCheck = z.hasOverflowed;
}
/* /*
* JSStackFrame::numActualArgs is only defined for function frames. Since the * JSStackFrame::numActualArgs is only defined for function frames. Since the
* actual arguments of the entry frame are kept on trace, argc is included in * actual arguments of the entry frame are kept on trace, argc is included in
@ -4424,6 +4469,30 @@ TraceRecorder::guard(bool expected, LIns* cond, ExitType exitType,
return guard(expected, cond, snapshot(exitType), abortIfAlwaysExits); return guard(expected, cond, snapshot(exitType), abortIfAlwaysExits);
} }
/*
* Emit a guard a 32-bit integer arithmetic operation op(d0, d1) and
* using the supplied side exit if it overflows.
*/
JS_REQUIRES_STACK LIns*
TraceRecorder::guard_xov(LOpcode op, LIns* d0, LIns* d1, VMSideExit* exit)
{
JS_ASSERT(exit->exitType == OVERFLOW_EXIT);
GuardRecord* guardRec = createGuardRecord(exit);
switch (op) {
case LIR_addi:
return w.addxovi(d0, d1, guardRec);
case LIR_subi:
return w.subxovi(d0, d1, guardRec);
case LIR_muli:
return w.mulxovi(d0, d1, guardRec);
default:
break;
}
JS_NOT_REACHED("unexpected opcode");
return NULL;
}
JS_REQUIRES_STACK VMSideExit* JS_REQUIRES_STACK VMSideExit*
TraceRecorder::copy(VMSideExit* copy) TraceRecorder::copy(VMSideExit* copy)
{ {
@ -8320,31 +8389,17 @@ TraceRecorder::guardNonNeg(LIns* d0, LIns* d1, VMSideExit* exit)
} }
JS_REQUIRES_STACK LIns* JS_REQUIRES_STACK LIns*
TraceRecorder::tryToDemote(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1) TraceRecorder::alu(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1)
{ {
/* /*
* If the operands and result of an arithmetic operation are all integers * To even consider this operation for demotion, both operands have to be
* at record-time, and the oracle doesn't direct us otherwise, we * integers and the oracle must not give us a negative hint for the
* speculatively emit a demoted (integer) operation, betting that at * instruction.
* runtime we will get integer results again.
*
* We also have to protect against various edge cases. For example,
* to protect against overflow we emit a guard that will inform the oracle
* on overflow and cause a non-demoted trace to be attached that uses
* floating-point math for this operation; the exception to this case is
* if the operands guarantee that the result will be an integer (e.g.
* z = d0 * d1 with 0 <= (d0|d1) <= 0xffff guarantees z <= fffe0001).
*/ */
if (!oracle || oracle->isInstructionUndemotable(cx->regs->pc) || if (!oracle || oracle->isInstructionUndemotable(cx->regs->pc) ||
!IsPromotedInt32(s0) || !IsPromotedInt32(s1)) !IsPromotedInt32(s0) || !IsPromotedInt32(s1)) {
{ out:
undemotable:
if (v == LIR_modd) { if (v == LIR_modd) {
/*
* LIR_modd is a placeholder that Nanojit doesn't actually support!
* Convert it to a call.
*/
LIns* args[] = { s1, s0 }; LIns* args[] = { s1, s0 };
return w.call(&js_dmod_ci, args); return w.call(&js_dmod_ci, args);
} }
@ -8353,96 +8408,58 @@ TraceRecorder::tryToDemote(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns*
return result; return result;
} }
jsdouble r;
switch (v) {
case LIR_addd:
r = v0 + v1;
break;
case LIR_subd:
r = v0 - v1;
break;
case LIR_muld:
r = v0 * v1;
if (r == 0.0 && (v0 < 0.0 || v1 < 0.0))
goto out;
break;
#if defined NANOJIT_IA32 || defined NANOJIT_X64
case LIR_divd:
if (v1 == 0)
goto out;
r = v0 / v1;
break;
case LIR_modd:
if (v0 < 0 || v1 == 0 || (s1->isImmD() && v1 < 0))
goto out;
r = js_dmod(v0, v1);
break;
#endif
default:
goto out;
}
/*
* The result must be an integer at record time, otherwise there is no
* point in trying to demote it.
*/
if (jsint(r) != r || JSDOUBLE_IS_NEGZERO(r))
goto out;
LIns* d0 = w.demoteToInt32(s0); LIns* d0 = w.demoteToInt32(s0);
LIns* d1 = w.demoteToInt32(s1); LIns* d1 = w.demoteToInt32(s1);
jsdouble r;
/*
* Speculatively emit an integer operation, betting that at runtime we
* will get integer results again.
*/
VMSideExit* exit = NULL;
LIns* result; LIns* result;
switch (v) { switch (v) {
case LIR_addd: {
r = v0 + v1;
if (jsint(r) != r || JSDOUBLE_IS_NEGZERO(r))
goto undemotable;
Interval i0 = Interval::of(d0, 3);
Interval i1 = Interval::of(d1, 3);
result = Interval::add(i0, i1).hasOverflowed
? w.addxovi(d0, d1, createGuardRecord(snapshot(OVERFLOW_EXIT)))
: w.addi(d0, d1);
break;
}
case LIR_subd: {
r = v0 - v1;
if (jsint(r) != r || JSDOUBLE_IS_NEGZERO(r))
goto undemotable;
Interval i0 = Interval::of(d0, 3);
Interval i1 = Interval::of(d1, 3);
result = Interval::sub(i0, i1).hasOverflowed
? w.subxovi(d0, d1, createGuardRecord(snapshot(OVERFLOW_EXIT)))
: w.subi(d0, d1);
break;
}
case LIR_muld: {
r = v0 * v1;
if (jsint(r) != r || JSDOUBLE_IS_NEGZERO(r))
goto undemotable;
if (r == 0.0 && (v0 < 0.0 || v1 < 0.0))
goto undemotable;
Interval i0 = Interval::of(d0, 3);
Interval i1 = Interval::of(d1, 3);
result = Interval::mul(i0, i1).hasOverflowed
? w.mulxovi(d0, d1, createGuardRecord(snapshot(OVERFLOW_EXIT)))
: w.muli(d0, d1);
/*
* A would-be negative zero result can only occur if we have
* mul(0, -n) or mul(-n, 0), where n != 0. In particular, a multiply
* where one operand is a positive immediate cannot result in negative
* zero.
*
* This assumes that -0 cannot be an operand; if one had occurred we
* would have already exited the trace in order to promote the
* computation back to doubles.
*/
bool needsNegZeroCheck = (i0.canBeZero() && i1.canBeNegative()) ||
(i1.canBeZero() && i0.canBeNegative());
if (needsNegZeroCheck) {
/*
* Make sure we don't lose a -0. We exit if the result is zero and if
* either operand is negative. We start out using a weaker guard, checking
* if either argument is negative. If this ever fails, we recompile with
* a stronger, but slower, guard.
*/
if (v0 < 0.0 || v1 < 0.0 || oracle->isInstructionSlowZeroTest(cx->regs->pc)) {
guard(true,
w.eqi0(w.andi(w.eqi0(result),
w.ori(w.ltiN(d0, 0),
w.ltiN(d1, 0)))),
snapshot(OVERFLOW_EXIT));
} else {
guardNonNeg(d0, d1, snapshot(MUL_ZERO_EXIT));
}
}
break;
}
#if defined NANOJIT_IA32 || defined NANOJIT_X64 #if defined NANOJIT_IA32 || defined NANOJIT_X64
case LIR_divd: { case LIR_divd:
if (v1 == 0)
goto undemotable;
r = v0 / v1;
if (jsint(r) != r || JSDOUBLE_IS_NEGZERO(r))
goto undemotable;
/* Check for this case ourselves; Nanojit won't do it for us. */
if (d0->isImmI() && d1->isImmI()) if (d0->isImmI() && d1->isImmI())
return w.i2d(w.immi(jsint(r))); return w.i2d(w.immi(jsint(r)));
VMSideExit* exit = snapshot(OVERFLOW_EXIT); exit = snapshot(OVERFLOW_EXIT);
/* /*
* If the divisor is greater than zero its always safe to execute * If the divisor is greater than zero its always safe to execute
@ -8456,8 +8473,9 @@ TraceRecorder::tryToDemote(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns*
w.eqiN(d1, -1))), exit); w.eqiN(d1, -1))), exit);
w.label(mbr); w.label(mbr);
} }
} else if (d1->immI() == -1) { } else {
guard(false, w.eqiN(d0, 0x80000000), exit); if (d1->immI() == -1)
guard(false, w.eqiN(d0, 0x80000000), exit);
} }
v = LIR_divi; v = LIR_divi;
result = w.divi(d0, d1); result = w.divi(d0, d1);
@ -8467,22 +8485,13 @@ TraceRecorder::tryToDemote(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns*
/* Don't lose a -0. */ /* Don't lose a -0. */
guard(false, w.eqi0(result), exit); guard(false, w.eqi0(result), exit);
break; break;
}
case LIR_modd: { case LIR_modd: {
if (v0 < 0 || v1 == 0 || (s1->isImmD() && v1 < 0))
goto undemotable;
r = js_dmod(v0, v1);
if (jsint(r) != r || JSDOUBLE_IS_NEGZERO(r))
goto undemotable;
/* Check for this case ourselves; Nanojit won't do it for us. */
if (d0->isImmI() && d1->isImmI()) if (d0->isImmI() && d1->isImmI())
return w.i2d(w.immi(jsint(r))); return w.i2d(w.immi(jsint(r)));
VMSideExit* exit = snapshot(OVERFLOW_EXIT); exit = snapshot(OVERFLOW_EXIT);
/* Make sure we don't trigger division by zero at runtime. */ /* Make sure we don't trigger division by zero at runtime. */
if (!d1->isImmI()) if (!d1->isImmI())
@ -8490,12 +8499,12 @@ TraceRecorder::tryToDemote(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns*
v = LIR_modi; v = LIR_modi;
result = w.modi(w.divi(d0, d1)); result = w.modi(w.divi(d0, d1));
/* /* If the result is not 0, it is always within the integer domain. */
* If the result is not 0, it is always within the integer domain.
* Otherwise, we must exit if the lhs is negative since the result is
* -0 in this case, which is not in the integer domain.
*/
if (MaybeBranch mbr = w.jf(w.eqi0(result))) { if (MaybeBranch mbr = w.jf(w.eqi0(result))) {
/*
* If the result is zero, we must exit if the lhs is negative since
* the result is -0 in this case, which is not in the integer domain.
*/
guard(false, w.ltiN(d0, 0), exit); guard(false, w.ltiN(d0, 0), exit);
w.label(mbr); w.label(mbr);
} }
@ -8503,16 +8512,50 @@ TraceRecorder::tryToDemote(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns*
} }
#endif #endif
default: default:
JS_NOT_REACHED("tryToDemote"); v = arithOpcodeD2I(v);
result = NULL; JS_ASSERT(v == LIR_addi || v == LIR_muli || v == LIR_subi);
/*
* If the operands guarantee that the result will be an integer (e.g.
* z = x * y with 0 <= (x|y) <= 0xffff guarantees z <= fffe0001), we
* don't have to guard against an overflow. Otherwise we emit a guard
* that will inform the oracle and cause a non-demoted trace to be
* attached that uses floating-point math for this operation.
*/
bool needsOverflowCheck = true, needsNegZeroCheck = true;
ChecksRequired(v, d0, d1, &needsOverflowCheck, &needsNegZeroCheck);
if (needsOverflowCheck) {
exit = snapshot(OVERFLOW_EXIT);
result = guard_xov(v, d0, d1, exit);
} else {
result = w.ins2(v, d0, d1);
}
if (needsNegZeroCheck) {
JS_ASSERT(v == LIR_muli);
/*
* Make sure we don't lose a -0. We exit if the result is zero and if
* either operand is negative. We start out using a weaker guard, checking
* if either argument is negative. If this ever fails, we recompile with
* a stronger, but slower, guard.
*/
if (v0 < 0.0 || v1 < 0.0
|| !oracle || oracle->isInstructionSlowZeroTest(cx->regs->pc))
{
if (!exit)
exit = snapshot(OVERFLOW_EXIT);
guard(true,
w.eqi0(w.andi(w.eqi0(result),
w.ori(w.ltiN(d0, 0),
w.ltiN(d1, 0)))),
exit);
} else {
guardNonNeg(d0, d1, snapshot(MUL_ZERO_EXIT));
}
}
break; break;
} }
/*
* Successful demotion! Convert result to a double. This i2d will be
* removed if the result feeds into another integer or demoted operation.
*/
JS_ASSERT_IF(d0->isImmI() && d1->isImmI(), result->isImmI(jsint(r))); JS_ASSERT_IF(d0->isImmI() && d1->isImmI(), result->isImmI(jsint(r)));
return w.i2d(result); return w.i2d(result);
} }
@ -8818,7 +8861,7 @@ TraceRecorder::incHelper(const Value &v, LIns*& v_ins, Value &v_after,
AutoValueRooter tvr(cx); AutoValueRooter tvr(cx);
*tvr.addr() = v; *tvr.addr() = v;
ValueToNumber(cx, tvr.value(), &num); ValueToNumber(cx, tvr.value(), &num);
v_ins_after = tryToDemote(LIR_addd, num, incr, v_ins, w.immd(incr)); v_ins_after = alu(LIR_addd, num, incr, v_ins, w.immd(incr));
v_after.setDouble(num + incr); v_after.setDouble(num + incr);
} }
@ -9272,13 +9315,17 @@ TraceRecorder::relational(LOpcode op, bool tryBranchAfterCond)
} }
JS_REQUIRES_STACK RecordingStatus JS_REQUIRES_STACK RecordingStatus
TraceRecorder::unaryIntOp(LOpcode op) TraceRecorder::unary(LOpcode op)
{ {
Value& v = stackval(-1); Value& v = stackval(-1);
JS_ASSERT(retTypes[op] == LTy_I); bool intop = retTypes[op] == LTy_I;
if (v.isNumber()) { if (v.isNumber()) {
LIns* a = get(&v); LIns* a = get(&v);
a = w.i2d(w.ins1(op, d2i(a))); if (intop)
a = d2i(a);
a = w.ins1(op, a);
if (intop)
a = w.i2d(a);
set(&v, a); set(&v, a);
return RECORD_CONTINUE; return RECORD_CONTINUE;
} }
@ -9362,12 +9409,12 @@ TraceRecorder::binary(LOpcode op)
} }
if (leftIsNumber && rightIsNumber) { if (leftIsNumber && rightIsNumber) {
if (intop) { if (intop) {
a = (op == LIR_rshui) a = (op == LIR_rshui) ? d2u(a) : d2i(a);
? w.ui2d(w.ins2(op, d2u(a), d2i(b))) b = d2i(b);
: w.i2d(w.ins2(op, d2i(a), d2i(b)));
} else {
a = tryToDemote(op, lnum, rnum, a, b);
} }
a = alu(op, lnum, rnum, a, b);
if (intop)
a = (op == LIR_rshui) ? w.ui2d(a) : w.i2d(a);
set(&l, a); set(&l, a);
return RECORD_CONTINUE; return RECORD_CONTINUE;
} }
@ -10785,7 +10832,7 @@ TraceRecorder::record_JSOP_NOT()
JS_REQUIRES_STACK AbortableRecordingStatus JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_BITNOT() TraceRecorder::record_JSOP_BITNOT()
{ {
return InjectStatus(unaryIntOp(LIR_noti)); return InjectStatus(unary(LIR_noti));
} }
JS_REQUIRES_STACK AbortableRecordingStatus JS_REQUIRES_STACK AbortableRecordingStatus
@ -10814,7 +10861,7 @@ TraceRecorder::record_JSOP_NEG()
-v.toNumber() == (int)-v.toNumber()) -v.toNumber() == (int)-v.toNumber())
{ {
VMSideExit* exit = snapshot(OVERFLOW_EXIT); VMSideExit* exit = snapshot(OVERFLOW_EXIT);
a = w.subxovi(w.immi(0), w.demoteToInt32(a), createGuardRecord(exit)); a = guard_xov(LIR_subi, w.immi(0), w.demoteToInt32(a), exit);
if (!a->isImmI() && a->isop(LIR_subxovi)) { if (!a->isImmI() && a->isop(LIR_subxovi)) {
guard(false, w.eqiN(a, 0), exit); // make sure we don't lose a -0 guard(false, w.eqiN(a, 0), exit); // make sure we don't lose a -0
} }
@ -15707,7 +15754,7 @@ TraceRecorder::record_JSOP_ARGCNT()
// interpreter, so we have to check for that in the trace entry frame. // interpreter, so we have to check for that in the trace entry frame.
// We also have to check that arguments.length has not been mutated // We also have to check that arguments.length has not been mutated
// at record time, because if so we will generate incorrect constant // at record time, because if so we will generate incorrect constant
// LIR, which will assert in tryToDemote(). // LIR, which will assert in alu().
if (fp->hasArgsObj() && fp->argsObj().isArgsLengthOverridden()) if (fp->hasArgsObj() && fp->argsObj().isArgsLengthOverridden())
RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified"); RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified");
LIns *a_ins = getFrameObjPtr(fp->addressOfArgs()); LIns *a_ins = getFrameObjPtr(fp->addressOfArgs());

View File

@ -1199,6 +1199,8 @@ class TraceRecorder
bool abortIfAlwaysExits = false); bool abortIfAlwaysExits = false);
JS_REQUIRES_STACK RecordingStatus guard(bool expected, nanojit::LIns* cond, VMSideExit* exit, JS_REQUIRES_STACK RecordingStatus guard(bool expected, nanojit::LIns* cond, VMSideExit* exit,
bool abortIfAlwaysExits = false); bool abortIfAlwaysExits = false);
JS_REQUIRES_STACK nanojit::LIns* guard_xov(nanojit::LOpcode op, nanojit::LIns* d0,
nanojit::LIns* d1, VMSideExit* exit);
nanojit::LIns* writeBack(nanojit::LIns* i, nanojit::LIns* base, ptrdiff_t offset, nanojit::LIns* writeBack(nanojit::LIns* i, nanojit::LIns* base, ptrdiff_t offset,
bool shouldDemoteToInt32); bool shouldDemoteToInt32);
@ -1274,8 +1276,8 @@ class TraceRecorder
JS_REQUIRES_STACK void stack(int n, nanojit::LIns* i); JS_REQUIRES_STACK void stack(int n, nanojit::LIns* i);
JS_REQUIRES_STACK void guardNonNeg(nanojit::LIns* d0, nanojit::LIns* d1, VMSideExit* exit); JS_REQUIRES_STACK void guardNonNeg(nanojit::LIns* d0, nanojit::LIns* d1, VMSideExit* exit);
JS_REQUIRES_STACK nanojit::LIns* tryToDemote(nanojit::LOpcode op, jsdouble v0, jsdouble v1, JS_REQUIRES_STACK nanojit::LIns* alu(nanojit::LOpcode op, jsdouble v0, jsdouble v1,
nanojit::LIns* s0, nanojit::LIns* s1); nanojit::LIns* s0, nanojit::LIns* s1);
nanojit::LIns* d2i(nanojit::LIns* f, bool resultCanBeImpreciseIfFractional = false); nanojit::LIns* d2i(nanojit::LIns* f, bool resultCanBeImpreciseIfFractional = false);
nanojit::LIns* d2u(nanojit::LIns* d); nanojit::LIns* d2u(nanojit::LIns* d);
@ -1311,7 +1313,7 @@ class TraceRecorder
Value& rval); Value& rval);
JS_REQUIRES_STACK AbortableRecordingStatus relational(nanojit::LOpcode op, bool tryBranchAfterCond); JS_REQUIRES_STACK AbortableRecordingStatus relational(nanojit::LOpcode op, bool tryBranchAfterCond);
JS_REQUIRES_STACK RecordingStatus unaryIntOp(nanojit::LOpcode op); JS_REQUIRES_STACK RecordingStatus unary(nanojit::LOpcode op);
JS_REQUIRES_STACK RecordingStatus binary(nanojit::LOpcode op); JS_REQUIRES_STACK RecordingStatus binary(nanojit::LOpcode op);
JS_REQUIRES_STACK RecordingStatus guardShape(nanojit::LIns* obj_ins, JSObject* obj, JS_REQUIRES_STACK RecordingStatus guardShape(nanojit::LIns* obj_ins, JSObject* obj,