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