CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / js / src / methodjit / FastArithmetic.cpp
blob653581d63ed9107dc0eed6ee9fd499734760021a
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
15 * License.
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 * May 28, 2008.
20 * The Initial Developer of the Original Code is
21 * Brendan Eich <brendan@mozilla.org>
23 * Contributor(s):
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 ***** */
41 #include "jsbool.h"
42 #include "jslibmath.h"
43 #include "jsnum.h"
44 #include "methodjit/MethodJIT.h"
45 #include "methodjit/Compiler.h"
46 #include "methodjit/StubCalls.h"
47 #include "methodjit/FrameState-inl.h"
49 using namespace js;
50 using namespace js::mjit;
51 using namespace JSC;
53 typedef JSC::MacroAssembler::FPRegisterID FPRegisterID;
55 bool
56 mjit::Compiler::tryBinaryConstantFold(JSContext *cx, FrameState &frame, JSOp op,
57 FrameEntry *lhs, FrameEntry *rhs)
59 if (!lhs->isConstant() || !rhs->isConstant())
60 return false;
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()))) {
67 return false;
70 bool needInt;
71 switch (op) {
72 case JSOP_ADD:
73 case JSOP_SUB:
74 case JSOP_MUL:
75 case JSOP_DIV:
76 needInt = false;
77 break;
79 case JSOP_MOD:
80 needInt = (L.isInt32() && R.isInt32() &&
81 L.toInt32() >= 0 && R.toInt32() > 0);
82 break;
84 case JSOP_RSH:
85 needInt = true;
86 break;
88 default:
89 JS_NOT_REACHED("NYI");
90 needInt = false; /* Silence compiler warning. */
91 break;
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
98 * is infallible.
100 if (needInt) {
101 ValueToECMAInt32(cx, L, &nL);
102 ValueToECMAInt32(cx, R, &nR);
103 } else {
104 ValueToNumber(cx, L, &dL);
105 ValueToNumber(cx, R, &dR);
108 switch (op) {
109 case JSOP_ADD:
110 dL += dR;
111 break;
112 case JSOP_SUB:
113 dL -= dR;
114 break;
115 case JSOP_MUL:
116 dL *= dR;
117 break;
118 case JSOP_DIV:
119 if (dR == 0) {
120 #ifdef XP_WIN
121 if (JSDOUBLE_IS_NaN(dR))
122 dL = js_NaN;
123 else
124 #endif
125 if (dL == 0 || JSDOUBLE_IS_NaN(dL))
126 dL = js_NaN;
127 else if (JSDOUBLE_IS_NEG(dL) != JSDOUBLE_IS_NEG(dR))
128 dL = cx->runtime->negativeInfinityValue.toDouble();
129 else
130 dL = cx->runtime->positiveInfinityValue.toDouble();
131 } else {
132 dL /= dR;
134 break;
135 case JSOP_MOD:
136 if (needInt)
137 nL %= nR;
138 else if (dR == 0)
139 dL = js_NaN;
140 else
141 dL = js_fmod(dL, dR);
142 break;
144 case JSOP_RSH:
145 nL >>= (nR & 31);
146 break;
148 default:
149 JS_NOT_REACHED("NYI");
150 break;
153 Value v;
154 if (needInt)
155 v.setInt32(nL);
156 else
157 v.setNumber(dL);
158 frame.popn(2);
159 frame.push(v);
161 return true;
164 void
165 mjit::Compiler::slowLoadConstantDouble(Assembler &masm,
166 FrameEntry *fe, FPRegisterID fpreg)
168 DoublePatch patch;
169 if (fe->getKnownType() == JSVAL_TYPE_INT32)
170 patch.d = (double)fe->getValue().toInt32();
171 else
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);
179 void
180 mjit::Compiler::maybeJumpIfNotInt32(Assembler &masm, MaybeJump &mj, FrameEntry *fe,
181 MaybeRegisterID &mreg)
183 if (!fe->isTypeKnown()) {
184 if (mreg.isSet())
185 mj.setJump(masm.testInt32(Assembler::NotEqual, mreg.reg()));
186 else
187 mj.setJump(masm.testInt32(Assembler::NotEqual, frame.addressOf(fe)));
188 } else if (fe->getKnownType() != JSVAL_TYPE_INT32) {
189 mj.setJump(masm.jump());
193 void
194 mjit::Compiler::maybeJumpIfNotDouble(Assembler &masm, MaybeJump &mj, FrameEntry *fe,
195 MaybeRegisterID &mreg)
197 if (!fe->isTypeKnown()) {
198 if (mreg.isSet())
199 mj.setJump(masm.testDouble(Assembler::NotEqual, mreg.reg()));
200 else
201 mj.setJump(masm.testDouble(Assembler::NotEqual, frame.addressOf(fe)));
202 } else if (fe->getKnownType() != JSVAL_TYPE_DOUBLE) {
203 mj.setJump(masm.jump());
207 void
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))
214 return;
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. */
225 || op == JSOP_MUL
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);
234 frame.popn(2);
235 if (isStringResult)
236 frame.pushSyncedType(JSVAL_TYPE_STRING);
237 else
238 frame.pushSynced();
239 return;
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));
247 if (canDoIntMath)
248 jsop_binary_full(lhs, rhs, op, stub);
249 else
250 jsop_binary_double(lhs, rhs, op, stub);
253 static void
254 EmitDoubleOp(JSOp op, FPRegisterID fpRight, FPRegisterID fpLeft, Assembler &masm)
256 switch (op) {
257 case JSOP_ADD:
258 masm.addDouble(fpRight, fpLeft);
259 break;
261 case JSOP_SUB:
262 masm.subDouble(fpRight, fpLeft);
263 break;
265 case JSOP_MUL:
266 masm.mulDouble(fpRight, fpLeft);
267 break;
269 case JSOP_DIV:
270 masm.divDouble(fpRight, fpLeft);
271 break;
273 default:
274 JS_NOT_REACHED("unrecognized binary op");
278 mjit::MaybeJump
279 mjit::Compiler::loadDouble(FrameEntry *fe, FPRegisterID fpReg)
281 MaybeJump notNumber;
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);
292 // CANDIDATE
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);
298 } else {
299 JS_ASSERT(fe->getKnownType() == JSVAL_TYPE_DOUBLE);
300 frame.loadDouble(fe, fpReg, masm);
303 return notNumber;
307 * This function emits a single fast-path for handling numerical arithmetic.
308 * Unlike jsop_binary_full(), all integers are converted to doubles.
310 void
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);
323 } else {
324 rhsNotNumber = loadDouble(rhs, fpRight);
325 if (rhsNotNumber.isSet())
326 stubcc.linkExit(rhsNotNumber.get(), Uses(2));
329 EmitDoubleOp(op, fpRight, fpLeft, masm);
331 MaybeJump done;
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();
340 JumpList isDouble;
341 masm.branchConvertDoubleToInt32(fpLeft, reg, isDouble, fpRight);
343 masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), reg,
344 frame.addressOf(lhs));
346 frame.freeReg(reg);
347 done.setJump(masm.jump());
348 isDouble.linkTo(masm.label(), &masm);
351 masm.storeDouble(fpLeft, frame.addressOf(lhs));
353 if (done.isSet())
354 done.getJump().linkTo(masm.label(), &masm);
356 if (lhsNotNumber.isSet() || rhsNotNumber.isSet()) {
357 stubcc.leave();
358 OOL_STUBCALL(stub);
361 frame.popn(2);
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.
371 void
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);
380 frame.popn(2);
381 frame.pushNumber(MaybeRegisterID());
382 return;
385 /* Allocate all registers up-front. */
386 FrameState::BinaryAlloc regs;
387 frame.allocForSameBinary(fe, op, regs);
389 MaybeJump notNumber;
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. */
410 MaybeJump overflow;
411 switch (op) {
412 case JSOP_ADD:
413 overflow = masm.branchAdd32(Assembler::Overflow, regs.result, regs.result);
414 break;
416 case JSOP_SUB:
417 overflow = masm.branchSub32(Assembler::Overflow, regs.result, regs.result);
418 break;
420 #if !defined(JS_CPU_ARM)
421 case JSOP_MUL:
422 overflow = masm.branchMul32(Assembler::Overflow, regs.result, regs.result);
423 break;
424 #endif
426 default:
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);
444 } else {
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));
464 stubcc.leave();
465 OOL_STUBCALL(stub);
467 /* Finish up stack operations. */
468 frame.popn(2);
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:
487 * Inline OOL
488 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
489 * Is LHS Int32? ------ No --------> Is LHS Double? ----- No -------,
490 * Sync LHS |
491 * Load LHS into XMM1 |
492 * Is RHS Double? ---- Yes --, |
493 * Is RHS Int32? ---- No --|-----|
494 * Convert RHS into XMM0 | |
495 * Else <-------------------' |
496 * Sync RHS |
497 * Load RHS into XMM0 |
498 * [Add,Sub,Mul] XMM0,XMM1 |
499 * Jump ---------------------, |
500 * | |
501 * Is RHS Int32? ------ No -------> Is RHS Double? ----- No --|-----|
502 * Sync RHS | |
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
515 void
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);
520 return;
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. */
564 int32 value = 0;
565 JSOp origOp = op;
566 MaybeRegisterID reg;
567 MaybeJump preOverflow;
568 if (!regs.resultHasRhs) {
569 if (!regs.rhsData.isSet())
570 value = rhs->getValue().toInt32();
571 else
572 reg = regs.rhsData.reg();
573 } else {
574 if (!regs.lhsData.isSet())
575 value = lhs->getValue().toInt32();
576 else
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);
583 op = JSOP_ADD;
587 /* Okay - good to emit the integer fast-path. */
588 MaybeJump overflow, negZeroDone;
589 switch (op) {
590 case JSOP_ADD:
591 if (reg.isSet())
592 overflow = masm.branchAdd32(Assembler::Overflow, reg.reg(), regs.result);
593 else
594 overflow = masm.branchAdd32(Assembler::Overflow, Imm32(value), regs.result);
595 break;
597 case JSOP_SUB:
598 if (reg.isSet())
599 overflow = masm.branchSub32(Assembler::Overflow, reg.reg(), regs.result);
600 else
601 overflow = masm.branchSub32(Assembler::Overflow, Imm32(value), regs.result);
602 break;
604 #if !defined(JS_CPU_ARM)
605 case JSOP_MUL:
607 JS_ASSERT(reg.isSet());
609 MaybeJump storeNegZero;
610 bool maybeNegZero = true;
611 bool hasConstant = (lhs->isConstant() || rhs->isConstant());
613 if (hasConstant) {
614 value = (lhs->isConstant() ? lhs : rhs)->getValue().toInt32();
615 RegisterID nonConstReg = lhs->isConstant() ? regs.rhsData.reg() : regs.lhsData.reg();
617 if (value > 0)
618 maybeNegZero = false;
619 else if (value < 0)
620 storeNegZero = masm.branchTest32(Assembler::Zero, nonConstReg);
621 else
622 storeNegZero = masm.branch32(Assembler::LessThan, nonConstReg, Imm32(0));
624 overflow = masm.branchMul32(Assembler::Overflow, reg.reg(), regs.result);
626 if (maybeNegZero) {
627 if (!hasConstant) {
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);
635 else
636 stubcc.masm.move(regs.rhsData.reg(), regs.result);
637 } else {
638 if (regs.lhsNeedsRemat)
639 stubcc.masm.loadPayload(frame.addressForDataRemat(lhs), regs.result);
640 else
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);
647 } else {
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();
655 break;
657 #endif
659 default:
660 JS_NOT_REACHED("unrecognized op");
662 op = origOp;
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);
680 } else {
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);
689 } else {
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));
717 stubcc.leave();
718 OOL_STUBCALL(stub);
720 /* Finish up stack operations. */
721 frame.popn(2);
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;
736 void
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);
744 frame.pop();
745 frame.pushSynced();
746 return;
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). */
765 MaybeJump jmpNotDbl;
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);
776 #endif
778 /* Overwrite pushed frame's memory (before push). */
779 masm.storeDouble(fpreg, frame.addressOf(fe));
782 /* Try an integer path (out-of-line). */
783 MaybeJump jmpNotInt;
784 MaybeJump jmpIntZero;
785 MaybeJump jmpMinInt;
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());
805 frame.freeReg(reg);
806 if (feTypeReg.isSet())
807 frame.unpinReg(feTypeReg.reg());
809 stubcc.leave();
810 OOL_STUBCALL(stubs::Neg);
812 frame.pop();
813 frame.pushSynced();
815 /* Link jumps. */
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));
831 void
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))
839 return;
841 if ((lhs->isTypeKnown() && lhs->getKnownType() != JSVAL_TYPE_INT32) ||
842 (rhs->isTypeKnown() && rhs->getKnownType() != JSVAL_TYPE_INT32))
843 #endif
845 prepareStubCall(Uses(2));
846 INLINE_STUBCALL(stubs::Mod);
847 frame.popn(2);
848 frame.pushSynced();
849 return;
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);
865 } else {
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;
872 RegisterID rhsReg;
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);
877 } else {
878 rhsReg = frame.allocReg(Registers::AvailRegs & ~Registers::maskReg(X86Registers::edx));
879 JS_ASSERT(rhsReg != X86Registers::edx);
880 masm.move(Imm32(rhs->getValue().toInt32()), rhsReg);
881 temp = rhsReg;
883 frame.takeReg(X86Registers::edx);
884 frame.freeReg(X86Registers::eax);
886 if (temp.isSet())
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,
894 Imm32(0x80000000));
895 stubcc.linkExit(checkDivExc, Uses(2));
896 slowPath = true;
898 } else {
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));
903 slowPath = true;
906 /* Perform division. */
907 masm.idiv(rhsReg);
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);
921 MaybeJump done;
922 if (lhsMaybeNeg) {
923 MaybeRegisterID lhsData;
924 if (!lhsIsNeg)
925 lhsData = frame.tempRegForData(lhs);
926 Jump negZero1 = masm.branchTest32(Assembler::NonZero, X86Registers::edx);
927 MaybeJump negZero2;
928 if (!lhsIsNeg)
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. */
935 done = masm.jump();
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));
944 if (done.isSet())
945 done.getJump().linkTo(masm.label(), &masm);
947 if (slowPath) {
948 stubcc.leave();
949 OOL_STUBCALL(stubs::Mod);
952 frame.popn(2);
953 frame.pushNumber(X86Registers::edx);
955 if (slowPath)
956 stubcc.rejoin(Changes(1));
957 #endif
960 bool
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;
970 rhs = lhs;
971 lhs = temp;
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;
982 switch (op) {
983 case JSOP_EQ:
984 cond = flipCondition ? Assembler::NotEqual : Assembler::Equal;
985 break;
986 case JSOP_NE:
987 cond = flipCondition ? Assembler::Equal : Assembler::NotEqual;
988 break;
989 default:
990 JS_NOT_REACHED("wat");
991 return false;
994 if (target) {
995 Value rval = UndefinedValue(); /* quiet gcc warning */
996 bool rhsConst = false;
997 if (rhs->isConstant()) {
998 rhsConst = true;
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();
1014 frame.pop();
1015 frame.pop();
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;
1031 #ifdef JS_MONOIC
1032 EqualityGenInfo ic;
1034 ic.cond = cond;
1035 ic.tempReg = tempReg;
1036 ic.lvr = lvr;
1037 ic.rvr = rvr;
1038 ic.stubEntry = stubEntry;
1039 ic.stub = stub;
1041 bool useIC = !addTraceHints || target >= PC;
1043 /* Call the IC stub, which may generate a fast path. */
1044 if (useIC) {
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);
1049 needStub = false;
1051 #endif
1053 if (needStub)
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)
1061 ? Assembler::Zero
1062 : Assembler::NonZero;
1063 Jump stubBranch =
1064 stubcc.masm.branchTest32(ncond, Registers::ReturnReg, Registers::ReturnReg);
1065 Jump stubFallthrough = stubcc.masm.jump();
1067 JaegerSpew(JSpew_Insns, " ---- END STUB CALL CODE ---- \n");
1068 CHECK_OOL_SPACE();
1070 Jump fast;
1071 MaybeJump firstStubJump;
1073 if ((!lhs->isTypeKnown() || lhsInt) && (!rhs->isTypeKnown() || rhsInt)) {
1074 if (!lhsInt) {
1075 Jump lhsFail = masm.testInt32(Assembler::NotEqual, lvr.typeReg());
1076 stubcc.linkExitDirect(lhsFail, stubEntry);
1077 firstStubJump = lhsFail;
1079 if (!rhsInt) {
1080 Jump rhsFail = masm.testInt32(Assembler::NotEqual, rvr.typeReg());
1081 stubcc.linkExitDirect(rhsFail, stubEntry);
1082 if (!firstStubJump.isSet())
1083 firstStubJump = rhsFail;
1086 if (rhsConst)
1087 fast = masm.branch32(cond, lvr.dataReg(), Imm32(rval.toInt32()));
1088 else
1089 fast = masm.branch32(cond, lvr.dataReg(), rvr.dataReg());
1091 if (!jumpInScript(fast, target))
1092 return false;
1093 } else {
1094 Jump j = masm.jump();
1095 stubcc.linkExitDirect(j, stubEntry);
1096 firstStubJump = j;
1098 /* This is just a dummy jump. */
1099 fast = masm.jump();
1102 #ifdef JS_MONOIC
1103 ic.jumpToStub = firstStubJump;
1104 if (useIC) {
1105 ic.fallThrough = masm.label();
1106 ic.jumpTarget = target;
1107 equalityICs.append(ic);
1109 #endif
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))
1119 return false;
1120 } else {
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));
1129 } else {
1130 if (!lhsInt) {
1131 Jump lhsFail = frame.testInt32(Assembler::NotEqual, lhs);
1132 stubcc.linkExit(lhsFail, Uses(2));
1134 if (!rhsInt) {
1135 Jump rhsFail = frame.testInt32(Assembler::NotEqual, rhs);
1136 stubcc.linkExit(rhsFail, Uses(2));
1140 stubcc.leave();
1141 OOL_STUBCALL(stub);
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)),
1156 resultReg);
1157 } else {
1158 masm.set32(cond, reg, frame.tempRegForData(rhs), resultReg);
1161 /* Clean up and push a boolean. */
1162 frame.pop();
1163 frame.pop();
1164 if (reg != resultReg)
1165 frame.freeReg(reg);
1166 frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, resultReg);
1167 stubcc.rejoin(Changes(1));
1169 return true;
1173 * Emit an OOL path for a possibly double LHS, and possibly int32 or number RHS.
1175 void
1176 mjit::Compiler::emitLeftDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState::BinaryAlloc &regs,
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);
1200 else
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);
1215 /* Load the LHS. */
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.
1224 void
1225 mjit::Compiler::emitRightDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState::BinaryAlloc &regs,
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);
1241 else
1242 stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), fpLeft);
1244 /* Load the RHS. */
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;
1253 switch (op) {
1254 case JSOP_GT:
1255 return ifeq
1256 ? Assembler::DoubleLessThanOrEqualOrUnordered
1257 : Assembler::DoubleGreaterThan;
1258 case JSOP_GE:
1259 return ifeq
1260 ? Assembler::DoubleLessThanOrUnordered
1261 : Assembler::DoubleGreaterThanOrEqual;
1262 case JSOP_LT:
1263 return ifeq
1264 ? Assembler::DoubleGreaterThanOrEqualOrUnordered
1265 : Assembler::DoubleLessThan;
1266 case JSOP_LE:
1267 return ifeq
1268 ? Assembler::DoubleGreaterThanOrUnordered
1269 : Assembler::DoubleLessThanOrEqual;
1270 default:
1271 JS_NOT_REACHED("unrecognized op");
1272 return Assembler::DoubleLessThan;
1276 bool
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);
1292 if (target) {
1293 if (lhsNotNumber.isSet())
1294 stubcc.linkExitForBranch(lhsNotNumber.get());
1295 if (rhsNotNumber.isSet())
1296 stubcc.linkExitForBranch(rhsNotNumber.get());
1297 stubcc.leave();
1298 OOL_STUBCALL(stub);
1300 frame.popn(2);
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)
1310 ? Assembler::Zero
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))
1323 return false;
1324 } else {
1325 if (lhsNotNumber.isSet())
1326 stubcc.linkExit(lhsNotNumber.get(), Uses(2));
1327 if (rhsNotNumber.isSet())
1328 stubcc.linkExit(rhsNotNumber.get(), Uses(2));
1329 stubcc.leave();
1330 OOL_STUBCALL(stub);
1332 frame.popn(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));
1346 return true;
1349 bool
1350 mjit::Compiler::jsop_relational_self(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused)
1352 #ifdef DEBUG
1353 FrameEntry *rhs = frame.peek(-1);
1354 FrameEntry *lhs = frame.peek(-2);
1356 JS_ASSERT(frame.haveSameBacking(lhs, rhs));
1357 #endif
1359 /* :TODO: optimize this? */
1360 return emitStubCmpOp(stub, target, fused);
1363 /* See jsop_binary_full() for more information on how this works. */
1364 bool
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. */
1391 JSOp cmpOp = op;
1392 int32 value = 0;
1393 RegisterID cmpReg;
1394 MaybeRegisterID reg;
1395 if (regs.lhsData.isSet()) {
1396 cmpReg = regs.lhsData.reg();
1397 if (!regs.rhsData.isSet())
1398 value = rhs->getValue().toInt32();
1399 else
1400 reg = regs.rhsData.reg();
1401 } else {
1402 cmpReg = regs.rhsData.reg();
1403 value = lhs->getValue().toInt32();
1404 switch (op) {
1405 case JSOP_GT:
1406 cmpOp = JSOP_LT;
1407 break;
1408 case JSOP_GE:
1409 cmpOp = JSOP_LE;
1410 break;
1411 case JSOP_LT:
1412 cmpOp = JSOP_GT;
1413 break;
1414 case JSOP_LE:
1415 cmpOp = JSOP_GE;
1416 break;
1417 default:
1418 JS_NOT_REACHED("unrecognized op");
1419 break;
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
1426 * separately.
1429 if (target) {
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
1457 * jumping logic.
1459 frame.sync(stubcc.masm, Uses(frame.frameSlots()));
1460 stubcc.leave();
1461 OOL_STUBCALL(stub);
1464 /* Forget the world, preserving data. */
1465 frame.pinReg(cmpReg);
1466 if (reg.isSet())
1467 frame.pinReg(reg.reg());
1469 frame.popn(2);
1471 frame.syncAndKillEverything();
1472 frame.unpinKilledReg(cmpReg);
1473 if (reg.isSet())
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;
1480 switch (cmpOp) {
1481 case JSOP_GT:
1482 i32Cond = ifeq ? Assembler::LessThanOrEqual : Assembler::GreaterThan;
1483 break;
1484 case JSOP_GE:
1485 i32Cond = ifeq ? Assembler::LessThan : Assembler::GreaterThanOrEqual;
1486 break;
1487 case JSOP_LT:
1488 i32Cond = ifeq ? Assembler::GreaterThanOrEqual : Assembler::LessThan;
1489 break;
1490 case JSOP_LE:
1491 i32Cond = ifeq ? Assembler::GreaterThan : Assembler::LessThanOrEqual;
1492 break;
1493 default:
1494 JS_NOT_REACHED("unrecognized op");
1495 return false;
1498 /* Emit the i32 path. */
1499 Jump fast;
1500 if (reg.isSet())
1501 fast = masm.branch32(i32Cond, cmpReg, reg.reg());
1502 else
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)
1510 ? Assembler::Zero
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))
1530 return false;
1532 /* Rejoin from the double path. */
1533 if (hasDoublePath)
1534 stubcc.crossJump(doubleFall.get(), masm.label());
1535 } else {
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));
1565 stubcc.leave();
1566 OOL_STUBCALL(stub);
1569 /* Get an integer comparison condition. */
1570 Assembler::Condition i32Cond;
1571 switch (cmpOp) {
1572 case JSOP_GT:
1573 i32Cond = Assembler::GreaterThan;
1574 break;
1575 case JSOP_GE:
1576 i32Cond = Assembler::GreaterThanOrEqual;
1577 break;
1578 case JSOP_LT:
1579 i32Cond = Assembler::LessThan;
1580 break;
1581 case JSOP_LE:
1582 i32Cond = Assembler::LessThanOrEqual;
1583 break;
1584 default:
1585 JS_NOT_REACHED("unrecognized op");
1586 return false;
1589 /* Emit the compare & set. */
1590 if (Registers::maskReg(regs.result) & Registers::SingleByteRegs) {
1591 if (reg.isSet())
1592 masm.set32(i32Cond, cmpReg, reg.reg(), regs.result);
1593 else
1594 masm.set32(i32Cond, cmpReg, Imm32(value), regs.result);
1595 } else {
1596 Jump j;
1597 if (reg.isSet())
1598 j = masm.branch32(i32Cond, cmpReg, reg.reg());
1599 else
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);
1608 frame.popn(2);
1609 frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, regs.result);
1611 if (hasDoublePath)
1612 stubcc.crossJump(doubleDone.get(), masm.label());
1613 stubcc.rejoin(Changes(1));
1615 return true;