1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
20 * The Initial Developer of the Original Code is
21 * Brendan Eich <brendan@mozilla.org>
24 * David Anderson <danderson@mozilla.com>
25 * David Mandelin <dmandelin@mozilla.com>
26 * Sean Stangl <sstangl@mozilla.com>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 #include "jslibmath.h"
44 #include "methodjit/MethodJIT.h"
45 #include "methodjit/Compiler.h"
46 #include "methodjit/StubCalls.h"
47 #include "methodjit/FrameState-inl.h"
50 using namespace js::mjit;
53 typedef JSC::MacroAssembler::FPRegisterID FPRegisterID;
56 mjit::Compiler::tryBinaryConstantFold(JSContext *cx, FrameState &frame, JSOp op,
57 FrameEntry *lhs, FrameEntry *rhs)
59 if (!lhs->isConstant() || !rhs->isConstant())
62 const Value &L = lhs->getValue();
63 const Value &R = rhs->getValue();
65 if (!L.isPrimitive() || !R.isPrimitive() ||
66 (op == JSOP_ADD && (L.isString() || R.isString()))) {
80 needInt = (L.isInt32() && R.isInt32() &&
81 L.toInt32() >= 0 && R.toInt32() > 0);
89 JS_NOT_REACHED("NYI");
90 needInt = false; /* Silence compiler warning. */
94 double dL = 0, dR = 0;
95 int32_t nL = 0, nR = 0;
97 * We don't need to check for conversion failure, since primitive conversion
101 ValueToECMAInt32(cx, L, &nL);
102 ValueToECMAInt32(cx, R, &nR);
104 ValueToNumber(cx, L, &dL);
105 ValueToNumber(cx, R, &dR);
121 if (JSDOUBLE_IS_NaN(dR))
125 if (dL == 0 || JSDOUBLE_IS_NaN(dL))
127 else if (JSDOUBLE_IS_NEG(dL) != JSDOUBLE_IS_NEG(dR))
128 dL = cx->runtime->negativeInfinityValue.toDouble();
130 dL = cx->runtime->positiveInfinityValue.toDouble();
141 dL = js_fmod(dL, dR);
149 JS_NOT_REACHED("NYI");
165 mjit::Compiler::slowLoadConstantDouble(Assembler &masm,
166 FrameEntry *fe, FPRegisterID fpreg)
169 if (fe->getKnownType() == JSVAL_TYPE_INT32)
170 patch.d = (double)fe->getValue().toInt32();
172 patch.d = fe->getValue().toDouble();
173 patch.label = masm.loadDouble(NULL, fpreg);
174 patch.ool = &masm != &this->masm;
175 JS_ASSERT_IF(patch.ool, &masm == &stubcc.masm);
176 doubleList.append(patch);
180 mjit::Compiler::maybeJumpIfNotInt32(Assembler &masm, MaybeJump &mj, FrameEntry *fe,
181 MaybeRegisterID &mreg)
183 if (!fe->isTypeKnown()) {
185 mj.setJump(masm.testInt32(Assembler::NotEqual, mreg.reg()));
187 mj.setJump(masm.testInt32(Assembler::NotEqual, frame.addressOf(fe)));
188 } else if (fe->getKnownType() != JSVAL_TYPE_INT32) {
189 mj.setJump(masm.jump());
194 mjit::Compiler::maybeJumpIfNotDouble(Assembler &masm, MaybeJump &mj, FrameEntry *fe,
195 MaybeRegisterID &mreg)
197 if (!fe->isTypeKnown()) {
199 mj.setJump(masm.testDouble(Assembler::NotEqual, mreg.reg()));
201 mj.setJump(masm.testDouble(Assembler::NotEqual, frame.addressOf(fe)));
202 } else if (fe->getKnownType() != JSVAL_TYPE_DOUBLE) {
203 mj.setJump(masm.jump());
208 mjit::Compiler::jsop_binary(JSOp op, VoidStub stub)
210 FrameEntry *rhs = frame.peek(-1);
211 FrameEntry *lhs = frame.peek(-2);
213 if (tryBinaryConstantFold(cx, frame, op, lhs, rhs))
217 * Bail out if there are unhandled types or ops.
218 * This is temporary while ops are still being implemented.
220 if ((op == JSOP_MOD) ||
221 (lhs->isTypeKnown() && (lhs->getKnownType() > JSVAL_UPPER_INCL_TYPE_OF_NUMBER_SET)) ||
222 (rhs->isTypeKnown() && (rhs->getKnownType() > JSVAL_UPPER_INCL_TYPE_OF_NUMBER_SET))
223 #if defined(JS_CPU_ARM)
224 /* ARM cannot detect integer overflow with multiplication. */
226 #endif /* JS_CPU_ARM */
228 bool isStringResult = (op == JSOP_ADD) &&
229 (lhs->isType(JSVAL_TYPE_STRING) ||
230 rhs->isType(JSVAL_TYPE_STRING));
232 prepareStubCall(Uses(2));
233 INLINE_STUBCALL(stub);
236 frame.pushSyncedType(JSVAL_TYPE_STRING);
242 /* Can do int math iff there is no double constant and the op is not division. */
243 bool canDoIntMath = op != JSOP_DIV &&
244 !((rhs->isTypeKnown() && rhs->getKnownType() == JSVAL_TYPE_DOUBLE) ||
245 (lhs->isTypeKnown() && lhs->getKnownType() == JSVAL_TYPE_DOUBLE));
248 jsop_binary_full(lhs, rhs, op, stub);
250 jsop_binary_double(lhs, rhs, op, stub);
254 EmitDoubleOp(JSOp op, FPRegisterID fpRight, FPRegisterID fpLeft, Assembler &masm)
258 masm.addDouble(fpRight, fpLeft);
262 masm.subDouble(fpRight, fpLeft);
266 masm.mulDouble(fpRight, fpLeft);
270 masm.divDouble(fpRight, fpLeft);
274 JS_NOT_REACHED("unrecognized binary op");
279 mjit::Compiler::loadDouble(FrameEntry *fe, FPRegisterID fpReg)
283 if (fe->isConstant()) {
284 slowLoadConstantDouble(masm, fe, fpReg);
285 } else if (!fe->isTypeKnown()) {
286 frame.tempRegForType(fe);
287 Jump j = frame.testDouble(Assembler::Equal, fe);
288 notNumber = frame.testInt32(Assembler::NotEqual, fe);
289 frame.convertInt32ToDouble(masm, fe, fpReg);
290 Jump converted = masm.jump();
291 j.linkTo(masm.label(), &masm);
293 frame.loadDouble(fe, fpReg, masm);
294 converted.linkTo(masm.label(), &masm);
295 } else if (fe->getKnownType() == JSVAL_TYPE_INT32) {
296 frame.tempRegForData(fe);
297 frame.convertInt32ToDouble(masm, fe, fpReg);
299 JS_ASSERT(fe->getKnownType() == JSVAL_TYPE_DOUBLE);
300 frame.loadDouble(fe, fpReg, masm);
307 * This function emits a single fast-path for handling numerical arithmetic.
308 * Unlike jsop_binary_full(), all integers are converted to doubles.
311 mjit::Compiler::jsop_binary_double(FrameEntry *lhs, FrameEntry *rhs, JSOp op, VoidStub stub)
313 FPRegisterID fpLeft = FPRegisters::First;
314 FPRegisterID fpRight = FPRegisters::Second;
316 MaybeJump lhsNotNumber = loadDouble(lhs, fpLeft);
317 if (lhsNotNumber.isSet())
318 stubcc.linkExit(lhsNotNumber.get(), Uses(2));
320 MaybeJump rhsNotNumber;
321 if (frame.haveSameBacking(lhs, rhs)) {
322 masm.moveDouble(fpLeft, fpRight);
324 rhsNotNumber = loadDouble(rhs, fpRight);
325 if (rhsNotNumber.isSet())
326 stubcc.linkExit(rhsNotNumber.get(), Uses(2));
329 EmitDoubleOp(op, fpRight, fpLeft, masm);
334 * Try to convert result to integer. Skip this for 1/x or -1/x, as the
335 * result is unlikely to fit in an int.
337 if (op == JSOP_DIV && !(lhs->isConstant() && lhs->isType(JSVAL_TYPE_INT32) &&
338 abs(lhs->getValue().toInt32()) == 1)) {
339 RegisterID reg = frame.allocReg();
341 masm.branchConvertDoubleToInt32(fpLeft, reg, isDouble, fpRight);
343 masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), reg,
344 frame.addressOf(lhs));
347 done.setJump(masm.jump());
348 isDouble.linkTo(masm.label(), &masm);
351 masm.storeDouble(fpLeft, frame.addressOf(lhs));
354 done.getJump().linkTo(masm.label(), &masm);
356 if (lhsNotNumber.isSet() || rhsNotNumber.isSet()) {
362 frame.pushNumber(MaybeRegisterID());
364 if (lhsNotNumber.isSet() || rhsNotNumber.isSet())
365 stubcc.rejoin(Changes(1));
369 * Simpler version of jsop_binary_full() for when lhs == rhs.
372 mjit::Compiler::jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub)
374 FrameEntry *lhs = frame.peek(-2);
376 /* Easiest case: known double. Don't bother conversion back yet? */
377 if (fe->isTypeKnown() && fe->getKnownType() == JSVAL_TYPE_DOUBLE) {
378 loadDouble(fe, FPRegisters::First);
379 EmitDoubleOp(op, FPRegisters::First, FPRegisters::First, masm);
381 frame.pushNumber(MaybeRegisterID());
385 /* Allocate all registers up-front. */
386 FrameState::BinaryAlloc regs;
387 frame.allocForSameBinary(fe, op, regs);
390 MaybeJump doublePathDone;
391 if (!fe->isTypeKnown()) {
392 Jump notInt = masm.testInt32(Assembler::NotEqual, regs.lhsType.reg());
393 stubcc.linkExitDirect(notInt, stubcc.masm.label());
395 notNumber = stubcc.masm.testDouble(Assembler::NotEqual, regs.lhsType.reg());
396 frame.loadDouble(fe, FPRegisters::First, stubcc.masm);
397 EmitDoubleOp(op, FPRegisters::First, FPRegisters::First, stubcc.masm);
399 /* Force the double back to memory. */
400 Address result = frame.addressOf(lhs);
401 stubcc.masm.storeDouble(FPRegisters::First, result);
403 /* Load the payload into the result reg so the rejoin is safe. */
404 stubcc.masm.loadPayload(result, regs.result);
406 doublePathDone = stubcc.masm.jump();
409 /* Okay - good to emit the integer fast-path. */
413 overflow = masm.branchAdd32(Assembler::Overflow, regs.result, regs.result);
417 overflow = masm.branchSub32(Assembler::Overflow, regs.result, regs.result);
420 #if !defined(JS_CPU_ARM)
422 overflow = masm.branchMul32(Assembler::Overflow, regs.result, regs.result);
427 JS_NOT_REACHED("unrecognized op");
430 JS_ASSERT(overflow.isSet());
433 * Integer overflow path. Separate from the first double path, since we
434 * know never to try and convert back to integer.
436 MaybeJump overflowDone;
437 stubcc.linkExitDirect(overflow.get(), stubcc.masm.label());
439 if (regs.lhsNeedsRemat) {
440 Address address = masm.payloadOf(frame.addressForDataRemat(lhs));
441 stubcc.masm.convertInt32ToDouble(address, FPRegisters::First);
442 } else if (!lhs->isConstant()) {
443 stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), FPRegisters::First);
445 slowLoadConstantDouble(stubcc.masm, lhs, FPRegisters::First);
448 EmitDoubleOp(op, FPRegisters::First, FPRegisters::First, stubcc.masm);
450 Address address = frame.addressOf(lhs);
451 stubcc.masm.storeDouble(FPRegisters::First, address);
452 stubcc.masm.loadPayload(address, regs.result);
454 overflowDone = stubcc.masm.jump();
457 /* Slow paths funnel here. */
458 if (notNumber.isSet())
459 notNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm);
460 overflowDone.get().linkTo(stubcc.masm.label(), &stubcc.masm);
462 /* Slow call - use frame.sync to avoid erroneous jump repatching in stubcc. */
463 frame.sync(stubcc.masm, Uses(2));
467 /* Finish up stack operations. */
469 frame.pushNumber(regs.result, true);
471 /* Merge back OOL double paths. */
472 if (doublePathDone.isSet())
473 stubcc.linkRejoin(doublePathDone.get());
474 stubcc.linkRejoin(overflowDone.get());
476 stubcc.rejoin(Changes(1));
480 * This function emits multiple fast-paths for handling numerical arithmetic.
481 * Currently, it handles only ADD, SUB, and MUL, where both LHS and RHS are
482 * known not to be doubles.
484 * The control flow of the emitted code depends on which types are known.
485 * Given both types are unknown, the full spread looks like:
488 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
489 * Is LHS Int32? ------ No --------> Is LHS Double? ----- No -------,
491 * Load LHS into XMM1 |
492 * Is RHS Double? ---- Yes --, |
493 * Is RHS Int32? ---- No --|-----|
494 * Convert RHS into XMM0 | |
495 * Else <-------------------' |
497 * Load RHS into XMM0 |
498 * [Add,Sub,Mul] XMM0,XMM1 |
499 * Jump ---------------------, |
501 * Is RHS Int32? ------ No -------> Is RHS Double? ----- No --|-----|
503 * Load RHS into XMM0 | |
504 * Convert LHS into XMM1 | |
505 * [Add,Sub,Mul] XMM0,XMM1 | |
506 * Jump ---------------------| Slow Call
508 * [Add,Sub,Mul] RHS, LHS |
509 * Overflow ------ Yes -------> Convert RHS into XMM0 |
510 * Convert LHS into XMM1 |
511 * [Add,Sub,Mul] XMM0,XMM1 |
512 * Sync XMM1 to stack <---'
513 * <--------------------------------- Rejoin
516 mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, VoidStub stub)
518 if (frame.haveSameBacking(lhs, rhs)) {
519 jsop_binary_full_simple(lhs, op, stub);
523 /* Allocate all registers up-front. */
524 FrameState::BinaryAlloc regs;
525 frame.allocForBinary(lhs, rhs, op, regs);
527 /* Quick-test some invariants. */
528 JS_ASSERT_IF(lhs->isTypeKnown(), lhs->getKnownType() == JSVAL_TYPE_INT32);
529 JS_ASSERT_IF(rhs->isTypeKnown(), rhs->getKnownType() == JSVAL_TYPE_INT32);
531 FPRegisterID fpLeft = FPRegisters::First;
532 FPRegisterID fpRight = FPRegisters::Second;
534 MaybeJump lhsNotDouble, rhsNotNumber, lhsUnknownDone;
535 if (!lhs->isTypeKnown())
536 emitLeftDoublePath(lhs, rhs, regs, lhsNotDouble, rhsNotNumber, lhsUnknownDone);
538 MaybeJump rhsNotNumber2;
539 if (!rhs->isTypeKnown())
540 emitRightDoublePath(lhs, rhs, regs, rhsNotNumber2);
542 /* Perform the double addition. */
543 MaybeJump doublePathDone;
544 if (!rhs->isTypeKnown() || lhsUnknownDone.isSet()) {
545 /* If the LHS type was not known, link its path here. */
546 if (lhsUnknownDone.isSet())
547 lhsUnknownDone.get().linkTo(stubcc.masm.label(), &stubcc.masm);
549 /* Perform the double operation. */
550 EmitDoubleOp(op, fpRight, fpLeft, stubcc.masm);
552 /* Force the double back to memory. */
553 Address result = frame.addressOf(lhs);
554 stubcc.masm.storeDouble(fpLeft, result);
556 /* Load the payload into the result reg so the rejoin is safe. */
557 stubcc.masm.loadPayload(result, regs.result);
559 /* We'll link this back up later, at the bottom of the op. */
560 doublePathDone = stubcc.masm.jump();
563 /* Time to do the integer path. Figure out the immutable side. */
567 MaybeJump preOverflow;
568 if (!regs.resultHasRhs) {
569 if (!regs.rhsData.isSet())
570 value = rhs->getValue().toInt32();
572 reg = regs.rhsData.reg();
574 if (!regs.lhsData.isSet())
575 value = lhs->getValue().toInt32();
577 reg = regs.lhsData.reg();
578 if (op == JSOP_SUB) {
579 // If the RHS is 0x80000000, the smallest negative value, neg does
580 // not work. Guard against this and treat it as an overflow.
581 preOverflow = masm.branch32(Assembler::Equal, regs.result, Imm32(0x80000000));
582 masm.neg32(regs.result);
587 /* Okay - good to emit the integer fast-path. */
588 MaybeJump overflow, negZeroDone;
592 overflow = masm.branchAdd32(Assembler::Overflow, reg.reg(), regs.result);
594 overflow = masm.branchAdd32(Assembler::Overflow, Imm32(value), regs.result);
599 overflow = masm.branchSub32(Assembler::Overflow, reg.reg(), regs.result);
601 overflow = masm.branchSub32(Assembler::Overflow, Imm32(value), regs.result);
604 #if !defined(JS_CPU_ARM)
607 JS_ASSERT(reg.isSet());
609 MaybeJump storeNegZero;
610 bool maybeNegZero = true;
611 bool hasConstant = (lhs->isConstant() || rhs->isConstant());
614 value = (lhs->isConstant() ? lhs : rhs)->getValue().toInt32();
615 RegisterID nonConstReg = lhs->isConstant() ? regs.rhsData.reg() : regs.lhsData.reg();
618 maybeNegZero = false;
620 storeNegZero = masm.branchTest32(Assembler::Zero, nonConstReg);
622 storeNegZero = masm.branch32(Assembler::LessThan, nonConstReg, Imm32(0));
624 overflow = masm.branchMul32(Assembler::Overflow, reg.reg(), regs.result);
628 Jump isZero = masm.branchTest32(Assembler::Zero, regs.result);
629 stubcc.linkExitDirect(isZero, stubcc.masm.label());
631 /* Restore original value. */
632 if (regs.resultHasRhs) {
633 if (regs.rhsNeedsRemat)
634 stubcc.masm.loadPayload(frame.addressForDataRemat(rhs), regs.result);
636 stubcc.masm.move(regs.rhsData.reg(), regs.result);
638 if (regs.lhsNeedsRemat)
639 stubcc.masm.loadPayload(frame.addressForDataRemat(lhs), regs.result);
641 stubcc.masm.move(regs.lhsData.reg(), regs.result);
643 storeNegZero = stubcc.masm.branchOr32(Assembler::Signed, reg.reg(), regs.result);
644 stubcc.masm.xor32(regs.result, regs.result);
645 stubcc.crossJump(stubcc.masm.jump(), masm.label());
646 storeNegZero.getJump().linkTo(stubcc.masm.label(), &stubcc.masm);
648 JS_ASSERT(storeNegZero.isSet());
649 stubcc.linkExitDirect(storeNegZero.get(), stubcc.masm.label());
651 stubcc.masm.storeValue(DoubleValue(-0.0), frame.addressOf(lhs));
652 stubcc.masm.loadPayload(frame.addressOf(lhs), regs.result);
653 negZeroDone = stubcc.masm.jump();
660 JS_NOT_REACHED("unrecognized op");
664 JS_ASSERT(overflow.isSet());
667 * Integer overflow path. Separate from the first double path, since we
668 * know never to try and convert back to integer.
670 MaybeJump overflowDone;
671 if (preOverflow.isSet())
672 stubcc.linkExitDirect(preOverflow.get(), stubcc.masm.label());
673 stubcc.linkExitDirect(overflow.get(), stubcc.masm.label());
675 if (regs.lhsNeedsRemat) {
676 Address address = masm.payloadOf(frame.addressForDataRemat(lhs));
677 stubcc.masm.convertInt32ToDouble(address, fpLeft);
678 } else if (!lhs->isConstant()) {
679 stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), fpLeft);
681 slowLoadConstantDouble(stubcc.masm, lhs, fpLeft);
684 if (regs.rhsNeedsRemat) {
685 Address address = masm.payloadOf(frame.addressForDataRemat(rhs));
686 stubcc.masm.convertInt32ToDouble(address, fpRight);
687 } else if (!rhs->isConstant()) {
688 stubcc.masm.convertInt32ToDouble(regs.rhsData.reg(), fpRight);
690 slowLoadConstantDouble(stubcc.masm, rhs, fpRight);
693 EmitDoubleOp(op, fpRight, fpLeft, stubcc.masm);
695 Address address = frame.addressOf(lhs);
696 stubcc.masm.storeDouble(fpLeft, address);
697 stubcc.masm.loadPayload(address, regs.result);
699 overflowDone = stubcc.masm.jump();
702 /* The register allocator creates at most one temporary. */
703 if (regs.extraFree.isSet())
704 frame.freeReg(regs.extraFree.reg());
706 /* Slow paths funnel here. */
707 if (lhsNotDouble.isSet()) {
708 lhsNotDouble.get().linkTo(stubcc.masm.label(), &stubcc.masm);
709 if (rhsNotNumber.isSet())
710 rhsNotNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm);
712 if (rhsNotNumber2.isSet())
713 rhsNotNumber2.get().linkTo(stubcc.masm.label(), &stubcc.masm);
715 /* Slow call - use frame.sync to avoid erroneous jump repatching in stubcc. */
716 frame.sync(stubcc.masm, Uses(2));
720 /* Finish up stack operations. */
722 frame.pushNumber(regs.result, true);
724 /* Merge back OOL double paths. */
725 if (doublePathDone.isSet())
726 stubcc.linkRejoin(doublePathDone.get());
727 if (negZeroDone.isSet())
728 stubcc.linkRejoin(negZeroDone.get());
729 stubcc.linkRejoin(overflowDone.get());
731 stubcc.rejoin(Changes(1));
734 static const uint64 DoubleNegMask = 0x8000000000000000ULL;
737 mjit::Compiler::jsop_neg()
739 FrameEntry *fe = frame.peek(-1);
741 if (fe->isTypeKnown() && fe->getKnownType() > JSVAL_UPPER_INCL_TYPE_OF_NUMBER_SET) {
742 prepareStubCall(Uses(1));
743 INLINE_STUBCALL(stubs::Neg);
749 JS_ASSERT(!fe->isConstant());
751 /* Load type information into register. */
752 MaybeRegisterID feTypeReg;
753 if (!fe->isTypeKnown() && !frame.shouldAvoidTypeRemat(fe)) {
754 /* Safe because only one type is loaded. */
755 feTypeReg.setReg(frame.tempRegForType(fe));
757 /* Don't get clobbered by copyDataIntoReg(). */
758 frame.pinReg(feTypeReg.reg());
761 RegisterID reg = frame.copyDataIntoReg(masm, fe);
762 Label feSyncTarget = stubcc.syncExitAndJump(Uses(1));
764 /* Try a double path (inline). */
767 maybeJumpIfNotDouble(masm, jmpNotDbl, fe, feTypeReg);
769 FPRegisterID fpreg = frame.copyEntryIntoFPReg(fe, FPRegisters::First);
771 #if defined JS_CPU_X86 || defined JS_CPU_X64
772 masm.loadDouble(&DoubleNegMask, FPRegisters::Second);
773 masm.xorDouble(FPRegisters::Second, fpreg);
774 #elif defined JS_CPU_ARM
775 masm.negDouble(fpreg, fpreg);
778 /* Overwrite pushed frame's memory (before push). */
779 masm.storeDouble(fpreg, frame.addressOf(fe));
782 /* Try an integer path (out-of-line). */
784 MaybeJump jmpIntZero;
786 MaybeJump jmpIntRejoin;
787 Label lblIntPath = stubcc.masm.label();
789 maybeJumpIfNotInt32(stubcc.masm, jmpNotInt, fe, feTypeReg);
791 /* 0 (int) -> -0 (double). */
792 jmpIntZero.setJump(stubcc.masm.branch32(Assembler::Equal, reg, Imm32(0)));
793 /* int32 negation on (-2147483648) yields (-2147483648). */
794 jmpMinInt.setJump(stubcc.masm.branch32(Assembler::Equal, reg, Imm32(1 << 31)));
796 stubcc.masm.neg32(reg);
798 /* Sync back with double path. */
799 stubcc.masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), reg,
800 frame.addressOf(fe));
802 jmpIntRejoin.setJump(stubcc.masm.jump());
806 if (feTypeReg.isSet())
807 frame.unpinReg(feTypeReg.reg());
810 OOL_STUBCALL(stubs::Neg);
816 if (jmpNotDbl.isSet())
817 stubcc.linkExitDirect(jmpNotDbl.getJump(), lblIntPath);
819 if (jmpNotInt.isSet())
820 jmpNotInt.getJump().linkTo(feSyncTarget, &stubcc.masm);
821 if (jmpIntZero.isSet())
822 jmpIntZero.getJump().linkTo(feSyncTarget, &stubcc.masm);
823 if (jmpMinInt.isSet())
824 jmpMinInt.getJump().linkTo(feSyncTarget, &stubcc.masm);
825 if (jmpIntRejoin.isSet())
826 stubcc.crossJump(jmpIntRejoin.getJump(), masm.label());
828 stubcc.rejoin(Changes(1));
832 mjit::Compiler::jsop_mod()
834 #if defined(JS_CPU_X86)
835 FrameEntry *lhs = frame.peek(-2);
836 FrameEntry *rhs = frame.peek(-1);
838 if (tryBinaryConstantFold(cx, frame, JSOP_MOD, lhs, rhs))
841 if ((lhs->isTypeKnown() && lhs->getKnownType() != JSVAL_TYPE_INT32) ||
842 (rhs->isTypeKnown() && rhs->getKnownType() != JSVAL_TYPE_INT32))
845 prepareStubCall(Uses(2));
846 INLINE_STUBCALL(stubs::Mod);
852 #if defined(JS_CPU_X86)
853 if (!lhs->isTypeKnown()) {
854 Jump j = frame.testInt32(Assembler::NotEqual, lhs);
855 stubcc.linkExit(j, Uses(2));
857 if (!rhs->isTypeKnown()) {
858 Jump j = frame.testInt32(Assembler::NotEqual, rhs);
859 stubcc.linkExit(j, Uses(2));
862 /* LHS must be in EAX:EDX */
863 if (!lhs->isConstant()) {
864 frame.copyDataIntoReg(lhs, X86Registers::eax);
866 frame.takeReg(X86Registers::eax);
867 masm.move(Imm32(lhs->getValue().toInt32()), X86Registers::eax);
870 /* Get RHS into anything but EDX - could avoid more spilling? */
871 MaybeRegisterID temp;
873 if (!rhs->isConstant()) {
874 uint32 mask = Registers::AvailRegs & ~Registers::maskReg(X86Registers::edx);
875 rhsReg = frame.tempRegInMaskForData(rhs, mask);
876 JS_ASSERT(rhsReg != X86Registers::edx);
878 rhsReg = frame.allocReg(Registers::AvailRegs & ~Registers::maskReg(X86Registers::edx));
879 JS_ASSERT(rhsReg != X86Registers::edx);
880 masm.move(Imm32(rhs->getValue().toInt32()), rhsReg);
883 frame.takeReg(X86Registers::edx);
884 frame.freeReg(X86Registers::eax);
887 frame.freeReg(temp.reg());
889 bool slowPath = !(lhs->isTypeKnown() && rhs->isTypeKnown());
890 if (rhs->isConstant() && rhs->getValue().toInt32() != 0) {
891 if (rhs->getValue().toInt32() == -1) {
892 /* Guard against -1 / INT_MIN which throws a hardware exception. */
893 Jump checkDivExc = masm.branch32(Assembler::Equal, X86Registers::eax,
895 stubcc.linkExit(checkDivExc, Uses(2));
899 Jump checkDivExc = masm.branch32(Assembler::Equal, X86Registers::eax, Imm32(0x80000000));
900 stubcc.linkExit(checkDivExc, Uses(2));
901 Jump checkZero = masm.branchTest32(Assembler::Zero, rhsReg, rhsReg);
902 stubcc.linkExit(checkZero, Uses(2));
906 /* Perform division. */
909 /* ECMA-262 11.5.3 requires the result to have the same sign as the lhs.
910 * Thus, if the remainder of the div instruction is zero and the lhs is
911 * negative, we must return negative 0. */
913 bool lhsMaybeNeg = true;
914 bool lhsIsNeg = false;
915 if (lhs->isConstant()) {
916 /* This condition is established at the top of this function. */
917 JS_ASSERT(lhs->getValue().isInt32());
918 lhsMaybeNeg = lhsIsNeg = (lhs->getValue().toInt32() < 0);
923 MaybeRegisterID lhsData;
925 lhsData = frame.tempRegForData(lhs);
926 Jump negZero1 = masm.branchTest32(Assembler::NonZero, X86Registers::edx);
929 negZero2 = masm.branchTest32(Assembler::Zero, lhsData.reg(), Imm32(0x80000000));
930 /* Darn, negative 0. */
931 masm.storeValue(DoubleValue(-0.0), frame.addressOf(lhs));
933 /* :TODO: This is wrong, must load into EDX as well. */
936 negZero1.linkTo(masm.label(), &masm);
937 if (negZero2.isSet())
938 negZero2.getJump().linkTo(masm.label(), &masm);
941 /* Better - integer. */
942 masm.storeTypeTag(ImmType(JSVAL_TYPE_INT32), frame.addressOf(lhs));
945 done.getJump().linkTo(masm.label(), &masm);
949 OOL_STUBCALL(stubs::Mod);
953 frame.pushNumber(X86Registers::edx);
956 stubcc.rejoin(Changes(1));
961 mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused)
963 FrameEntry *rhs = frame.peek(-1);
964 FrameEntry *lhs = frame.peek(-2);
966 /* Swap the LHS and RHS if it makes register allocation better... or possible. */
967 if (lhs->isConstant() ||
968 (frame.shouldAvoidDataRemat(lhs) && !rhs->isConstant())) {
969 FrameEntry *temp = rhs;
974 bool lhsInt = lhs->isType(JSVAL_TYPE_INT32);
975 bool rhsInt = rhs->isType(JSVAL_TYPE_INT32);
977 /* Invert the condition if fusing with an IFEQ branch. */
978 bool flipCondition = (target && fused == JSOP_IFEQ);
980 /* Get the condition being tested. */
981 Assembler::Condition cond;
984 cond = flipCondition ? Assembler::NotEqual : Assembler::Equal;
987 cond = flipCondition ? Assembler::Equal : Assembler::NotEqual;
990 JS_NOT_REACHED("wat");
995 Value rval = UndefinedValue(); /* quiet gcc warning */
996 bool rhsConst = false;
997 if (rhs->isConstant()) {
999 rval = rhs->getValue();
1002 ValueRemat lvr, rvr;
1003 frame.pinEntry(lhs, lvr);
1004 frame.pinEntry(rhs, rvr);
1007 * Sync everything except the top two entries.
1008 * We will handle the lhs/rhs in the stub call path.
1010 frame.syncAndKill(Registers(Registers::AvailRegs), Uses(frame.frameSlots()), Uses(2));
1012 RegisterID tempReg = frame.allocReg();
1016 frame.discardFrame();
1018 JaegerSpew(JSpew_Insns, " ---- BEGIN STUB CALL CODE ---- \n");
1020 RESERVE_OOL_SPACE(stubcc.masm);
1022 /* Start of the slow path for equality stub call. */
1023 Label stubEntry = stubcc.masm.label();
1025 /* The lhs/rhs need to be synced in the stub call path. */
1026 frame.ensureValueSynced(stubcc.masm, lhs, lvr);
1027 frame.ensureValueSynced(stubcc.masm, rhs, rvr);
1029 bool needStub = true;
1035 ic.tempReg = tempReg;
1038 ic.stubEntry = stubEntry;
1041 bool useIC = !addTraceHints || target >= PC;
1043 /* Call the IC stub, which may generate a fast path. */
1045 /* Adjust for the two values just pushed. */
1046 ic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1);
1047 ic.stubCall = OOL_STUBCALL_LOCAL_SLOTS(ic::Equality,
1048 frame.stackDepth() + script->nfixed + 2);
1054 OOL_STUBCALL_LOCAL_SLOTS(stub, frame.stackDepth() + script->nfixed + 2);
1057 * The stub call has no need to rejoin, since state is synced.
1058 * Instead, we can just test the return value.
1060 Assembler::Condition ncond = (fused == JSOP_IFEQ)
1062 : Assembler::NonZero;
1064 stubcc.masm.branchTest32(ncond, Registers::ReturnReg, Registers::ReturnReg);
1065 Jump stubFallthrough = stubcc.masm.jump();
1067 JaegerSpew(JSpew_Insns, " ---- END STUB CALL CODE ---- \n");
1071 MaybeJump firstStubJump;
1073 if ((!lhs->isTypeKnown() || lhsInt) && (!rhs->isTypeKnown() || rhsInt)) {
1075 Jump lhsFail = masm.testInt32(Assembler::NotEqual, lvr.typeReg());
1076 stubcc.linkExitDirect(lhsFail, stubEntry);
1077 firstStubJump = lhsFail;
1080 Jump rhsFail = masm.testInt32(Assembler::NotEqual, rvr.typeReg());
1081 stubcc.linkExitDirect(rhsFail, stubEntry);
1082 if (!firstStubJump.isSet())
1083 firstStubJump = rhsFail;
1087 fast = masm.branch32(cond, lvr.dataReg(), Imm32(rval.toInt32()));
1089 fast = masm.branch32(cond, lvr.dataReg(), rvr.dataReg());
1091 if (!jumpInScript(fast, target))
1094 Jump j = masm.jump();
1095 stubcc.linkExitDirect(j, stubEntry);
1098 /* This is just a dummy jump. */
1103 ic.jumpToStub = firstStubJump;
1105 ic.fallThrough = masm.label();
1106 ic.jumpTarget = target;
1107 equalityICs.append(ic);
1111 /* Jump from the stub call fallthrough to here. */
1112 stubcc.crossJump(stubFallthrough, masm.label());
1115 * NB: jumpAndTrace emits to the OOL path, so make sure not to use it
1116 * in the middle of an in-progress slow path.
1118 if (!jumpAndTrace(fast, target, &stubBranch))
1121 /* No fusing. Compare, set, and push a boolean. */
1123 /* Should have filtered these out in the caller. */
1124 JS_ASSERT(!lhs->isType(JSVAL_TYPE_STRING) && !rhs->isType(JSVAL_TYPE_STRING));
1126 /* Test the types. */
1127 if ((lhs->isTypeKnown() && !lhsInt) || (rhs->isTypeKnown() && !rhsInt)) {
1128 stubcc.linkExit(masm.jump(), Uses(2));
1131 Jump lhsFail = frame.testInt32(Assembler::NotEqual, lhs);
1132 stubcc.linkExit(lhsFail, Uses(2));
1135 Jump rhsFail = frame.testInt32(Assembler::NotEqual, rhs);
1136 stubcc.linkExit(rhsFail, Uses(2));
1143 RegisterID reg = frame.ownRegForData(lhs);
1145 /* x86/64's SET instruction can only take single-byte regs.*/
1146 RegisterID resultReg = reg;
1147 if (!(Registers::maskReg(reg) & Registers::SingleByteRegs))
1148 resultReg = frame.allocReg(Registers::SingleByteRegs);
1150 /* Emit the compare & set. */
1151 if (rhs->isConstant()) {
1152 masm.set32(cond, reg, Imm32(rhs->getValue().toInt32()), resultReg);
1153 } else if (frame.shouldAvoidDataRemat(rhs)) {
1154 masm.set32(cond, reg,
1155 masm.payloadOf(frame.addressOf(rhs)),
1158 masm.set32(cond, reg, frame.tempRegForData(rhs), resultReg);
1161 /* Clean up and push a boolean. */
1164 if (reg != resultReg)
1166 frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, resultReg);
1167 stubcc.rejoin(Changes(1));
1173 * Emit an OOL path for a possibly double LHS, and possibly int32 or number RHS.
1176 mjit::Compiler::emitLeftDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState::BinaryAlloc ®s,
1177 MaybeJump &lhsNotDouble, MaybeJump &rhsNotNumber,
1178 MaybeJump &lhsUnknownDone)
1180 FPRegisterID fpLeft = FPRegisters::First;
1181 FPRegisterID fpRight = FPRegisters::Second;
1183 /* If the LHS is not a 32-bit integer, take OOL path. */
1184 Jump lhsNotInt32 = masm.testInt32(Assembler::NotEqual, regs.lhsType.reg());
1185 stubcc.linkExitDirect(lhsNotInt32, stubcc.masm.label());
1187 /* OOL path for LHS as a double - first test LHS is double. */
1188 lhsNotDouble = stubcc.masm.testDouble(Assembler::NotEqual, regs.lhsType.reg());
1190 /* Ensure the RHS is a number. */
1191 MaybeJump rhsIsDouble;
1192 if (!rhs->isTypeKnown()) {
1193 rhsIsDouble = stubcc.masm.testDouble(Assembler::Equal, regs.rhsType.reg());
1194 rhsNotNumber = stubcc.masm.testInt32(Assembler::NotEqual, regs.rhsType.reg());
1197 /* If RHS is constant, convert now. */
1198 if (rhs->isConstant())
1199 slowLoadConstantDouble(stubcc.masm, rhs, fpRight);
1201 stubcc.masm.convertInt32ToDouble(regs.rhsData.reg(), fpRight);
1203 if (!rhs->isTypeKnown()) {
1204 /* Jump past double load, bind double type check. */
1205 Jump converted = stubcc.masm.jump();
1206 rhsIsDouble.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1208 /* Load the double. */
1209 frame.loadDouble(regs.rhsType.reg(), regs.rhsData.reg(),
1210 rhs, fpRight, stubcc.masm);
1212 converted.linkTo(stubcc.masm.label(), &stubcc.masm);
1216 frame.loadDouble(regs.lhsType.reg(), regs.lhsData.reg(),
1217 lhs, fpLeft, stubcc.masm);
1218 lhsUnknownDone = stubcc.masm.jump();
1222 * Emit an OOL path for an integer LHS, possibly double RHS.
1225 mjit::Compiler::emitRightDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState::BinaryAlloc ®s,
1226 MaybeJump &rhsNotNumber2)
1228 FPRegisterID fpLeft = FPRegisters::First;
1229 FPRegisterID fpRight = FPRegisters::Second;
1231 /* If the RHS is not a double, take OOL path. */
1232 Jump notInt32 = masm.testInt32(Assembler::NotEqual, regs.rhsType.reg());
1233 stubcc.linkExitDirect(notInt32, stubcc.masm.label());
1235 /* Now test if RHS is a double. */
1236 rhsNotNumber2 = stubcc.masm.testDouble(Assembler::NotEqual, regs.rhsType.reg());
1238 /* We know LHS is an integer. */
1239 if (lhs->isConstant())
1240 slowLoadConstantDouble(stubcc.masm, lhs, fpLeft);
1242 stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), fpLeft);
1245 frame.loadDouble(regs.rhsType.reg(), regs.rhsData.reg(),
1246 rhs, fpRight, stubcc.masm);
1249 static inline Assembler::DoubleCondition
1250 DoubleCondForOp(JSOp op, JSOp fused)
1252 bool ifeq = fused == JSOP_IFEQ;
1256 ? Assembler::DoubleLessThanOrEqualOrUnordered
1257 : Assembler::DoubleGreaterThan;
1260 ? Assembler::DoubleLessThanOrUnordered
1261 : Assembler::DoubleGreaterThanOrEqual;
1264 ? Assembler::DoubleGreaterThanOrEqualOrUnordered
1265 : Assembler::DoubleLessThan;
1268 ? Assembler::DoubleGreaterThanOrUnordered
1269 : Assembler::DoubleLessThanOrEqual;
1271 JS_NOT_REACHED("unrecognized op");
1272 return Assembler::DoubleLessThan;
1277 mjit::Compiler::jsop_relational_double(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused)
1279 FrameEntry *rhs = frame.peek(-1);
1280 FrameEntry *lhs = frame.peek(-2);
1282 FPRegisterID fpLeft = FPRegisters::First;
1283 FPRegisterID fpRight = FPRegisters::Second;
1285 JS_ASSERT_IF(!target, fused != JSOP_IFEQ);
1287 MaybeJump lhsNotNumber = loadDouble(lhs, fpLeft);
1288 MaybeJump rhsNotNumber = loadDouble(rhs, fpRight);
1290 Assembler::DoubleCondition dblCond = DoubleCondForOp(op, fused);
1293 if (lhsNotNumber.isSet())
1294 stubcc.linkExitForBranch(lhsNotNumber.get());
1295 if (rhsNotNumber.isSet())
1296 stubcc.linkExitForBranch(rhsNotNumber.get());
1301 frame.syncAndForgetEverything();
1303 Jump j = masm.branchDouble(dblCond, fpLeft, fpRight);
1306 * The stub call has no need to rejoin since the state is synced.
1307 * Instead, we can just test the return value.
1309 Assembler::Condition cond = (fused == JSOP_IFEQ)
1311 : Assembler::NonZero;
1312 Jump sj = stubcc.masm.branchTest32(cond, Registers::ReturnReg, Registers::ReturnReg);
1314 /* Rejoin from the slow path. */
1315 Jump j2 = stubcc.masm.jump();
1316 stubcc.crossJump(j2, masm.label());
1319 * NB: jumpAndTrace emits to the OOL path, so make sure not to use it
1320 * in the middle of an in-progress slow path.
1322 if (!jumpAndTrace(j, target, &sj))
1325 if (lhsNotNumber.isSet())
1326 stubcc.linkExit(lhsNotNumber.get(), Uses(2));
1327 if (rhsNotNumber.isSet())
1328 stubcc.linkExit(rhsNotNumber.get(), Uses(2));
1334 RegisterID reg = frame.allocReg();
1335 Jump j = masm.branchDouble(dblCond, fpLeft, fpRight);
1336 masm.move(Imm32(0), reg);
1337 Jump skip = masm.jump();
1338 j.linkTo(masm.label(), &masm);
1339 masm.move(Imm32(1), reg);
1340 skip.linkTo(masm.label(), &masm);
1342 frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, reg);
1344 stubcc.rejoin(Changes(1));
1350 mjit::Compiler::jsop_relational_self(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused)
1353 FrameEntry *rhs = frame.peek(-1);
1354 FrameEntry *lhs = frame.peek(-2);
1356 JS_ASSERT(frame.haveSameBacking(lhs, rhs));
1359 /* :TODO: optimize this? */
1360 return emitStubCmpOp(stub, target, fused);
1363 /* See jsop_binary_full() for more information on how this works. */
1365 mjit::Compiler::jsop_relational_full(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused)
1367 FrameEntry *rhs = frame.peek(-1);
1368 FrameEntry *lhs = frame.peek(-2);
1370 /* Allocate all registers up-front. */
1371 FrameState::BinaryAlloc regs;
1372 frame.allocForBinary(lhs, rhs, op, regs, !target);
1374 FPRegisterID fpLeft = FPRegisters::First;
1375 FPRegisterID fpRight = FPRegisters::Second;
1377 MaybeJump lhsNotDouble, rhsNotNumber, lhsUnknownDone;
1378 if (!lhs->isTypeKnown())
1379 emitLeftDoublePath(lhs, rhs, regs, lhsNotDouble, rhsNotNumber, lhsUnknownDone);
1381 MaybeJump rhsNotNumber2;
1382 if (!rhs->isTypeKnown())
1383 emitRightDoublePath(lhs, rhs, regs, rhsNotNumber2);
1385 /* Both double paths will join here. */
1386 bool hasDoublePath = false;
1387 if (!rhs->isTypeKnown() || lhsUnknownDone.isSet())
1388 hasDoublePath = true;
1390 /* Integer path - figure out the immutable side. */
1394 MaybeRegisterID reg;
1395 if (regs.lhsData.isSet()) {
1396 cmpReg = regs.lhsData.reg();
1397 if (!regs.rhsData.isSet())
1398 value = rhs->getValue().toInt32();
1400 reg = regs.rhsData.reg();
1402 cmpReg = regs.rhsData.reg();
1403 value = lhs->getValue().toInt32();
1418 JS_NOT_REACHED("unrecognized op");
1424 * Emit the actual comparisons. When a fusion is in play, it's faster to
1425 * combine the comparison with the jump, so these two cases are implemented
1431 * Emit the double path now, necessary to complete the OOL fast-path
1432 * before emitting the slow path.
1434 * Note: doubles have not been swapped yet. Use original op.
1436 MaybeJump doubleTest, doubleFall;
1437 Assembler::DoubleCondition dblCond = DoubleCondForOp(op, fused);
1438 if (hasDoublePath) {
1439 if (lhsUnknownDone.isSet())
1440 lhsUnknownDone.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1441 frame.sync(stubcc.masm, Uses(frame.frameSlots()));
1442 doubleTest = stubcc.masm.branchDouble(dblCond, fpLeft, fpRight);
1443 doubleFall = stubcc.masm.jump();
1445 /* Link all incoming slow paths to here. */
1446 if (lhsNotDouble.isSet()) {
1447 lhsNotDouble.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1448 if (rhsNotNumber.isSet())
1449 rhsNotNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1451 if (rhsNotNumber2.isSet())
1452 rhsNotNumber2.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1455 * For fusions, spill the tracker state. xmm* remain intact. Note
1456 * that frame.sync() must be used directly, to avoid syncExit()'s
1459 frame.sync(stubcc.masm, Uses(frame.frameSlots()));
1464 /* Forget the world, preserving data. */
1465 frame.pinReg(cmpReg);
1467 frame.pinReg(reg.reg());
1471 frame.syncAndKillEverything();
1472 frame.unpinKilledReg(cmpReg);
1474 frame.unpinKilledReg(reg.reg());
1475 frame.syncAndForgetEverything();
1477 /* Operands could have been reordered, so use cmpOp. */
1478 Assembler::Condition i32Cond;
1479 bool ifeq = fused == JSOP_IFEQ;
1482 i32Cond = ifeq ? Assembler::LessThanOrEqual : Assembler::GreaterThan;
1485 i32Cond = ifeq ? Assembler::LessThan : Assembler::GreaterThanOrEqual;
1488 i32Cond = ifeq ? Assembler::GreaterThanOrEqual : Assembler::LessThan;
1491 i32Cond = ifeq ? Assembler::GreaterThan : Assembler::LessThanOrEqual;
1494 JS_NOT_REACHED("unrecognized op");
1498 /* Emit the i32 path. */
1501 fast = masm.branch32(i32Cond, cmpReg, reg.reg());
1503 fast = masm.branch32(i32Cond, cmpReg, Imm32(value));
1506 * The stub call has no need to rejoin since state is synced. Instead,
1507 * we can just test the return value.
1509 Assembler::Condition cond = (fused == JSOP_IFEQ)
1511 : Assembler::NonZero;
1512 Jump j = stubcc.masm.branchTest32(cond, Registers::ReturnReg, Registers::ReturnReg);
1514 /* Rejoin from the slow path. */
1515 Jump j2 = stubcc.masm.jump();
1516 stubcc.crossJump(j2, masm.label());
1518 /* :TODO: make double path invoke tracer. */
1519 if (hasDoublePath) {
1520 j.linkTo(stubcc.masm.label(), &stubcc.masm);
1521 doubleTest.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1522 j = stubcc.masm.jump();
1526 * NB: jumpAndTrace emits to the OOL path, so make sure not to use it
1527 * in the middle of an in-progress slow path.
1529 if (!jumpAndTrace(fast, target, &j))
1532 /* Rejoin from the double path. */
1534 stubcc.crossJump(doubleFall.get(), masm.label());
1537 * Emit the double path now, necessary to complete the OOL fast-path
1538 * before emitting the slow path.
1540 MaybeJump doubleDone;
1541 Assembler::DoubleCondition dblCond = DoubleCondForOp(op, JSOP_NOP);
1542 if (hasDoublePath) {
1543 if (lhsUnknownDone.isSet())
1544 lhsUnknownDone.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1545 /* :FIXME: Use SET if we can? */
1546 Jump test = stubcc.masm.branchDouble(dblCond, fpLeft, fpRight);
1547 stubcc.masm.move(Imm32(0), regs.result);
1548 Jump skip = stubcc.masm.jump();
1549 test.linkTo(stubcc.masm.label(), &stubcc.masm);
1550 stubcc.masm.move(Imm32(1), regs.result);
1551 skip.linkTo(stubcc.masm.label(), &stubcc.masm);
1552 doubleDone = stubcc.masm.jump();
1554 /* Link all incoming slow paths to here. */
1555 if (lhsNotDouble.isSet()) {
1556 lhsNotDouble.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1557 if (rhsNotNumber.isSet())
1558 rhsNotNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1560 if (rhsNotNumber2.isSet())
1561 rhsNotNumber2.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1563 /* Emit the slow path - note full frame syncage. */
1564 frame.sync(stubcc.masm, Uses(2));
1569 /* Get an integer comparison condition. */
1570 Assembler::Condition i32Cond;
1573 i32Cond = Assembler::GreaterThan;
1576 i32Cond = Assembler::GreaterThanOrEqual;
1579 i32Cond = Assembler::LessThan;
1582 i32Cond = Assembler::LessThanOrEqual;
1585 JS_NOT_REACHED("unrecognized op");
1589 /* Emit the compare & set. */
1590 if (Registers::maskReg(regs.result) & Registers::SingleByteRegs) {
1592 masm.set32(i32Cond, cmpReg, reg.reg(), regs.result);
1594 masm.set32(i32Cond, cmpReg, Imm32(value), regs.result);
1598 j = masm.branch32(i32Cond, cmpReg, reg.reg());
1600 j = masm.branch32(i32Cond, cmpReg, Imm32(value));
1601 masm.move(Imm32(0), regs.result);
1602 Jump skip = masm.jump();
1603 j.linkTo(masm.label(), &masm);
1604 masm.move(Imm32(1), regs.result);
1605 skip.linkTo(masm.label(), &masm);
1609 frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, regs.result);
1612 stubcc.crossJump(doubleDone.get(), masm.label());
1613 stubcc.rejoin(Changes(1));