LCOV - app.info - js/src/methodjit/FastArithmetic.cpp

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