LCOV - code coverage report
Current view: directory - js/src/methodjit - FastArithmetic.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 883 846 95.8 %
Date: 2012-04-07 Functions: 20 20 100.0 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  * vim: set ts=4 sw=4 et tw=99:
       3                 :  *
       4                 :  * ***** BEGIN LICENSE BLOCK *****
       5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       6                 :  *
       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/
      11                 :  *
      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.
      16                 :  *
      17                 :  * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
      18                 :  * May 28, 2008.
      19                 :  *
      20                 :  * The Initial Developer of the Original Code is
      21                 :  *   Brendan Eich <brendan@mozilla.org>
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   David Anderson <danderson@mozilla.com>
      25                 :  *   David Mandelin <dmandelin@mozilla.com>
      26                 :  *   Sean Stangl    <sstangl@mozilla.com>
      27                 :  *
      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.
      39                 :  *
      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"
      48                 : 
      49                 : using namespace js;
      50                 : using namespace js::mjit;
      51                 : using namespace js::analyze;
      52                 : using namespace JSC;
      53                 : 
      54                 : typedef JSC::MacroAssembler::FPRegisterID FPRegisterID;
      55                 : 
      56                 : bool
      57          391654 : mjit::Compiler::tryBinaryConstantFold(JSContext *cx, FrameState &frame, JSOp op,
      58                 :                                       FrameEntry *lhs, FrameEntry *rhs, Value *vp)
      59                 : {
      60          391654 :     if (!lhs->isConstant() || !rhs->isConstant())
      61          390778 :         return false;
      62                 : 
      63             876 :     const Value &L = lhs->getValue();
      64             876 :     const Value &R = rhs->getValue();
      65                 : 
      66            1562 :     if (!L.isPrimitive() || !R.isPrimitive() ||
      67             686 :         (op == JSOP_ADD && (L.isString() || R.isString()))) {
      68             153 :         return false;
      69                 :     }
      70                 : 
      71                 :     bool needInt;
      72             723 :     switch (op) {
      73                 :       case JSOP_ADD:
      74                 :       case JSOP_SUB:
      75                 :       case JSOP_MUL:
      76                 :       case JSOP_DIV:
      77             715 :         needInt = false;
      78             715 :         break;
      79                 : 
      80                 :       case JSOP_MOD:
      81              14 :         needInt = (L.isInt32() && R.isInt32() &&
      82              14 :                    L.toInt32() >= 0 && R.toInt32() > 0);
      83               8 :         break;
      84                 : 
      85                 :       default:
      86               0 :         JS_NOT_REACHED("NYI");
      87                 :         needInt = false; /* Silence compiler warning. */
      88                 :         break;
      89                 :     }
      90                 : 
      91             723 :     double dL = 0, dR = 0;
      92             723 :     int32_t nL = 0, nR = 0;
      93                 :     /*
      94                 :      * We don't need to check for conversion failure, since primitive conversion
      95                 :      * is infallible.
      96                 :      */
      97             723 :     if (needInt) {
      98               2 :         JS_ALWAYS_TRUE(ToInt32(cx, L, &nL));
      99               2 :         JS_ALWAYS_TRUE(ToInt32(cx, R, &nR));
     100                 :     } else {
     101             721 :         JS_ALWAYS_TRUE(ToNumber(cx, L, &dL));
     102             721 :         JS_ALWAYS_TRUE(ToNumber(cx, R, &dR));
     103                 :     }
     104                 : 
     105             723 :     switch (op) {
     106                 :       case JSOP_ADD:
     107             278 :         dL += dR;
     108             278 :         break;
     109                 :       case JSOP_SUB:
     110             118 :         dL -= dR;
     111             118 :         break;
     112                 :       case JSOP_MUL:
     113              60 :         dL *= dR;
     114              60 :         break;
     115                 :       case JSOP_DIV:
     116             259 :         dL = js::NumberDiv(dL, dR);
     117             259 :         break;
     118                 :       case JSOP_MOD:
     119               8 :         if (needInt)
     120               2 :             nL %= nR;
     121               6 :         else if (dR == 0)
     122               2 :             dL = js_NaN;
     123                 :         else
     124               4 :             dL = js_fmod(dL, dR);
     125               8 :         break;
     126                 : 
     127                 :       default:
     128               0 :         JS_NOT_REACHED("NYI");
     129                 :         break;
     130                 :     }
     131                 : 
     132             723 :     if (needInt)
     133               2 :         vp->setInt32(nL);
     134                 :     else
     135             721 :         vp->setNumber(dL);
     136                 : 
     137             723 :     return true;
     138                 : }
     139                 : 
     140                 : void
     141           47836 : mjit::Compiler::slowLoadConstantDouble(Assembler &masm,
     142                 :                                        FrameEntry *fe, FPRegisterID fpreg)
     143                 : {
     144           47836 :     if (fe->getValue().isInt32())
     145           46891 :         masm.slowLoadConstantDouble((double) fe->getValue().toInt32(), fpreg);
     146                 :     else
     147             945 :         masm.slowLoadConstantDouble(fe->getValue().toDouble(), fpreg);
     148           47836 : }
     149                 : 
     150                 : void
     151            2366 : mjit::Compiler::maybeJumpIfNotInt32(Assembler &masm, MaybeJump &mj, FrameEntry *fe,
     152                 :                                     MaybeRegisterID &mreg)
     153                 : {
     154            2366 :     if (!fe->isTypeKnown()) {
     155            2363 :         if (mreg.isSet())
     156            2335 :             mj.setJump(masm.testInt32(Assembler::NotEqual, mreg.reg()));
     157                 :         else
     158              28 :             mj.setJump(masm.testInt32(Assembler::NotEqual, frame.addressOf(fe)));
     159               3 :     } else if (fe->getKnownType() != JSVAL_TYPE_INT32) {
     160               0 :         mj.setJump(masm.jump());
     161                 :     }
     162            2366 : }
     163                 : 
     164                 : void
     165            2366 : mjit::Compiler::maybeJumpIfNotDouble(Assembler &masm, MaybeJump &mj, FrameEntry *fe,
     166                 :                                     MaybeRegisterID &mreg)
     167                 : {
     168            2366 :     if (!fe->isTypeKnown()) {
     169            2363 :         if (mreg.isSet())
     170            2335 :             mj.setJump(masm.testDouble(Assembler::NotEqual, mreg.reg()));
     171                 :         else
     172              28 :             mj.setJump(masm.testDouble(Assembler::NotEqual, frame.addressOf(fe)));
     173               3 :     } else if (fe->getKnownType() != JSVAL_TYPE_DOUBLE) {
     174               3 :         mj.setJump(masm.jump());
     175                 :     }
     176            2366 : }
     177                 : 
     178                 : bool
     179           18984 : mjit::Compiler::jsop_binary_slow(JSOp op, VoidStub stub, JSValueType type,
     180                 :                                  FrameEntry *lhs, FrameEntry *rhs)
     181                 : {
     182                 :     bool isStringResult = (op == JSOP_ADD) &&
     183           18984 :                           (lhs->isType(JSVAL_TYPE_STRING) || rhs->isType(JSVAL_TYPE_STRING));
     184           18984 :     JS_ASSERT_IF(isStringResult && type != JSVAL_TYPE_UNKNOWN, type == JSVAL_TYPE_STRING);
     185                 : 
     186           18984 :     prepareStubCall(Uses(2));
     187           18984 :     INLINE_STUBCALL(stub, REJOIN_BINARY);
     188           18984 :     frame.popn(2);
     189           18984 :     frame.pushSynced(isStringResult ? JSVAL_TYPE_STRING : type);
     190           18984 :     return true;
     191                 : }
     192                 : 
     193                 : bool
     194          390564 : mjit::Compiler::jsop_binary(JSOp op, VoidStub stub, JSValueType type, types::TypeSet *typeSet)
     195                 : {
     196          390564 :     FrameEntry *rhs = frame.peek(-1);
     197          390564 :     FrameEntry *lhs = frame.peek(-2);
     198                 : 
     199                 :     Value v;
     200          390564 :     if (tryBinaryConstantFold(cx, frame, op, lhs, rhs, &v)) {
     201             715 :         if (!v.isInt32() && typeSet && !typeSet->hasType(types::Type::DoubleType())) {
     202                 :             /*
     203                 :              * OK to ignore failure here, we aren't performing the operation
     204                 :              * itself. Note that monitorOverflow will propagate the type as
     205                 :              * necessary if a *INC operation overflowed.
     206                 :              */
     207               8 :             types::TypeScript::MonitorOverflow(cx, script, PC);
     208               8 :             return false;
     209                 :         }
     210             707 :         frame.popn(2);
     211             707 :         frame.push(v);
     212             707 :         return true;
     213                 :     }
     214                 : 
     215                 :     /*
     216                 :      * Bail out if there are unhandled types or ops.
     217                 :      * This is temporary while ops are still being implemented.
     218                 :      */
     219         1271038 :     if ((lhs->isConstant() && rhs->isConstant()) ||
     220          437148 :         (lhs->isTypeKnown() && (lhs->getKnownType() > JSVAL_UPPER_INCL_TYPE_OF_NUMBER_SET)) ||
     221          444041 :         (rhs->isTypeKnown() && (rhs->getKnownType() > JSVAL_UPPER_INCL_TYPE_OF_NUMBER_SET)))
     222                 :     {
     223           18984 :         return jsop_binary_slow(op, stub, type, lhs, rhs);
     224                 :     }
     225                 : 
     226                 :     /*
     227                 :      * If this is an operation on which integer overflows can be ignored, treat
     228                 :      * the result as an integer even if it has been marked as overflowing by
     229                 :      * the interpreter. Doing this changes the values we maintain on the stack
     230                 :      * from those the interpreter would maintain; this is OK as values derived
     231                 :      * from ignored overflows are not live across points where the interpreter
     232                 :      * can join into JIT code (loop heads and safe points).
     233                 :      */
     234          370865 :     CrossSSAValue pushv(a->inlineIndex, SSAValue::PushedValue(PC - script->code, 0));
     235          370865 :     bool cannotOverflow = loop && loop->cannotIntegerOverflow(pushv);
     236          370865 :     bool ignoreOverflow = loop && loop->ignoreIntegerOverflow(pushv);
     237                 : 
     238          370865 :     if (rhs->isType(JSVAL_TYPE_INT32) && lhs->isType(JSVAL_TYPE_INT32) &&
     239                 :         op == JSOP_ADD && ignoreOverflow) {
     240              14 :         type = JSVAL_TYPE_INT32;
     241                 :     }
     242                 : 
     243                 :     /* Can do int math iff there is no double constant and the op is not division. */
     244                 :     bool canDoIntMath = op != JSOP_DIV && type != JSVAL_TYPE_DOUBLE &&
     245          370865 :                         !(rhs->isType(JSVAL_TYPE_DOUBLE) || lhs->isType(JSVAL_TYPE_DOUBLE));
     246                 : 
     247          370865 :     if (!masm.supportsFloatingPoint() && (!canDoIntMath || frame.haveSameBacking(lhs, rhs)))
     248               0 :         return jsop_binary_slow(op, stub, type, lhs, rhs);
     249                 : 
     250          370865 :     if (canDoIntMath)
     251          363481 :         jsop_binary_full(lhs, rhs, op, stub, type, cannotOverflow, ignoreOverflow);
     252                 :     else
     253            7384 :         jsop_binary_double(lhs, rhs, op, stub, type);
     254                 : 
     255          370865 :     return true;
     256                 : }
     257                 : 
     258                 : static void
     259          345223 : EmitDoubleOp(JSOp op, FPRegisterID fpRight, FPRegisterID fpLeft, Assembler &masm)
     260                 : {
     261          345223 :     switch (op) {
     262                 :       case JSOP_ADD:
     263          315781 :         masm.addDouble(fpRight, fpLeft);
     264          315781 :         break;
     265                 : 
     266                 :       case JSOP_SUB:
     267           22234 :         masm.subDouble(fpRight, fpLeft);
     268           22234 :         break;
     269                 : 
     270                 :       case JSOP_MUL:
     271            4728 :         masm.mulDouble(fpRight, fpLeft);
     272            4728 :         break;
     273                 : 
     274                 :       case JSOP_DIV:
     275            2480 :         masm.divDouble(fpRight, fpLeft);
     276            2480 :         break;
     277                 : 
     278                 :       default:
     279               0 :         JS_NOT_REACHED("unrecognized binary op");
     280                 :     }
     281          345223 : }
     282                 : 
     283                 : mjit::MaybeJump
     284           18941 : mjit::Compiler::loadDouble(FrameEntry *fe, FPRegisterID *fpReg, bool *allocated)
     285                 : {
     286           18941 :     MaybeJump notNumber;
     287                 : 
     288           18941 :     if (!fe->isConstant() && fe->isType(JSVAL_TYPE_DOUBLE)) {
     289            9698 :         *fpReg = frame.tempFPRegForData(fe);
     290            9698 :         *allocated = false;
     291            9698 :         return notNumber;
     292                 :     }
     293                 : 
     294            9243 :     *fpReg = frame.allocFPReg();
     295            9243 :     *allocated = true;
     296                 : 
     297            9243 :     if (fe->isConstant()) {
     298            3410 :         slowLoadConstantDouble(masm, fe, *fpReg);
     299            5833 :     } else if (!fe->isTypeKnown()) {
     300            3430 :         frame.tempRegForType(fe);
     301            3430 :         Jump j = frame.testDouble(Assembler::Equal, fe);
     302            3430 :         notNumber = frame.testInt32(Assembler::NotEqual, fe);
     303            3430 :         frame.convertInt32ToDouble(masm, fe, *fpReg);
     304            3430 :         Jump converted = masm.jump();
     305            3430 :         j.linkTo(masm.label(), &masm);
     306                 :         // CANDIDATE
     307            3430 :         frame.loadDouble(fe, *fpReg, masm);
     308            3430 :         converted.linkTo(masm.label(), &masm);
     309                 :     } else {
     310            2403 :         JS_ASSERT(fe->isType(JSVAL_TYPE_INT32));
     311            2403 :         frame.tempRegForData(fe);
     312            2403 :         frame.convertInt32ToDouble(masm, fe, *fpReg);
     313                 :     }
     314                 : 
     315            9243 :     return notNumber;
     316                 : }
     317                 : 
     318                 : /*
     319                 :  * This function emits a single fast-path for handling numerical arithmetic.
     320                 :  * Unlike jsop_binary_full(), all integers are converted to doubles.
     321                 :  */
     322                 : void
     323            7384 : mjit::Compiler::jsop_binary_double(FrameEntry *lhs, FrameEntry *rhs, JSOp op,
     324                 :                                    VoidStub stub, JSValueType type)
     325                 : {
     326                 :     FPRegisterID fpLeft, fpRight;
     327                 :     bool allocateLeft, allocateRight;
     328                 : 
     329            7384 :     MaybeJump lhsNotNumber = loadDouble(lhs, &fpLeft, &allocateLeft);
     330            7384 :     if (lhsNotNumber.isSet())
     331            1580 :         stubcc.linkExit(lhsNotNumber.get(), Uses(2));
     332                 : 
     333                 :     /* The left register holds the result, and needs to be mutable. */
     334            7384 :     if (!allocateLeft) {
     335            3614 :         FPRegisterID res = frame.allocFPReg();
     336            3614 :         masm.moveDouble(fpLeft, res);
     337            3614 :         fpLeft = res;
     338            3614 :         allocateLeft = true;
     339                 :     }
     340                 : 
     341            7384 :     MaybeJump rhsNotNumber;
     342            7384 :     if (frame.haveSameBacking(lhs, rhs)) {
     343              41 :         fpRight = fpLeft;
     344              41 :         allocateRight = false;
     345                 :     } else {
     346            7343 :         rhsNotNumber = loadDouble(rhs, &fpRight, &allocateRight);
     347            7343 :         if (rhsNotNumber.isSet())
     348            1625 :             stubcc.linkExit(rhsNotNumber.get(), Uses(2));
     349                 :     }
     350                 : 
     351            7384 :     EmitDoubleOp(op, fpRight, fpLeft, masm);
     352                 :     
     353            7384 :     MaybeJump done;
     354                 : 
     355                 :     /*
     356                 :      * Try to convert result to integer, if the result has unknown or integer type.
     357                 :      * Skip this for 1/x or -1/x, as the result is unlikely to fit in an int.
     358                 :      */
     359            9604 :     if (op == JSOP_DIV &&
     360                 :         (type == JSVAL_TYPE_INT32 ||
     361                 :          (type == JSVAL_TYPE_UNKNOWN &&
     362            1111 :           !(lhs->isConstant() && lhs->isType(JSVAL_TYPE_INT32) &&
     363             247 :             abs(lhs->getValue().toInt32()) == 1)))) {
     364            1356 :         RegisterID reg = frame.allocReg();
     365            1356 :         FPRegisterID fpReg = frame.allocFPReg();
     366            2712 :         JumpList isDouble;
     367            1356 :         masm.branchConvertDoubleToInt32(fpLeft, reg, isDouble, fpReg);
     368                 :         
     369                 :         masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), reg,
     370            1356 :                                       frame.addressOf(lhs));
     371                 :         
     372            1356 :         frame.freeReg(reg);
     373            1356 :         frame.freeReg(fpReg);
     374            1356 :         done.setJump(masm.jump());
     375                 : 
     376            1356 :         isDouble.linkTo(masm.label(), &masm);
     377                 :     }
     378                 : 
     379                 :     /*
     380                 :      * Inference needs to know about any operation on integers that produces a
     381                 :      * double result. Unless the pushed type set already contains the double
     382                 :      * type, we need to call a stub rather than push. Note that looking at
     383                 :      * the pushed type tag is not sufficient, as it will be UNKNOWN if
     384                 :      * we do not yet know the possible types of the division's operands.
     385                 :      */
     386            7384 :     types::TypeSet *resultTypes = pushedTypeSet(0);
     387            7384 :     if (resultTypes && !resultTypes->hasType(types::Type::DoubleType())) {
     388                 :         /*
     389                 :          * Call a stub and try harder to convert to int32, failing that trigger
     390                 :          * recompilation of this script.
     391                 :          */
     392             950 :         stubcc.linkExit(masm.jump(), Uses(2));
     393                 :     } else {
     394            6434 :         JS_ASSERT(type != JSVAL_TYPE_INT32);
     395            6434 :         if (type != JSVAL_TYPE_DOUBLE)
     396             837 :             masm.storeDouble(fpLeft, frame.addressOf(lhs));
     397                 :     }
     398                 : 
     399            7384 :     if (done.isSet())
     400            1356 :         done.getJump().linkTo(masm.label(), &masm);
     401                 : 
     402            7384 :     stubcc.leave();
     403            7384 :     OOL_STUBCALL(stub, REJOIN_BINARY);
     404                 : 
     405            7384 :     if (allocateRight)
     406            4059 :         frame.freeReg(fpRight);
     407                 : 
     408            7384 :     frame.popn(2);
     409                 : 
     410            7384 :     if (type == JSVAL_TYPE_DOUBLE) {
     411            5597 :         frame.pushDouble(fpLeft);
     412                 :     } else {
     413            1787 :         frame.freeReg(fpLeft);
     414            1787 :         frame.pushSynced(type);
     415                 :     }
     416                 : 
     417            7384 :     stubcc.rejoin(Changes(1));
     418            7384 : }
     419                 : 
     420                 : /*
     421                 :  * Simpler version of jsop_binary_full() for when lhs == rhs.
     422                 :  */
     423                 : void
     424              82 : mjit::Compiler::jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub, JSValueType type)
     425                 : {
     426              82 :     FrameEntry *lhs = frame.peek(-2);
     427                 : 
     428                 :     /* Easiest case: known double. Don't bother conversion back yet? */
     429              82 :     if (fe->isType(JSVAL_TYPE_DOUBLE)) {
     430               0 :         FPRegisterID fpreg = frame.allocFPReg();
     431               0 :         FPRegisterID lhs = frame.tempFPRegForData(fe);
     432               0 :         masm.moveDouble(lhs, fpreg);
     433               0 :         EmitDoubleOp(op, fpreg, fpreg, masm);
     434               0 :         frame.popn(2);
     435                 : 
     436               0 :         JS_ASSERT(type == JSVAL_TYPE_DOUBLE);  /* :XXX: can fail */
     437               0 :         frame.pushDouble(fpreg);
     438               0 :         return;
     439                 :     }
     440                 : 
     441                 :     /* Allocate all registers up-front. */
     442              82 :     FrameState::BinaryAlloc regs;
     443              82 :     frame.allocForSameBinary(fe, op, regs);
     444                 : 
     445              82 :     MaybeJump notNumber;
     446              82 :     MaybeJump doublePathDone;
     447              82 :     if (!fe->isTypeKnown()) {
     448              54 :         Jump notInt = masm.testInt32(Assembler::NotEqual, regs.lhsType.reg());
     449              54 :         stubcc.linkExitDirect(notInt, stubcc.masm.label());
     450                 : 
     451              54 :         notNumber = stubcc.masm.testDouble(Assembler::NotEqual, regs.lhsType.reg());
     452              54 :         frame.loadDouble(fe, regs.lhsFP, stubcc.masm);
     453              54 :         EmitDoubleOp(op, regs.lhsFP, regs.lhsFP, stubcc.masm);
     454                 : 
     455                 :         /* Force the double back to memory. */
     456              54 :         Address result = frame.addressOf(lhs);
     457              54 :         stubcc.masm.storeDouble(regs.lhsFP, result);
     458                 : 
     459                 :         /* Load the payload into the result reg so the rejoin is safe. */
     460              54 :         stubcc.masm.loadPayload(result, regs.result);
     461                 : 
     462              54 :         doublePathDone = stubcc.masm.jump();
     463                 :     }
     464                 : 
     465                 :     /* Okay - good to emit the integer fast-path. */
     466              82 :     MaybeJump overflow;
     467              82 :     switch (op) {
     468                 :       case JSOP_ADD:
     469              28 :         overflow = masm.branchAdd32(Assembler::Overflow, regs.result, regs.result);
     470              28 :         break;
     471                 : 
     472                 :       case JSOP_SUB:
     473               0 :         overflow = masm.branchSub32(Assembler::Overflow, regs.result, regs.result);
     474               0 :         break;
     475                 : 
     476                 :       case JSOP_MUL:
     477              54 :         overflow = masm.branchMul32(Assembler::Overflow, regs.result, regs.result);
     478              54 :         break;
     479                 : 
     480                 :       default:
     481               0 :         JS_NOT_REACHED("unrecognized op");
     482                 :     }
     483                 :     
     484              82 :     JS_ASSERT(overflow.isSet());
     485                 : 
     486                 :     /*
     487                 :      * Integer overflow path. Restore the original values and make a stub call,
     488                 :      * which could trigger recompilation.
     489                 :      */
     490              82 :     stubcc.linkExitDirect(overflow.get(), stubcc.masm.label());
     491              82 :     frame.rematBinary(fe, NULL, regs, stubcc.masm);
     492              82 :     stubcc.syncExitAndJump(Uses(2));
     493                 : 
     494                 :     /* Slow paths funnel here. */
     495              82 :     if (notNumber.isSet())
     496              54 :         notNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm);
     497                 : 
     498                 :     /* Slow call - use frame.sync to avoid erroneous jump repatching in stubcc. */
     499              82 :     frame.sync(stubcc.masm, Uses(2));
     500              82 :     stubcc.leave();
     501              82 :     OOL_STUBCALL(stub, REJOIN_BINARY);
     502                 : 
     503                 :     /* Finish up stack operations. */
     504              82 :     frame.popn(2);
     505                 : 
     506              82 :     if (type == JSVAL_TYPE_INT32)
     507              28 :         frame.pushTypedPayload(type, regs.result);
     508                 :     else
     509              54 :         frame.pushNumber(regs.result, true);
     510                 : 
     511              82 :     frame.freeReg(regs.lhsFP);
     512                 : 
     513                 :     /* Merge back OOL double path. */
     514              82 :     if (doublePathDone.isSet())
     515              54 :         stubcc.linkRejoin(doublePathDone.get());
     516                 : 
     517              82 :     stubcc.rejoin(Changes(1));
     518                 : }
     519                 : 
     520                 : /*
     521                 :  * This function emits multiple fast-paths for handling numerical arithmetic.
     522                 :  * Currently, it handles only ADD, SUB, and MUL, where both LHS and RHS are
     523                 :  * known not to be doubles.
     524                 :  *
     525                 :  * The control flow of the emitted code depends on which types are known.
     526                 :  * Given both types are unknown, the full spread looks like:
     527                 :  *
     528                 :  * Inline                              OOL
     529                 :  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     530                 :  * Is LHS Int32?  ------ No -------->  Is LHS Double?  ----- No -------,
     531                 :  *                                     Sync LHS                        |
     532                 :  *                                     Load LHS into XMM1              |
     533                 :  *                                     Is RHS Double? ---- Yes --,     |
     534                 :  *                                       Is RHS Int32? ---- No --|-----|
     535                 :  *                                       Convert RHS into XMM0   |     |
     536                 :  *                                     Else  <-------------------'     |
     537                 :  *                                       Sync RHS                      |
     538                 :  *                                       Load RHS into XMM0            |
     539                 :  *                                     [Add,Sub,Mul] XMM0,XMM1         |
     540                 :  *                                     Jump ---------------------,     |
     541                 :  *                                                               |     |
     542                 :  * Is RHS Int32?  ------ No ------->   Is RHS Double? ----- No --|-----|
     543                 :  *                                     Sync RHS                  |     |
     544                 :  *                                     Load RHS into XMM0        |     |
     545                 :  *                                     Convert LHS into XMM1     |     |
     546                 :  *                                     [Add,Sub,Mul] XMM0,XMM1   |     |
     547                 :  *                                     Jump ---------------------|   Slow Call
     548                 :  *                                                               |
     549                 :  * [Add,Sub,Mul] RHS, LHS                                        |
     550                 :  * Overflow      ------ Yes ------->   Convert RHS into XMM0     |
     551                 :  *                                     Convert LHS into XMM1     |
     552                 :  *                                     [Add,Sub,Mul] XMM0,XMM1   |
     553                 :  *                                     Sync XMM1 to stack    <---'
     554                 :  *  <--------------------------------- Rejoin
     555                 :  */
     556                 : void
     557          363481 : mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op,
     558                 :                                  VoidStub stub, JSValueType type,
     559                 :                                  bool cannotOverflow, bool ignoreOverflow)
     560                 : {
     561          363481 :     if (frame.haveSameBacking(lhs, rhs)) {
     562              82 :         jsop_binary_full_simple(lhs, op, stub, type);
     563              82 :         return;
     564                 :     }
     565                 : 
     566                 :     /* Allocate all registers up-front. */
     567          363399 :     FrameState::BinaryAlloc regs;
     568          363399 :     frame.allocForBinary(lhs, rhs, op, regs);
     569                 : 
     570                 :     /* Quick-test some invariants. */
     571          363399 :     JS_ASSERT_IF(lhs->isTypeKnown(), lhs->getKnownType() == JSVAL_TYPE_INT32);
     572          363399 :     JS_ASSERT_IF(rhs->isTypeKnown(), rhs->getKnownType() == JSVAL_TYPE_INT32);
     573                 : 
     574          363399 :     MaybeJump lhsNotDouble, rhsNotNumber, lhsUnknownDone;
     575          363399 :     if (!lhs->isTypeKnown())
     576          336451 :         emitLeftDoublePath(lhs, rhs, regs, lhsNotDouble, rhsNotNumber, lhsUnknownDone);
     577                 : 
     578          363399 :     MaybeJump rhsNotNumber2;
     579          363399 :     if (!rhs->isTypeKnown())
     580          305492 :         emitRightDoublePath(lhs, rhs, regs, rhsNotNumber2);
     581                 : 
     582                 :     /* Perform the double addition. */
     583          363399 :     MaybeJump doublePathDone;
     584          363399 :     if (masm.supportsFloatingPoint() && (!rhs->isTypeKnown() || !lhs->isTypeKnown())) {
     585                 :         /* If the LHS type was not known, link its path here. */
     586          337785 :         if (lhsUnknownDone.isSet())
     587          336451 :             lhsUnknownDone.get().linkTo(stubcc.masm.label(), &stubcc.masm);
     588                 :         
     589                 :         /* Perform the double operation. */
     590          337785 :         EmitDoubleOp(op, regs.rhsFP, regs.lhsFP, stubcc.masm);
     591                 : 
     592                 :         /* Force the double back to memory. */
     593          337785 :         Address result = frame.addressOf(lhs);
     594          337785 :         stubcc.masm.storeDouble(regs.lhsFP, result);
     595                 : 
     596                 :         /* Load the payload into the result reg so the rejoin is safe. */
     597          337785 :         stubcc.masm.loadPayload(result, regs.result);
     598                 : 
     599                 :         /* We'll link this back up later, at the bottom of the op. */
     600          337785 :         doublePathDone = stubcc.masm.jump();
     601                 :     }
     602                 : 
     603                 :     /* Time to do the integer path. Figure out the immutable side. */
     604          363399 :     int32_t value = 0;
     605          363399 :     JSOp origOp = op;
     606          363399 :     MaybeRegisterID reg;
     607          363399 :     MaybeJump preOverflow;
     608          363399 :     if (!regs.resultHasRhs) {
     609          361456 :         if (!regs.rhsData.isSet())
     610           53301 :             value = rhs->getValue().toInt32();
     611                 :         else
     612          308155 :             reg = regs.rhsData.reg();
     613                 :     } else {
     614            1943 :         if (!regs.lhsData.isSet())
     615            1413 :             value = lhs->getValue().toInt32();
     616                 :         else
     617             530 :             reg = regs.lhsData.reg();
     618            1943 :         if (op == JSOP_SUB) {
     619                 :             // If the RHS is 0x80000000, the smallest negative value, neg does
     620                 :             // not work. Guard against this and treat it as an overflow.
     621             596 :             preOverflow = masm.branch32(Assembler::Equal, regs.result, Imm32(0x80000000));
     622             596 :             masm.neg32(regs.result);
     623             596 :             op = JSOP_ADD;
     624                 :         }
     625                 :     }
     626                 : 
     627                 :     /* Okay - good to emit the integer fast-path. */
     628          363399 :     MaybeJump overflow;
     629          363399 :     switch (op) {
     630                 :       case JSOP_ADD:
     631          324849 :         if (cannotOverflow || ignoreOverflow) {
     632             460 :             if (reg.isSet())
     633             118 :                 masm.add32(reg.reg(), regs.result);
     634                 :             else
     635             112 :                 masm.add32(Imm32(value), regs.result);
     636                 :         } else {
     637          324619 :             if (reg.isSet())
     638          305625 :                 overflow = masm.branchAdd32(Assembler::Overflow, reg.reg(), regs.result);
     639                 :             else
     640           18994 :                 overflow = masm.branchAdd32(Assembler::Overflow, Imm32(value), regs.result);
     641                 :         }
     642          324849 :         break;
     643                 : 
     644                 :       case JSOP_SUB:
     645           34986 :         if (cannotOverflow) {
     646            1139 :             if (reg.isSet())
     647               0 :                 masm.sub32(reg.reg(), regs.result);
     648                 :             else
     649            1139 :                 masm.sub32(Imm32(value), regs.result);
     650                 :         } else {
     651           33847 :             if (reg.isSet())
     652            1175 :                 overflow = masm.branchSub32(Assembler::Overflow, reg.reg(), regs.result);
     653                 :             else
     654           32672 :                 overflow = masm.branchSub32(Assembler::Overflow, Imm32(value), regs.result);
     655                 :         }
     656           34986 :         break;
     657                 : 
     658                 :       case JSOP_MUL:
     659                 :       {
     660            3564 :         MaybeJump storeNegZero;
     661            3564 :         bool maybeNegZero = !ignoreOverflow;
     662            3564 :         bool hasConstant = (lhs->isConstant() || rhs->isConstant());
     663                 : 
     664            3564 :         if (hasConstant && maybeNegZero) {
     665            1794 :             value = (lhs->isConstant() ? lhs : rhs)->getValue().toInt32();
     666            1794 :             RegisterID nonConstReg = lhs->isConstant() ? regs.rhsData.reg() : regs.lhsData.reg();
     667                 : 
     668            1794 :             if (value > 0)
     669            1729 :                 maybeNegZero = false;
     670              65 :             else if (value < 0)
     671              57 :                 storeNegZero = masm.branchTest32(Assembler::Zero, nonConstReg);
     672                 :             else
     673               8 :                 storeNegZero = masm.branch32(Assembler::LessThan, nonConstReg, Imm32(0));
     674                 :         }
     675                 : 
     676            3564 :         if (cannotOverflow) {
     677              12 :             if (reg.isSet())
     678              12 :                 masm.mul32(reg.reg(), regs.result);
     679                 :             else
     680               0 :                 masm.mul32(Imm32(value), regs.result, regs.result);
     681                 :         } else {
     682            3552 :             if (reg.isSet()) {
     683            1755 :                 overflow = masm.branchMul32(Assembler::Overflow, reg.reg(), regs.result);
     684                 :             } else {
     685                 :                 overflow = masm.branchMul32(Assembler::Overflow, Imm32(value), regs.result,
     686            1797 :                                             regs.result);
     687                 :             }
     688                 :         }
     689                 : 
     690            3564 :         if (maybeNegZero) {
     691            1824 :             if (hasConstant) {
     692              65 :                 stubcc.linkExit(storeNegZero.get(), Uses(2));
     693                 :             } else {
     694            1759 :                 Jump isZero = masm.branchTest32(Assembler::Zero, regs.result);
     695            1759 :                 stubcc.linkExitDirect(isZero, stubcc.masm.label());
     696                 : 
     697                 :                 /* Restore original value. */
     698            1759 :                 if (regs.resultHasRhs) {
     699             232 :                     if (regs.rhsNeedsRemat)
     700             232 :                         stubcc.masm.loadPayload(frame.addressForDataRemat(rhs), regs.result);
     701                 :                     else
     702               0 :                         stubcc.masm.move(regs.rhsData.reg(), regs.result);
     703                 :                 } else {
     704            1527 :                     if (regs.lhsNeedsRemat)
     705             446 :                         stubcc.masm.loadPayload(frame.addressForDataRemat(lhs), regs.result);
     706                 :                     else
     707            1081 :                         stubcc.masm.move(regs.lhsData.reg(), regs.result);
     708                 :                 }
     709            1759 :                 storeNegZero = stubcc.masm.branchOr32(Assembler::Signed, reg.reg(), regs.result);
     710            1759 :                 stubcc.masm.xor32(regs.result, regs.result);
     711            1759 :                 stubcc.crossJump(stubcc.masm.jump(), masm.label());
     712            1759 :                 storeNegZero.getJump().linkTo(stubcc.masm.label(), &stubcc.masm);
     713            1759 :                 frame.rematBinary(lhs, rhs, regs, stubcc.masm);
     714                 :             }
     715            1824 :             stubcc.syncExitAndJump(Uses(2));
     716                 :         }
     717            3564 :         break;
     718                 :       }
     719                 : 
     720                 :       default:
     721               0 :         JS_NOT_REACHED("unrecognized op");
     722                 :     }
     723          363399 :     op = origOp;
     724                 :     
     725                 :     /*
     726                 :      * Integer overflow path. Restore the original values and make a stub call,
     727                 :      * which could trigger recompilation.
     728                 :      */
     729          363399 :     MaybeJump overflowDone;
     730          363399 :     if (preOverflow.isSet())
     731             596 :         stubcc.linkExitDirect(preOverflow.get(), stubcc.masm.label());
     732          363399 :     if (overflow.isSet())
     733          362018 :         stubcc.linkExitDirect(overflow.get(), stubcc.masm.label());
     734                 : 
     735                 :     /* Restore the original operand registers for ADD. */
     736          363399 :     if (regs.undoResult) {
     737           41001 :         if (reg.isSet()) {
     738            8634 :             JS_ASSERT(op == JSOP_ADD);
     739            8634 :             stubcc.masm.neg32(reg.reg());
     740            8634 :             stubcc.masm.add32(reg.reg(), regs.result);
     741            8634 :             stubcc.masm.neg32(reg.reg());
     742                 :         } else {
     743           32367 :             JS_ASSERT(op == JSOP_ADD || op == JSOP_SUB);
     744           32367 :             int32_t fixValue = (op == JSOP_ADD) ? -value : value;
     745           32367 :             stubcc.masm.add32(Imm32(fixValue), regs.result);
     746                 :         }
     747                 :     }
     748                 : 
     749          363399 :     frame.rematBinary(lhs, rhs, regs, stubcc.masm);
     750          363399 :     stubcc.syncExitAndJump(Uses(2));
     751                 : 
     752                 :     /* The register allocator creates at most one temporary. */
     753          363399 :     if (regs.extraFree.isSet())
     754               0 :         frame.freeReg(regs.extraFree.reg());
     755                 : 
     756                 :     /* Slow paths funnel here. */
     757          363399 :     if (lhsNotDouble.isSet()) {
     758          336451 :         lhsNotDouble.get().linkTo(stubcc.masm.label(), &stubcc.masm);
     759          336451 :         if (rhsNotNumber.isSet())
     760          304158 :             rhsNotNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm);
     761                 :     }
     762          363399 :     if (rhsNotNumber2.isSet())
     763          305492 :         rhsNotNumber2.get().linkTo(stubcc.masm.label(), &stubcc.masm);
     764                 : 
     765                 :     /* Slow call - use frame.sync to avoid erroneous jump repatching in stubcc. */
     766          363399 :     frame.sync(stubcc.masm, Uses(2));
     767          363399 :     stubcc.leave();
     768          363399 :     OOL_STUBCALL(stub, REJOIN_BINARY);
     769                 : 
     770                 :     /* Finish up stack operations. */
     771          363399 :     frame.popn(2);
     772                 : 
     773                 :     /*
     774                 :      * Steal the result register if we remat the LHS/RHS by undoing the operation.
     775                 :      * In this case the result register was still assigned to the corresponding
     776                 :      * frame entry (so it is synced properly in OOL paths), so steal it back.
     777                 :      */
     778          363399 :     if (regs.undoResult)
     779           41001 :         frame.takeReg(regs.result);
     780                 : 
     781          363399 :     if (type == JSVAL_TYPE_INT32)
     782           29764 :         frame.pushTypedPayload(type, regs.result);
     783                 :     else
     784          333635 :         frame.pushNumber(regs.result, true);
     785                 : 
     786          363399 :     frame.freeReg(regs.lhsFP);
     787          363399 :     frame.freeReg(regs.rhsFP);
     788                 : 
     789                 :     /* Merge back OOL double path. */
     790          363399 :     if (doublePathDone.isSet())
     791          337785 :         stubcc.linkRejoin(doublePathDone.get());
     792                 : 
     793          363399 :     stubcc.rejoin(Changes(1));
     794                 : }
     795                 : 
     796                 : void
     797            3767 : mjit::Compiler::jsop_neg()
     798                 : {
     799            3767 :     FrameEntry *fe = frame.peek(-1);
     800            3767 :     JSValueType type = knownPushedType(0);
     801                 : 
     802            7430 :     if ((fe->isTypeKnown() && fe->getKnownType() > JSVAL_UPPER_INCL_TYPE_OF_NUMBER_SET) ||
     803            3663 :         !masm.supportsFloatingPoint())
     804                 :     {
     805             104 :         prepareStubCall(Uses(1));
     806             104 :         INLINE_STUBCALL(stubs::Neg, REJOIN_FALLTHROUGH);
     807             104 :         frame.pop();
     808             104 :         frame.pushSynced(type);
     809             104 :         return;
     810                 :     }
     811                 : 
     812            3663 :     JS_ASSERT(!fe->isConstant());
     813                 : 
     814                 :     /* Handle negation of a known double, or of a known integer which has previously overflowed. */
     815            6522 :     if (fe->isType(JSVAL_TYPE_DOUBLE) ||
     816            2859 :         (fe->isType(JSVAL_TYPE_INT32) && type == JSVAL_TYPE_DOUBLE))
     817                 :     {
     818                 :         FPRegisterID fpreg;
     819             865 :         if (fe->isType(JSVAL_TYPE_DOUBLE)) {
     820             804 :             fpreg = frame.tempFPRegForData(fe);
     821                 :         } else {
     822              61 :             fpreg = frame.allocFPReg();
     823              61 :             frame.convertInt32ToDouble(masm, fe, fpreg);
     824                 :         }
     825                 : 
     826             865 :         FPRegisterID res = frame.allocFPReg();
     827             865 :         masm.moveDouble(fpreg, res);
     828             865 :         masm.negateDouble(res);
     829                 : 
     830             865 :         if (!fe->isType(JSVAL_TYPE_DOUBLE))
     831              61 :             frame.freeReg(fpreg);
     832                 : 
     833             865 :         frame.pop();
     834             865 :         frame.pushDouble(res);
     835                 : 
     836             865 :         return;
     837                 :     }
     838                 : 
     839                 :     /* Inline integer path for known integers. */
     840            2798 :     if (fe->isType(JSVAL_TYPE_INT32) && type == JSVAL_TYPE_INT32) {
     841             432 :         RegisterID reg = frame.copyDataIntoReg(fe);
     842                 : 
     843                 :         /* Test for 0 and -2147483648 (both result in a double). */
     844             432 :         Jump zeroOrMinInt = masm.branchTest32(Assembler::Zero, reg, Imm32(0x7fffffff));
     845             432 :         stubcc.linkExit(zeroOrMinInt, Uses(1));
     846                 : 
     847             432 :         masm.neg32(reg);
     848                 : 
     849             432 :         stubcc.leave();
     850             432 :         OOL_STUBCALL(stubs::Neg, REJOIN_FALLTHROUGH);
     851                 : 
     852             432 :         frame.pop();
     853             432 :         frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
     854                 : 
     855             432 :         stubcc.rejoin(Changes(1));
     856             432 :         return;
     857                 :     }
     858                 : 
     859                 :     /* Load type information into register. */
     860            2366 :     MaybeRegisterID feTypeReg;
     861            2366 :     if (!fe->isTypeKnown() && !frame.shouldAvoidTypeRemat(fe)) {
     862                 :         /* Safe because only one type is loaded. */
     863            2335 :         feTypeReg.setReg(frame.tempRegForType(fe));
     864                 : 
     865                 :         /* Don't get clobbered by copyDataIntoReg(). */
     866            2335 :         frame.pinReg(feTypeReg.reg());
     867                 :     }
     868                 : 
     869            2366 :     RegisterID reg = frame.copyDataIntoReg(masm, fe);
     870            2366 :     Label feSyncTarget = stubcc.syncExitAndJump(Uses(1));
     871                 : 
     872                 :     /* Try a double path (inline). */
     873            2366 :     MaybeJump jmpNotDbl;
     874                 :     {
     875            2366 :         maybeJumpIfNotDouble(masm, jmpNotDbl, fe, feTypeReg);
     876                 : 
     877            2366 :         FPRegisterID fpreg = frame.allocFPReg();
     878            2366 :         frame.loadDouble(fe, fpreg, masm);
     879            2366 :         masm.negateDouble(fpreg);
     880                 : 
     881                 :         /* Overwrite pushed frame's memory (before push). */
     882            2366 :         masm.storeDouble(fpreg, frame.addressOf(fe));
     883            2366 :         frame.freeReg(fpreg);
     884                 :     }
     885                 : 
     886                 :     /* Try an integer path (out-of-line). */
     887            2366 :     MaybeJump jmpNotInt;
     888            2366 :     MaybeJump jmpMinIntOrIntZero;
     889            2366 :     MaybeJump jmpIntRejoin;
     890            2366 :     Label lblIntPath = stubcc.masm.label();
     891                 :     {
     892            2366 :         maybeJumpIfNotInt32(stubcc.masm, jmpNotInt, fe, feTypeReg);
     893                 : 
     894                 :         /* Test for 0 and -2147483648 (both result in a double). */
     895            2366 :         jmpMinIntOrIntZero = stubcc.masm.branchTest32(Assembler::Zero, reg, Imm32(0x7fffffff));
     896                 : 
     897            2366 :         stubcc.masm.neg32(reg);
     898                 : 
     899                 :         /* Sync back with double path. */
     900            2366 :         if (type == JSVAL_TYPE_DOUBLE) {
     901              41 :             stubcc.masm.convertInt32ToDouble(reg, Registers::FPConversionTemp);
     902              41 :             stubcc.masm.storeDouble(Registers::FPConversionTemp, frame.addressOf(fe));
     903                 :         } else {
     904                 :             stubcc.masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), reg,
     905            2325 :                                                  frame.addressOf(fe));
     906                 :         }
     907                 : 
     908            2366 :         jmpIntRejoin.setJump(stubcc.masm.jump());
     909                 :     }
     910                 : 
     911            2366 :     frame.freeReg(reg);
     912            2366 :     if (feTypeReg.isSet())
     913            2335 :         frame.unpinReg(feTypeReg.reg());
     914                 : 
     915            2366 :     stubcc.leave();
     916            2366 :     OOL_STUBCALL(stubs::Neg, REJOIN_FALLTHROUGH);
     917                 : 
     918            2366 :     frame.pop();
     919            2366 :     frame.pushSynced(type);
     920                 : 
     921                 :     /* Link jumps. */
     922            2366 :     if (jmpNotDbl.isSet())
     923            2366 :         stubcc.linkExitDirect(jmpNotDbl.getJump(), lblIntPath);
     924                 : 
     925            2366 :     if (jmpNotInt.isSet())
     926            2363 :         jmpNotInt.getJump().linkTo(feSyncTarget, &stubcc.masm);
     927            2366 :     if (jmpMinIntOrIntZero.isSet())
     928            2366 :         jmpMinIntOrIntZero.getJump().linkTo(feSyncTarget, &stubcc.masm);
     929            2366 :     if (jmpIntRejoin.isSet())
     930            2366 :         stubcc.crossJump(jmpIntRejoin.getJump(), masm.label());
     931                 : 
     932            2366 :     stubcc.rejoin(Changes(1));
     933                 : }
     934                 : 
     935                 : bool
     936            1090 : mjit::Compiler::jsop_mod()
     937                 : {
     938                 : #if defined(JS_CPU_X86) || defined(JS_CPU_X64)
     939            1090 :     JSValueType type = knownPushedType(0);
     940            1090 :     FrameEntry *lhs = frame.peek(-2);
     941            1090 :     FrameEntry *rhs = frame.peek(-1);
     942                 : 
     943                 :     Value v;
     944            1090 :     if (tryBinaryConstantFold(cx, frame, JSOP_MOD, lhs, rhs, &v)) {
     945               8 :         types::TypeSet *pushed = pushedTypeSet(0);
     946               8 :         if (!v.isInt32() && pushed && !pushed->hasType(types::Type::DoubleType())) {
     947               2 :             types::TypeScript::MonitorOverflow(cx, script, PC);
     948               2 :             return false;
     949                 :         }
     950               6 :         frame.popn(2);
     951               6 :         frame.push(v);
     952               6 :         return true;
     953                 :     }
     954                 : 
     955            4675 :     if ((lhs->isConstant() && rhs->isConstant()) ||
     956            1682 :         (lhs->isTypeKnown() && lhs->getKnownType() != JSVAL_TYPE_INT32) ||
     957            1911 :         (rhs->isTypeKnown() && rhs->getKnownType() != JSVAL_TYPE_INT32) ||
     958                 :         (type != JSVAL_TYPE_INT32 && type != JSVAL_TYPE_UNKNOWN))
     959                 : #endif
     960                 :     {
     961              74 :         prepareStubCall(Uses(2));
     962              74 :         INLINE_STUBCALL(stubs::Mod, REJOIN_FALLTHROUGH);
     963              74 :         frame.popn(2);
     964              74 :         frame.pushSynced(knownPushedType(0));
     965              74 :         return true;
     966                 :     }
     967                 : 
     968                 : #if defined(JS_CPU_X86) || defined(JS_CPU_X64)
     969            1008 :     if (!lhs->isTypeKnown()) {
     970             466 :         Jump j = frame.testInt32(Assembler::NotEqual, lhs);
     971             466 :         stubcc.linkExit(j, Uses(2));
     972                 :     }
     973            1008 :     if (!rhs->isTypeKnown()) {
     974             158 :         Jump j = frame.testInt32(Assembler::NotEqual, rhs);
     975             158 :         stubcc.linkExit(j, Uses(2));
     976                 :     }
     977                 : 
     978                 :     /* LHS must be in EAX:EDX */
     979            1008 :     if (!lhs->isConstant()) {
     980            1000 :         frame.copyDataIntoReg(lhs, X86Registers::eax);
     981                 :     } else {
     982               8 :         frame.takeReg(X86Registers::eax);
     983               8 :         masm.move(Imm32(lhs->getValue().toInt32()), X86Registers::eax);
     984                 :     }
     985                 : 
     986                 :     /* Get RHS into anything but EDX - could avoid more spilling? */
     987            1008 :     MaybeRegisterID temp;
     988                 :     RegisterID rhsReg;
     989            1008 :     uint32_t mask = Registers::AvailRegs & ~Registers::maskReg(X86Registers::edx);
     990            1008 :     if (!rhs->isConstant()) {
     991             303 :         rhsReg = frame.tempRegInMaskForData(rhs, mask).reg();
     992             303 :         JS_ASSERT(rhsReg != X86Registers::edx);
     993                 :     } else {
     994             705 :         rhsReg = frame.allocReg(mask).reg();
     995             705 :         JS_ASSERT(rhsReg != X86Registers::edx);
     996             705 :         masm.move(Imm32(rhs->getValue().toInt32()), rhsReg);
     997             705 :         temp = rhsReg;
     998                 :     }
     999            1008 :     frame.takeReg(X86Registers::edx);
    1000            1008 :     frame.freeReg(X86Registers::eax);
    1001                 : 
    1002            1008 :     if (temp.isSet())
    1003             705 :         frame.freeReg(temp.reg());
    1004                 : 
    1005            1008 :     bool slowPath = !(lhs->isTypeKnown() && rhs->isTypeKnown());
    1006            1008 :     if (rhs->isConstant() && rhs->getValue().toInt32() != 0) {
    1007             699 :         if (rhs->getValue().toInt32() == -1) {
    1008                 :             /* Guard against -1 / INT_MIN which throws a hardware exception. */
    1009                 :             Jump checkDivExc = masm.branch32(Assembler::Equal, X86Registers::eax,
    1010               4 :                                              Imm32(0x80000000));
    1011               4 :             stubcc.linkExit(checkDivExc, Uses(2));
    1012               4 :             slowPath = true;
    1013                 :         }
    1014                 :     } else {
    1015             309 :         Jump checkDivExc = masm.branch32(Assembler::Equal, X86Registers::eax, Imm32(0x80000000));
    1016             309 :         stubcc.linkExit(checkDivExc, Uses(2));
    1017             309 :         Jump checkZero = masm.branchTest32(Assembler::Zero, rhsReg, rhsReg);
    1018             309 :         stubcc.linkExit(checkZero, Uses(2));
    1019             309 :         slowPath = true;
    1020                 :     }
    1021                 : 
    1022                 :     /* Perform division. */
    1023            1008 :     masm.idiv(rhsReg);
    1024                 : 
    1025                 :     /* ECMA-262 11.5.3 requires the result to have the same sign as the lhs.
    1026                 :      * Thus, if the remainder of the div instruction is zero and the lhs is
    1027                 :      * negative, we must return negative 0. */
    1028                 : 
    1029            1008 :     bool lhsMaybeNeg = true;
    1030            1008 :     bool lhsIsNeg = false;
    1031            1008 :     if (lhs->isConstant()) {
    1032                 :         /* This condition is established at the top of this function. */
    1033               8 :         JS_ASSERT(lhs->getValue().isInt32());
    1034               8 :         lhsMaybeNeg = lhsIsNeg = (lhs->getValue().toInt32() < 0);
    1035                 :     }
    1036                 : 
    1037            1008 :     MaybeJump gotNegZero;
    1038            1008 :     MaybeJump done;
    1039            1008 :     if (lhsMaybeNeg) {
    1040            1000 :         MaybeRegisterID lhsData;
    1041            1000 :         if (!lhsIsNeg)
    1042            1000 :             lhsData = frame.tempRegForData(lhs);
    1043            1000 :         Jump negZero1 = masm.branchTest32(Assembler::NonZero, X86Registers::edx);
    1044            1000 :         MaybeJump negZero2;
    1045            1000 :         if (!lhsIsNeg)
    1046            1000 :             negZero2 = masm.branchTest32(Assembler::Zero, lhsData.reg(), Imm32(0x80000000));
    1047                 :         /*
    1048                 :          * Darn, negative 0. This goes to a stub call (after our in progress call)
    1049                 :          * which triggers recompilation if necessary.
    1050                 :          */
    1051            1000 :         gotNegZero = masm.jump();
    1052                 : 
    1053                 :         /* :TODO: This is wrong, must load into EDX as well. */
    1054                 : 
    1055            1000 :         done = masm.jump();
    1056            1000 :         negZero1.linkTo(masm.label(), &masm);
    1057            1000 :         if (negZero2.isSet())
    1058            1000 :             negZero2.getJump().linkTo(masm.label(), &masm);
    1059                 :     }
    1060                 : 
    1061                 :     /* Better - integer. */
    1062            1008 :     masm.storeTypeTag(ImmType(JSVAL_TYPE_INT32), frame.addressOf(lhs));
    1063                 : 
    1064            1008 :     if (done.isSet())
    1065            1000 :         done.getJump().linkTo(masm.label(), &masm);
    1066                 : 
    1067            1008 :     if (slowPath) {
    1068             667 :         stubcc.leave();
    1069             667 :         OOL_STUBCALL(stubs::Mod, REJOIN_FALLTHROUGH);
    1070                 :     }
    1071                 : 
    1072            1008 :     frame.popn(2);
    1073                 : 
    1074            1008 :     if (type == JSVAL_TYPE_INT32)
    1075             749 :         frame.pushTypedPayload(type, X86Registers::edx);
    1076                 :     else
    1077             259 :         frame.pushNumber(X86Registers::edx);
    1078                 : 
    1079            1008 :     if (slowPath)
    1080             667 :         stubcc.rejoin(Changes(1));
    1081                 : 
    1082            1008 :     if (gotNegZero.isSet()) {
    1083            1000 :         stubcc.linkExit(gotNegZero.getJump(), Uses(2));
    1084            1000 :         stubcc.leave();
    1085            1000 :         OOL_STUBCALL(stubs::NegZeroHelper, REJOIN_FALLTHROUGH);
    1086            1000 :         stubcc.rejoin(Changes(1));
    1087                 :     }
    1088                 : #endif
    1089                 : 
    1090            1008 :     return true;
    1091                 : }
    1092                 : 
    1093                 : bool
    1094            7299 : mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub,
    1095                 :                                          jsbytecode *target, JSOp fused)
    1096                 : {
    1097            7299 :     FrameEntry *rhs = frame.peek(-1);
    1098            7299 :     FrameEntry *lhs = frame.peek(-2);
    1099                 : 
    1100                 :     /* Swap the LHS and RHS if it makes register allocation better... or possible. */
    1101           14812 :     if (lhs->isConstant() ||
    1102            7513 :         (frame.shouldAvoidDataRemat(lhs) && !rhs->isConstant())) {
    1103             238 :         FrameEntry *temp = rhs;
    1104             238 :         rhs = lhs;
    1105             238 :         lhs = temp;
    1106                 :     }
    1107                 : 
    1108            7299 :     bool lhsInt = lhs->isType(JSVAL_TYPE_INT32);
    1109            7299 :     bool rhsInt = rhs->isType(JSVAL_TYPE_INT32);
    1110                 : 
    1111                 :     /* Invert the condition if fusing with an IFEQ branch. */
    1112            7299 :     bool flipCondition = (target && fused == JSOP_IFEQ);
    1113                 : 
    1114                 :     /* Get the condition being tested. */
    1115                 :     Assembler::Condition cond;
    1116            7299 :     switch (op) {
    1117                 :       case JSOP_EQ:
    1118            3626 :         cond = flipCondition ? Assembler::NotEqual : Assembler::Equal;
    1119            3626 :         break;
    1120                 :       case JSOP_NE:
    1121            3673 :         cond = flipCondition ? Assembler::Equal : Assembler::NotEqual;
    1122            3673 :         break;
    1123                 :       default:
    1124               0 :         JS_NOT_REACHED("wat");
    1125                 :         return false;
    1126                 :     }
    1127                 : 
    1128            7299 :     if (target) {
    1129            5356 :         Value rval = UndefinedValue();  /* quiet gcc warning */
    1130            5356 :         bool rhsConst = false;
    1131            5356 :         if (rhs->isConstant()) {
    1132            2880 :             rhsConst = true;
    1133            2880 :             rval = rhs->getValue();
    1134                 :         }
    1135                 : 
    1136                 :         ValueRemat lvr, rvr;
    1137            5356 :         frame.pinEntry(lhs, lvr);
    1138            5356 :         frame.pinEntry(rhs, rvr);
    1139                 : 
    1140                 :         /*
    1141                 :          * Sync everything except the top two entries.
    1142                 :          * We will handle the lhs/rhs in the stub call path.
    1143                 :          */
    1144            5356 :         frame.syncAndKill(Registers(Registers::AvailRegs), Uses(frame.frameSlots()), Uses(2));
    1145                 : 
    1146            5356 :         RegisterID tempReg = frame.allocReg();
    1147                 : 
    1148            5356 :         JaegerSpew(JSpew_Insns, " ---- BEGIN STUB CALL CODE ---- \n");
    1149                 : 
    1150                 :         RESERVE_OOL_SPACE(stubcc.masm);
    1151                 : 
    1152                 :         /* Start of the slow path for equality stub call. */
    1153            5356 :         Label stubEntry = stubcc.masm.label();
    1154                 : 
    1155                 :         /* The lhs/rhs need to be synced in the stub call path. */
    1156            5356 :         frame.ensureValueSynced(stubcc.masm, lhs, lvr);
    1157            5356 :         frame.ensureValueSynced(stubcc.masm, rhs, rvr);
    1158                 : 
    1159            5356 :         bool needIntPath = (!lhs->isTypeKnown() || lhsInt) && (!rhs->isTypeKnown() || rhsInt);
    1160                 : 
    1161            5356 :         frame.pop();
    1162            5356 :         frame.pop();
    1163            5356 :         frame.discardFrame();
    1164                 : 
    1165            5356 :         bool needStub = true;
    1166                 :         
    1167                 : #ifdef JS_MONOIC
    1168            5356 :         EqualityGenInfo ic;
    1169                 : 
    1170            5356 :         ic.cond = cond;
    1171            5356 :         ic.tempReg = tempReg;
    1172            5356 :         ic.lvr = lvr;
    1173            5356 :         ic.rvr = rvr;
    1174            5356 :         ic.stubEntry = stubEntry;
    1175            5356 :         ic.stub = stub;
    1176                 : 
    1177            5356 :         bool useIC = !a->parent && bytecodeInChunk(target);
    1178                 : 
    1179                 :         /* Call the IC stub, which may generate a fast path. */
    1180            5356 :         if (useIC) {
    1181                 :             /* Adjust for the two values just pushed. */
    1182            5127 :             ic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1);
    1183            5127 :             ic.stubCall = OOL_STUBCALL_LOCAL_SLOTS(ic::Equality, REJOIN_BRANCH,
    1184            5127 :                                                    frame.totalDepth() + 2);
    1185            5127 :             needStub = false;
    1186                 :         }
    1187                 : #endif
    1188                 : 
    1189            5356 :         if (needStub)
    1190             229 :             OOL_STUBCALL_LOCAL_SLOTS(stub, REJOIN_BRANCH, frame.totalDepth() + 2);
    1191                 : 
    1192                 :         /*
    1193                 :          * The stub call has no need to rejoin, since state is synced.
    1194                 :          * Instead, we can just test the return value.
    1195                 :          */
    1196                 :         Jump stubBranch = stubcc.masm.branchTest32(GetStubCompareCondition(fused),
    1197            5356 :                                                    Registers::ReturnReg, Registers::ReturnReg);
    1198            5356 :         Jump stubFallthrough = stubcc.masm.jump();
    1199                 : 
    1200            5356 :         JaegerSpew(JSpew_Insns, " ---- END STUB CALL CODE ---- \n");
    1201                 :         CHECK_OOL_SPACE();
    1202                 : 
    1203            5356 :         Jump fast;
    1204            5356 :         MaybeJump firstStubJump;
    1205                 : 
    1206            5356 :         if (needIntPath) {
    1207            4966 :             if (!lhsInt) {
    1208            2964 :                 Jump lhsFail = masm.testInt32(Assembler::NotEqual, lvr.typeReg());
    1209            2964 :                 stubcc.linkExitDirect(lhsFail, stubEntry);
    1210            2964 :                 firstStubJump = lhsFail;
    1211                 :             }
    1212            4966 :             if (!rhsInt) {
    1213            1420 :                 Jump rhsFail = masm.testInt32(Assembler::NotEqual, rvr.typeReg());
    1214            1420 :                 stubcc.linkExitDirect(rhsFail, stubEntry);
    1215            1420 :                 if (!firstStubJump.isSet())
    1216              23 :                     firstStubJump = rhsFail;
    1217                 :             }
    1218                 : 
    1219            4966 :             if (rhsConst)
    1220            2533 :                 fast = masm.branch32(cond, lvr.dataReg(), Imm32(rval.toInt32()));
    1221                 :             else
    1222            2433 :                 fast = masm.branch32(cond, lvr.dataReg(), rvr.dataReg());
    1223                 :         } else {
    1224             390 :             Jump j = masm.jump();
    1225             390 :             stubcc.linkExitDirect(j, stubEntry);
    1226             390 :             firstStubJump = j;
    1227                 : 
    1228                 :             /* This is just a dummy jump. */
    1229             390 :             fast = masm.jump();
    1230                 :         }
    1231                 : 
    1232                 :         /* Jump from the stub call fallthrough to here. */
    1233            5356 :         stubcc.crossJump(stubFallthrough, masm.label());
    1234                 : 
    1235            5356 :         bool *ptrampoline = NULL;
    1236                 : #ifdef JS_MONOIC
    1237                 :         /* Remember the stub label in case there is a trampoline for the IC. */
    1238            5356 :         ic.trampoline = false;
    1239            5356 :         ic.trampolineStart = stubcc.masm.label();
    1240            5356 :         if (useIC)
    1241            5127 :             ptrampoline = &ic.trampoline;
    1242                 : #endif
    1243                 : 
    1244                 :         /*
    1245                 :          * NB: jumpAndRun emits to the OOL path, so make sure not to use it
    1246                 :          * in the middle of an in-progress slow path.
    1247                 :          */
    1248            5356 :         if (!jumpAndRun(fast, target, &stubBranch, ptrampoline))
    1249               0 :             return false;
    1250                 : 
    1251                 : #ifdef JS_MONOIC
    1252            5356 :         if (useIC) {
    1253            5127 :             ic.jumpToStub = firstStubJump;
    1254            5127 :             ic.fallThrough = masm.label();
    1255            5127 :             ic.jumpTarget = target;
    1256            5127 :             equalityICs.append(ic);
    1257                 :         }
    1258                 : #endif
    1259                 : 
    1260                 :     } else {
    1261                 :         /* No fusing. Compare, set, and push a boolean. */
    1262                 : 
    1263                 :         /* Should have filtered these out in the caller. */
    1264            1943 :         JS_ASSERT(!lhs->isType(JSVAL_TYPE_STRING) && !rhs->isType(JSVAL_TYPE_STRING));
    1265                 : 
    1266                 :         /* Test the types. */
    1267            1943 :         if ((lhs->isTypeKnown() && !lhsInt) || (rhs->isTypeKnown() && !rhsInt)) {
    1268               0 :             stubcc.linkExit(masm.jump(), Uses(2));
    1269                 :         } else {
    1270            1943 :             if (!lhsInt) {
    1271            1221 :                 Jump lhsFail = frame.testInt32(Assembler::NotEqual, lhs);
    1272            1221 :                 stubcc.linkExit(lhsFail, Uses(2));
    1273                 :             }
    1274            1943 :             if (!rhsInt) {
    1275             580 :                 Jump rhsFail = frame.testInt32(Assembler::NotEqual, rhs);
    1276             580 :                 stubcc.linkExit(rhsFail, Uses(2));
    1277                 :             }
    1278                 :         }
    1279                 : 
    1280            1943 :         stubcc.leave();
    1281            1943 :         OOL_STUBCALL(stub, REJOIN_FALLTHROUGH);
    1282                 : 
    1283            1943 :         RegisterID reg = frame.ownRegForData(lhs);
    1284                 : 
    1285                 :         /* x86/64's SET instruction can only take single-byte regs.*/
    1286            1943 :         RegisterID resultReg = reg;
    1287            1943 :         if (!(Registers::maskReg(reg) & Registers::SingleByteRegs))
    1288            1331 :             resultReg = frame.allocReg(Registers::SingleByteRegs).reg();
    1289                 : 
    1290                 :         /* Emit the compare & set. */
    1291            1943 :         if (rhs->isConstant()) {
    1292            1095 :             masm.set32(cond, reg, Imm32(rhs->getValue().toInt32()), resultReg);
    1293             848 :         } else if (frame.shouldAvoidDataRemat(rhs)) {
    1294                 :             masm.set32(cond, reg,
    1295             199 :                        masm.payloadOf(frame.addressOf(rhs)),
    1296             199 :                        resultReg);
    1297                 :         } else {
    1298             649 :             masm.set32(cond, reg, frame.tempRegForData(rhs), resultReg);
    1299                 :         }
    1300                 : 
    1301                 :         /* Clean up and push a boolean. */
    1302            1943 :         frame.pop();
    1303            1943 :         frame.pop();
    1304            1943 :         if (reg != resultReg)
    1305            1331 :             frame.freeReg(reg);
    1306            1943 :         frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, resultReg);
    1307            1943 :         stubcc.rejoin(Changes(1));
    1308                 :     }
    1309            7299 :     return true;
    1310                 : }
    1311                 : 
    1312                 : /*
    1313                 :  * Emit an OOL path for a possibly double LHS, and possibly int32_t or number RHS.
    1314                 :  */
    1315                 : void
    1316          361528 : mjit::Compiler::emitLeftDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState::BinaryAlloc &regs,
    1317                 :                                    MaybeJump &lhsNotDouble, MaybeJump &rhsNotNumber,
    1318                 :                                    MaybeJump &lhsUnknownDone)
    1319                 : {
    1320                 :     /* If the LHS is not a 32-bit integer, take OOL path. */
    1321          361528 :     Jump lhsNotInt32 = masm.testInt32(Assembler::NotEqual, regs.lhsType.reg());
    1322          361528 :     stubcc.linkExitDirect(lhsNotInt32, stubcc.masm.label());
    1323                 : 
    1324          361528 :     if (!masm.supportsFloatingPoint()) {
    1325               0 :         lhsNotDouble = stubcc.masm.jump();
    1326               0 :         return;
    1327                 :     }
    1328                 : 
    1329                 :     /* OOL path for LHS as a double - first test LHS is double. */
    1330          361528 :     lhsNotDouble = stubcc.masm.testDouble(Assembler::NotEqual, regs.lhsType.reg());
    1331                 : 
    1332                 :     /* Ensure the RHS is a number. */
    1333          361528 :     MaybeJump rhsIsDouble;
    1334          361528 :     if (!rhs->isTypeKnown()) {
    1335          312220 :         rhsIsDouble = stubcc.masm.testDouble(Assembler::Equal, regs.rhsType.reg());
    1336          312220 :         rhsNotNumber = stubcc.masm.testInt32(Assembler::NotEqual, regs.rhsType.reg());
    1337                 :     }
    1338                 : 
    1339                 :     /* If RHS is constant, convert now. */
    1340          361528 :     if (rhs->isConstant())
    1341           43533 :         slowLoadConstantDouble(stubcc.masm, rhs, regs.rhsFP);
    1342                 :     else
    1343          317995 :         stubcc.masm.convertInt32ToDouble(regs.rhsData.reg(), regs.rhsFP);
    1344                 : 
    1345          361528 :     if (!rhs->isTypeKnown()) {
    1346                 :         /* Jump past double load, bind double type check. */
    1347          312220 :         Jump converted = stubcc.masm.jump();
    1348          312220 :         rhsIsDouble.get().linkTo(stubcc.masm.label(), &stubcc.masm);
    1349                 : 
    1350                 :         /* Load the double. */
    1351                 :         frame.loadDouble(regs.rhsType.reg(), regs.rhsData.reg(),
    1352          312220 :                          rhs, regs.rhsFP, stubcc.masm);
    1353                 : 
    1354          312220 :         converted.linkTo(stubcc.masm.label(), &stubcc.masm);
    1355                 :     }
    1356                 : 
    1357                 :     /* Load the LHS. */
    1358                 :     frame.loadDouble(regs.lhsType.reg(), regs.lhsData.reg(),
    1359          361528 :                      lhs, regs.lhsFP, stubcc.masm);
    1360          361528 :     lhsUnknownDone = stubcc.masm.jump();
    1361                 : }
    1362                 : 
    1363                 : /*
    1364                 :  * Emit an OOL path for an integer LHS, possibly double RHS.
    1365                 :  */
    1366                 : void
    1367          314647 : mjit::Compiler::emitRightDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState::BinaryAlloc &regs,
    1368                 :                                     MaybeJump &rhsNotNumber2)
    1369                 : {
    1370                 :     /* If the RHS is not a double, take OOL path. */
    1371          314647 :     Jump notInt32 = masm.testInt32(Assembler::NotEqual, regs.rhsType.reg());
    1372          314647 :     stubcc.linkExitDirect(notInt32, stubcc.masm.label());
    1373                 : 
    1374          314647 :     if (!masm.supportsFloatingPoint()) {
    1375               0 :         rhsNotNumber2 = stubcc.masm.jump();
    1376               0 :         return;
    1377                 :     }
    1378                 : 
    1379                 :     /* Now test if RHS is a double. */
    1380          314647 :     rhsNotNumber2 = stubcc.masm.testDouble(Assembler::NotEqual, regs.rhsType.reg());
    1381                 : 
    1382                 :     /* We know LHS is an integer. */
    1383          314647 :     if (lhs->isConstant())
    1384             893 :         slowLoadConstantDouble(stubcc.masm, lhs, regs.lhsFP);
    1385                 :     else
    1386          313754 :         stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), regs.lhsFP);
    1387                 : 
    1388                 :     /* Load the RHS. */
    1389                 :     frame.loadDouble(regs.rhsType.reg(), regs.rhsData.reg(),
    1390          314647 :                      rhs, regs.rhsFP, stubcc.masm);
    1391                 : }
    1392                 : 
    1393                 : static inline Assembler::DoubleCondition
    1394           27760 : DoubleCondForOp(JSOp op, JSOp fused)
    1395                 : {
    1396           27760 :     bool ifeq = fused == JSOP_IFEQ;
    1397           27760 :     switch (op) {
    1398                 :       case JSOP_GT:
    1399                 :         return ifeq 
    1400                 :                ? Assembler::DoubleLessThanOrEqualOrUnordered
    1401            2246 :                : Assembler::DoubleGreaterThan;
    1402                 :       case JSOP_GE:
    1403                 :         return ifeq
    1404                 :                ? Assembler::DoubleLessThanOrUnordered
    1405            1598 :                : Assembler::DoubleGreaterThanOrEqual;
    1406                 :       case JSOP_LT:
    1407                 :         return ifeq
    1408                 :                ? Assembler::DoubleGreaterThanOrEqualOrUnordered
    1409           22788 :                : Assembler::DoubleLessThan;
    1410                 :       case JSOP_LE:
    1411                 :         return ifeq
    1412                 :                ? Assembler::DoubleGreaterThanOrUnordered
    1413            1128 :                : Assembler::DoubleLessThanOrEqual;
    1414                 :       default:
    1415               0 :         JS_NOT_REACHED("unrecognized op");
    1416                 :         return Assembler::DoubleLessThan;
    1417                 :     }
    1418                 : }
    1419                 : 
    1420                 : bool
    1421            1579 : mjit::Compiler::jsop_relational_double(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused)
    1422                 : {
    1423            1579 :     FrameEntry *rhs = frame.peek(-1);
    1424            1579 :     FrameEntry *lhs = frame.peek(-2);
    1425                 : 
    1426            1579 :     JS_ASSERT_IF(!target, fused != JSOP_IFEQ);
    1427                 : 
    1428                 :     FPRegisterID fpLeft, fpRight;
    1429                 :     bool allocateLeft, allocateRight;
    1430                 : 
    1431            1579 :     MaybeJump lhsNotNumber = loadDouble(lhs, &fpLeft, &allocateLeft);
    1432            1579 :     if (lhsNotNumber.isSet()) {
    1433             160 :         if (target)
    1434             111 :             stubcc.linkExitForBranch(lhsNotNumber.get());
    1435                 :         else
    1436              49 :             stubcc.linkExit(lhsNotNumber.get(), Uses(2));
    1437                 :     }
    1438            1579 :     if (!allocateLeft)
    1439            1195 :         frame.pinReg(fpLeft);
    1440                 : 
    1441            1579 :     MaybeJump rhsNotNumber = loadDouble(rhs, &fpRight, &allocateRight);
    1442            1579 :     if (rhsNotNumber.isSet()) {
    1443              49 :         if (target)
    1444              18 :             stubcc.linkExitForBranch(rhsNotNumber.get());
    1445                 :         else
    1446              31 :             stubcc.linkExit(rhsNotNumber.get(), Uses(2));
    1447                 :     }
    1448            1579 :     if (!allocateLeft)
    1449            1195 :         frame.unpinReg(fpLeft);
    1450                 : 
    1451            1579 :     Assembler::DoubleCondition dblCond = DoubleCondForOp(op, fused);
    1452                 : 
    1453            1579 :     if (target) {
    1454            1131 :         stubcc.leave();
    1455            1131 :         OOL_STUBCALL(stub, REJOIN_BRANCH);
    1456                 : 
    1457            1131 :         if (!allocateLeft)
    1458             860 :             frame.pinReg(fpLeft);
    1459            1131 :         if (!allocateRight)
    1460             742 :             frame.pinReg(fpRight);
    1461                 : 
    1462            1131 :         frame.syncAndKillEverything();
    1463                 : 
    1464            1131 :         Jump j = masm.branchDouble(dblCond, fpLeft, fpRight);
    1465                 : 
    1466            1131 :         if (allocateLeft)
    1467             271 :             frame.freeReg(fpLeft);
    1468                 :         else
    1469             860 :             frame.unpinKilledReg(fpLeft);
    1470                 : 
    1471            1131 :         if (allocateRight)
    1472             389 :             frame.freeReg(fpRight);
    1473                 :         else
    1474             742 :             frame.unpinKilledReg(fpRight);
    1475                 : 
    1476            1131 :         frame.popn(2);
    1477                 : 
    1478                 :         Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused),
    1479            1131 :                                            Registers::ReturnReg, Registers::ReturnReg);
    1480                 : 
    1481                 :         /* Rejoin from the slow path. */
    1482            1131 :         stubcc.rejoin(Changes(0));
    1483                 : 
    1484                 :         /*
    1485                 :          * NB: jumpAndRun emits to the OOL path, so make sure not to use it
    1486                 :          * in the middle of an in-progress slow path.
    1487                 :          */
    1488            1131 :         if (!jumpAndRun(j, target, &sj))
    1489               0 :             return false;
    1490                 :     } else {
    1491             448 :         stubcc.leave();
    1492             448 :         OOL_STUBCALL(stub, REJOIN_FALLTHROUGH);
    1493                 : 
    1494             448 :         frame.popn(2);
    1495                 : 
    1496             448 :         RegisterID reg = frame.allocReg();
    1497             448 :         Jump j = masm.branchDouble(dblCond, fpLeft, fpRight);
    1498             448 :         masm.move(Imm32(0), reg);
    1499             448 :         Jump skip = masm.jump();
    1500             448 :         j.linkTo(masm.label(), &masm);
    1501             448 :         masm.move(Imm32(1), reg);
    1502             448 :         skip.linkTo(masm.label(), &masm);
    1503                 : 
    1504             448 :         frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, reg);
    1505                 : 
    1506             448 :         stubcc.rejoin(Changes(1));
    1507                 : 
    1508             448 :         if (allocateLeft)
    1509             113 :             frame.freeReg(fpLeft);
    1510             448 :         if (allocateRight)
    1511             318 :             frame.freeReg(fpRight);
    1512                 :     }
    1513                 : 
    1514            1579 :     return true;
    1515                 : }
    1516                 : 
    1517                 : bool
    1518           16134 : mjit::Compiler::jsop_relational_int(JSOp op, jsbytecode *target, JSOp fused)
    1519                 : {
    1520           16134 :     FrameEntry *rhs = frame.peek(-1);
    1521           16134 :     FrameEntry *lhs = frame.peek(-2);
    1522                 : 
    1523                 :     /* Reverse N cmp A comparisons.  The left side must be in a register. */
    1524           16134 :     if (lhs->isConstant()) {
    1525               0 :         JS_ASSERT(!rhs->isConstant());
    1526               0 :         FrameEntry *tmp = lhs;
    1527               0 :         lhs = rhs;
    1528               0 :         rhs = tmp;
    1529               0 :         op = ReverseCompareOp(op);
    1530                 :     }
    1531                 : 
    1532           16134 :     JS_ASSERT_IF(!target, fused != JSOP_IFEQ);
    1533           16134 :     Assembler::Condition cond = GetCompareCondition(op, fused);
    1534                 : 
    1535           16134 :     if (target) {
    1536           15550 :         if (!frame.syncForBranch(target, Uses(2)))
    1537               0 :             return false;
    1538                 : 
    1539           15550 :         RegisterID lreg = frame.tempRegForData(lhs);
    1540           15550 :         Jump fast;
    1541           15550 :         if (rhs->isConstant()) {
    1542           11257 :             fast = masm.branch32(cond, lreg, Imm32(rhs->getValue().toInt32()));
    1543                 :         } else {
    1544            4293 :             frame.pinReg(lreg);
    1545            4293 :             RegisterID rreg = frame.tempRegForData(rhs);
    1546            4293 :             frame.unpinReg(lreg);
    1547            4293 :             fast = masm.branch32(cond, lreg, rreg);
    1548                 :         }
    1549           15550 :         frame.popn(2);
    1550                 : 
    1551                 :         Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused),
    1552           15550 :                                            Registers::ReturnReg, Registers::ReturnReg);
    1553                 : 
    1554           15550 :         return jumpAndRun(fast, target, &sj);
    1555                 :     } else {
    1556             584 :         RegisterID result = frame.allocReg();
    1557             584 :         RegisterID lreg = frame.tempRegForData(lhs);
    1558                 : 
    1559             584 :         if (rhs->isConstant()) {
    1560             414 :             masm.branchValue(cond, lreg, rhs->getValue().toInt32(), result);
    1561                 :         } else {
    1562             170 :             frame.pinReg(lreg);
    1563             170 :             RegisterID rreg = frame.tempRegForData(rhs);
    1564             170 :             frame.unpinReg(lreg);
    1565             170 :             masm.branchValue(cond, lreg, rreg, result);
    1566                 :         }
    1567                 : 
    1568             584 :         frame.popn(2);
    1569             584 :         frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
    1570                 :     }
    1571                 : 
    1572             584 :     return true;
    1573                 : }
    1574                 : 
    1575                 : /* See jsop_binary_full() for more information on how this works. */
    1576                 : bool
    1577           26181 : mjit::Compiler::jsop_relational_full(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused)
    1578                 : {
    1579           26181 :     FrameEntry *rhs = frame.peek(-1);
    1580           26181 :     FrameEntry *lhs = frame.peek(-2);
    1581                 : 
    1582                 :     /* Allocate all registers up-front. */
    1583           26181 :     FrameState::BinaryAlloc regs;
    1584           26181 :     frame.allocForBinary(lhs, rhs, op, regs, !target);
    1585                 : 
    1586           26181 :     MaybeJump lhsNotDouble, rhsNotNumber, lhsUnknownDone;
    1587           26181 :     if (!lhs->isTypeKnown())
    1588           25077 :         emitLeftDoublePath(lhs, rhs, regs, lhsNotDouble, rhsNotNumber, lhsUnknownDone);
    1589                 : 
    1590           26181 :     MaybeJump rhsNotNumber2;
    1591           26181 :     if (!rhs->isTypeKnown())
    1592            9155 :         emitRightDoublePath(lhs, rhs, regs, rhsNotNumber2);
    1593                 : 
    1594                 :     /* Both double paths will join here. */
    1595           26181 :     bool hasDoublePath = false;
    1596           26181 :     if (masm.supportsFloatingPoint() && (!rhs->isTypeKnown() || !lhs->isTypeKnown()))
    1597           26170 :         hasDoublePath = true;
    1598                 : 
    1599                 :     /* Integer path - figure out the immutable side. */
    1600           26181 :     JSOp cmpOp = op;
    1601           26181 :     int32_t value = 0;
    1602                 :     RegisterID cmpReg;
    1603           26181 :     MaybeRegisterID reg;
    1604           26181 :     if (regs.lhsData.isSet()) {
    1605           26045 :         cmpReg = regs.lhsData.reg();
    1606           26045 :         if (!regs.rhsData.isSet())
    1607           12013 :             value = rhs->getValue().toInt32();
    1608                 :         else
    1609           14032 :             reg = regs.rhsData.reg();
    1610                 :     } else {
    1611             136 :         cmpReg = regs.rhsData.reg();
    1612             136 :         value = lhs->getValue().toInt32();
    1613             136 :         cmpOp = ReverseCompareOp(op);
    1614                 :     }
    1615                 : 
    1616                 :     /*
    1617                 :      * Emit the actual comparisons. When a fusion is in play, it's faster to
    1618                 :      * combine the comparison with the jump, so these two cases are implemented
    1619                 :      * separately.
    1620                 :      */
    1621                 : 
    1622           26181 :     if (target) {
    1623                 :         /*
    1624                 :          * Emit the double path now, necessary to complete the OOL fast-path
    1625                 :          * before emitting the slow path.
    1626                 :          *
    1627                 :          * Note: doubles have not been swapped yet. Use original op.
    1628                 :          */
    1629           24811 :         MaybeJump doubleTest, doubleFall;
    1630           24811 :         Assembler::DoubleCondition dblCond = DoubleCondForOp(op, fused);
    1631           24811 :         if (hasDoublePath) {
    1632           24805 :             if (lhsUnknownDone.isSet())
    1633           23868 :                 lhsUnknownDone.get().linkTo(stubcc.masm.label(), &stubcc.masm);
    1634           24805 :             frame.sync(stubcc.masm, Uses(frame.frameSlots()));
    1635           24805 :             doubleTest = stubcc.masm.branchDouble(dblCond, regs.lhsFP, regs.rhsFP);
    1636           24805 :             doubleFall = stubcc.masm.jump();
    1637                 :         }
    1638                 : 
    1639                 :         /* Link all incoming slow paths to here. */
    1640           24811 :         if (lhsNotDouble.isSet()) {
    1641           23868 :             lhsNotDouble.get().linkTo(stubcc.masm.label(), &stubcc.masm);
    1642           23868 :             if (rhsNotNumber.isSet())
    1643            7477 :                 rhsNotNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm);
    1644                 :         }
    1645           24811 :         if (rhsNotNumber2.isSet())
    1646            8414 :             rhsNotNumber2.get().linkTo(stubcc.masm.label(), &stubcc.masm);
    1647                 : 
    1648                 :         /*
    1649                 :          * For fusions, spill the tracker state. xmm* remain intact. Note
    1650                 :          * that frame.sync() must be used directly, to avoid syncExit()'s
    1651                 :          * jumping logic.
    1652                 :          */
    1653           24811 :         frame.sync(stubcc.masm, Uses(frame.frameSlots()));
    1654           24811 :         stubcc.leave();
    1655           24811 :         OOL_STUBCALL(stub, REJOIN_BRANCH);
    1656                 : 
    1657                 :         /* Forget the world, preserving data. */
    1658           24811 :         frame.pinReg(cmpReg);
    1659           24811 :         if (reg.isSet())
    1660           13406 :             frame.pinReg(reg.reg());
    1661                 :         
    1662           24811 :         frame.popn(2);
    1663                 : 
    1664           24811 :         frame.syncAndKillEverything();
    1665           24811 :         frame.unpinKilledReg(cmpReg);
    1666           24811 :         if (reg.isSet())
    1667           13406 :             frame.unpinKilledReg(reg.reg());
    1668           24811 :         frame.freeReg(regs.lhsFP);
    1669           24811 :         frame.freeReg(regs.rhsFP);
    1670                 : 
    1671                 :         /* Operands could have been reordered, so use cmpOp. */
    1672           24811 :         Assembler::Condition i32Cond = GetCompareCondition(cmpOp, fused);
    1673                 : 
    1674                 :         /* Emit the i32 path. */
    1675           24811 :         Jump fast;
    1676           24811 :         if (reg.isSet())
    1677           13406 :             fast = masm.branch32(i32Cond, cmpReg, reg.reg());
    1678                 :         else
    1679           11405 :             fast = masm.branch32(i32Cond, cmpReg, Imm32(value));
    1680                 : 
    1681                 :         /*
    1682                 :          * The stub call has no need to rejoin since state is synced. Instead,
    1683                 :          * we can just test the return value.
    1684                 :          */
    1685                 :         Jump j = stubcc.masm.branchTest32(GetStubCompareCondition(fused),
    1686           24811 :                                           Registers::ReturnReg, Registers::ReturnReg);
    1687                 : 
    1688                 :         /* Rejoin from the slow path. */
    1689           24811 :         Jump j2 = stubcc.masm.jump();
    1690           24811 :         stubcc.crossJump(j2, masm.label());
    1691                 : 
    1692           24811 :         if (hasDoublePath) {
    1693           24805 :             j.linkTo(stubcc.masm.label(), &stubcc.masm);
    1694           24805 :             doubleTest.get().linkTo(stubcc.masm.label(), &stubcc.masm);
    1695           24805 :             j = stubcc.masm.jump();
    1696                 :         }
    1697                 : 
    1698                 :         /*
    1699                 :          * NB: jumpAndRun emits to the OOL path, so make sure not to use it
    1700                 :          * in the middle of an in-progress slow path.
    1701                 :          */
    1702           24811 :         if (!jumpAndRun(fast, target, &j))
    1703               0 :             return false;
    1704                 : 
    1705                 :         /* Rejoin from the double path. */
    1706           24811 :         if (hasDoublePath)
    1707           24805 :             stubcc.crossJump(doubleFall.get(), masm.label());
    1708                 :     } else {
    1709                 :         /*
    1710                 :          * Emit the double path now, necessary to complete the OOL fast-path
    1711                 :          * before emitting the slow path.
    1712                 :          */
    1713            1370 :         MaybeJump doubleDone;
    1714            1370 :         Assembler::DoubleCondition dblCond = DoubleCondForOp(op, JSOP_NOP);
    1715            1370 :         if (hasDoublePath) {
    1716            1365 :             if (lhsUnknownDone.isSet())
    1717            1209 :                 lhsUnknownDone.get().linkTo(stubcc.masm.label(), &stubcc.masm);
    1718                 :             /* :FIXME: Use SET if we can? */
    1719            1365 :             Jump test = stubcc.masm.branchDouble(dblCond, regs.lhsFP, regs.rhsFP);
    1720            1365 :             stubcc.masm.move(Imm32(0), regs.result);
    1721            1365 :             Jump skip = stubcc.masm.jump();
    1722            1365 :             test.linkTo(stubcc.masm.label(), &stubcc.masm);
    1723            1365 :             stubcc.masm.move(Imm32(1), regs.result);
    1724            1365 :             skip.linkTo(stubcc.masm.label(), &stubcc.masm);
    1725            1365 :             doubleDone = stubcc.masm.jump();
    1726                 :         }
    1727                 : 
    1728                 :         /* Link all incoming slow paths to here. */
    1729            1370 :         if (lhsNotDouble.isSet()) {
    1730            1209 :             lhsNotDouble.get().linkTo(stubcc.masm.label(), &stubcc.masm);
    1731            1209 :             if (rhsNotNumber.isSet())
    1732             585 :                 rhsNotNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm);
    1733                 :         }
    1734            1370 :         if (rhsNotNumber2.isSet())
    1735             741 :             rhsNotNumber2.get().linkTo(stubcc.masm.label(), &stubcc.masm);
    1736                 : 
    1737                 :         /* Emit the slow path - note full frame syncage. */
    1738            1370 :         frame.sync(stubcc.masm, Uses(2));
    1739            1370 :         stubcc.leave();
    1740            1370 :         OOL_STUBCALL(stub, REJOIN_FALLTHROUGH);
    1741                 : 
    1742                 :         /* Get an integer comparison condition. */
    1743            1370 :         Assembler::Condition i32Cond = GetCompareCondition(cmpOp, fused);
    1744                 : 
    1745                 :         /* Emit the compare & set. */
    1746            1370 :         if (reg.isSet())
    1747             626 :             masm.branchValue(i32Cond, cmpReg, reg.reg(), regs.result);
    1748                 :         else
    1749             744 :             masm.branchValue(i32Cond, cmpReg, value, regs.result);
    1750                 : 
    1751            1370 :         frame.popn(2);
    1752            1370 :         frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, regs.result);
    1753                 : 
    1754            1370 :         if (hasDoublePath)
    1755            1365 :             stubcc.crossJump(doubleDone.get(), masm.label());
    1756            1370 :         stubcc.rejoin(Changes(1));
    1757                 : 
    1758            1370 :         frame.freeReg(regs.lhsFP);
    1759            1370 :         frame.freeReg(regs.rhsFP);
    1760                 :     }
    1761                 : 
    1762           26181 :     return true;
    1763                 : }
    1764                 : 

Generated by: LCOV version 1.7