LCOV - code coverage report
Current view: directory - js/src/methodjit - LoopState.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1091 992 90.9 %
Date: 2012-06-02 Functions: 40 40 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                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      27                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include "methodjit/Compiler.h"
      40                 : #include "methodjit/LoopState.h"
      41                 : #include "methodjit/FrameState-inl.h"
      42                 : #include "methodjit/StubCalls.h"
      43                 : 
      44                 : #include "jstypedarrayinlines.h"
      45                 : 
      46                 : using namespace js;
      47                 : using namespace js::mjit;
      48                 : using namespace js::analyze;
      49                 : using namespace js::types;
      50                 : 
      51                 : inline bool
      52            3756 : SafeAdd(int32_t one, int32_t two, int32_t *res)
      53                 : {
      54            3756 :     *res = one + two;
      55            3756 :     int64_t ores = (int64_t)one + (int64_t)two;
      56            3756 :     if (ores == (int64_t)*res)
      57            3750 :         return true;
      58               6 :     JaegerSpew(JSpew_Analysis, "Overflow computing %d + %d\n", one, two);
      59               6 :     return false;
      60                 : }
      61                 : 
      62                 : inline bool
      63           17433 : SafeSub(int32_t one, int32_t two, int32_t *res)
      64                 : {
      65           17433 :     *res = one - two;
      66           17433 :     int64_t ores = (int64_t)one - (int64_t)two;
      67           17433 :     if (ores == (int64_t)*res)
      68           17433 :         return true;
      69               0 :     JaegerSpew(JSpew_Analysis, "Overflow computing %d - %d\n", one, two);
      70               0 :     return false;
      71                 : }
      72                 : 
      73                 : inline bool
      74              40 : SafeMul(int32_t one, int32_t two, int32_t *res)
      75                 : {
      76              40 :     *res = one * two;
      77              40 :     int64_t ores = (int64_t)one * (int64_t)two;
      78              40 :     if (ores == (int64_t)*res)
      79              32 :         return true;
      80               8 :     JaegerSpew(JSpew_Analysis, "Overflow computing %d * %d\n", one, two);
      81               8 :     return false;
      82                 : }
      83                 : 
      84           33602 : LoopState::LoopState(JSContext *cx, analyze::CrossScriptSSA *ssa,
      85                 :                      mjit::Compiler *cc, FrameState *frame)
      86                 :     : cx(cx), ssa(ssa),
      87           67204 :       outerScript(ssa->outerScript()), outerAnalysis(outerScript->analysis()),
      88                 :       cc(*cc), frame(*frame),
      89                 :       lifetime(NULL), alloc(NULL), reachedEntryPoint(false), loopRegs(0), skipAnalysis(false),
      90                 :       loopJoins(CompilerAllocPolicy(cx, *cc)),
      91                 :       loopPatches(CompilerAllocPolicy(cx, *cc)),
      92                 :       restoreInvariantCalls(CompilerAllocPolicy(cx, *cc)),
      93                 :       invariantEntries(CompilerAllocPolicy(cx, *cc)),
      94                 :       outer(NULL), temporariesStart(0),
      95                 :       testLHS(UNASSIGNED), testRHS(UNASSIGNED),
      96                 :       testConstant(0), testLessEqual(false),
      97                 :       increments(CompilerAllocPolicy(cx, *cc)), unknownModset(false),
      98                 :       growArrays(CompilerAllocPolicy(cx, *cc)),
      99                 :       modifiedProperties(CompilerAllocPolicy(cx, *cc)),
     100          100806 :       constrainedLoop(true)
     101                 : {
     102           33602 :     JS_ASSERT(cx->typeInferenceEnabled());
     103           33602 : }
     104                 : 
     105                 : bool
     106           33602 : LoopState::init(jsbytecode *head, Jump entry, jsbytecode *entryTarget)
     107                 : {
     108           33602 :     this->lifetime = outerAnalysis->getLoop(head);
     109               0 :     JS_ASSERT(lifetime &&
     110                 :               lifetime->head == uint32_t(head - outerScript->code) &&
     111           33602 :               lifetime->entry == uint32_t(entryTarget - outerScript->code));
     112                 : 
     113           33602 :     this->entry = entry;
     114                 : 
     115           33602 :     analyzeLoopTest();
     116           33602 :     analyzeLoopIncrements();
     117           70750 :     for (unsigned i = 0; i < ssa->numFrames(); i++) {
     118                 :         /* Only analyze this frame if it is nested within the loop itself. */
     119           37148 :         uint32_t index = ssa->iterFrame(i).index;
     120           37148 :         if (index != CrossScriptSSA::OUTER_FRAME) {
     121            3546 :             unsigned pframe = index;
     122            8226 :             while (ssa->getFrame(pframe).parent != CrossScriptSSA::OUTER_FRAME)
     123            1134 :                 pframe = ssa->getFrame(pframe).parent;
     124            3546 :             uint32_t offset = ssa->getFrame(pframe).parentpc - outerScript->code;
     125            3546 :             JS_ASSERT(offset < outerScript->length);
     126            3546 :             if (offset < lifetime->head || offset > lifetime->backedge)
     127            1946 :                 continue;
     128                 :         }
     129           35202 :         analyzeLoopBody(index);
     130                 :     }
     131                 : 
     132           33602 :     if (testLHS != UNASSIGNED) {
     133                 :         JaegerSpew(JSpew_Analysis, "loop test at %u: %s %s %s + %d\n", lifetime->head,
     134                 :                    frame.entryName(testLHS),
     135                 :                    testLessEqual ? "<=" : ">=",
     136            2428 :                    (testRHS == UNASSIGNED) ? "" : frame.entryName(testRHS),
     137           11153 :                    testConstant);
     138                 :     }
     139                 : 
     140           43491 :     for (unsigned i = 0; i < increments.length(); i++) {
     141                 :         JaegerSpew(JSpew_Analysis, "loop increment at %u for %s: %u\n", lifetime->head,
     142            9889 :                    frame.entryName(increments[i].slot),
     143           19778 :                    increments[i].offset);
     144                 :     }
     145                 : 
     146           34584 :     for (unsigned i = 0; i < growArrays.length(); i++) {
     147                 :         JaegerSpew(JSpew_Analysis, "loop grow array at %u: %s\n", lifetime->head,
     148             982 :                    types::TypeString(types::Type::ObjectType(growArrays[i])));
     149                 :     }
     150                 : 
     151           38193 :     for (unsigned i = 0; i < modifiedProperties.length(); i++) {
     152                 :         JaegerSpew(JSpew_Analysis, "loop modified property at %u: %s %s\n", lifetime->head,
     153            4591 :                    types::TypeString(types::Type::ObjectType(modifiedProperties[i].object)),
     154            9182 :                    TypeIdString(modifiedProperties[i].id));
     155                 :     }
     156                 : 
     157           33602 :     RegisterAllocation *&alloc = outerAnalysis->getAllocation(head);
     158           33602 :     JS_ASSERT(!alloc);
     159                 : 
     160           33602 :     alloc = cx->typeLifoAlloc().new_<RegisterAllocation>(true);
     161           33602 :     if (!alloc) {
     162               0 :         js_ReportOutOfMemory(cx);
     163               0 :         return false;
     164                 :     }
     165                 : 
     166           33602 :     this->alloc = alloc;
     167           33602 :     this->loopRegs = Registers::AvailAnyRegs;
     168                 : 
     169                 :     /*
     170                 :      * Don't hoist bounds checks or loop invariant code in scripts that have
     171                 :      * had indirect modification of their arguments.
     172                 :      */
     173           33602 :     if (outerScript->function()) {
     174           27088 :         if (TypeSet::HasObjectFlags(cx, outerScript->function()->getType(cx), OBJECT_FLAG_UNINLINEABLE))
     175            8975 :             this->skipAnalysis = true;
     176                 :     }
     177                 : 
     178                 :     /*
     179                 :      * Don't hoist bounds checks or loop invariant code in loops with safe
     180                 :      * points in the middle, which the interpreter can join at directly without
     181                 :      * performing hoisted bounds checks or doing initial computation of loop
     182                 :      * invariant terms.
     183                 :      */
     184           33602 :     if (lifetime->hasSafePoints)
     185             271 :         this->skipAnalysis = true;
     186                 : 
     187           33602 :     return true;
     188                 : }
     189                 : 
     190                 : void
     191          446582 : LoopState::addJoin(unsigned index, bool script)
     192                 : {
     193                 :     StubJoin r;
     194          446582 :     r.index = index;
     195          446582 :     r.script = script;
     196          446582 :     loopJoins.append(r);
     197          446582 : }
     198                 : 
     199                 : void
     200           31293 : LoopState::addInvariantCall(Jump jump, Label label, bool ool, bool entry, unsigned patchIndex, Uses uses)
     201                 : {
     202           31293 :     RestoreInvariantCall call;
     203           31293 :     call.jump = jump;
     204           31293 :     call.label = label;
     205           31293 :     call.ool = ool;
     206           31293 :     call.entry = entry;
     207           31293 :     call.patchIndex = patchIndex;
     208           31293 :     call.temporaryCopies = frame.getTemporaryCopies(uses);
     209                 : 
     210           31293 :     restoreInvariantCalls.append(call);
     211           31293 : }
     212                 : 
     213                 : void
     214           33590 : LoopState::flushLoop(StubCompiler &stubcc)
     215                 : {
     216           33590 :     clearLoopRegisters();
     217                 : 
     218                 :     /*
     219                 :      * Patch stub compiler rejoins with loads of loop carried registers
     220                 :      * discovered after the fact.
     221                 :      */
     222           67454 :     for (unsigned i = 0; i < loopPatches.length(); i++) {
     223           33864 :         const StubJoinPatch &p = loopPatches[i];
     224           33864 :         stubcc.patchJoin(p.join.index, p.join.script, p.address, p.reg);
     225                 :     }
     226           33590 :     loopJoins.clear();
     227           33590 :     loopPatches.clear();
     228                 : 
     229           33590 :     if (hasInvariants()) {
     230           10704 :         for (unsigned i = 0; i < restoreInvariantCalls.length(); i++) {
     231            8588 :             RestoreInvariantCall &call = restoreInvariantCalls[i];
     232            8588 :             Assembler &masm = cc.getAssembler(true);
     233           17176 :             Vector<Jump> failureJumps(cx);
     234                 : 
     235            8588 :             jsbytecode *pc = cc.getInvariantPC(call.patchIndex);
     236                 : 
     237            8588 :             if (call.ool) {
     238            8406 :                 call.jump.linkTo(masm.label(), &masm);
     239            8406 :                 restoreInvariants(pc, masm, call.temporaryCopies, &failureJumps);
     240            8406 :                 masm.jump().linkTo(call.label, &masm);
     241                 :             } else {
     242             182 :                 stubcc.linkExitDirect(call.jump, masm.label());
     243             182 :                 restoreInvariants(pc, masm, call.temporaryCopies, &failureJumps);
     244             182 :                 stubcc.crossJump(masm.jump(), call.label);
     245                 :             }
     246                 : 
     247            8588 :             if (!failureJumps.empty()) {
     248           45107 :                 for (unsigned i = 0; i < failureJumps.length(); i++)
     249           36876 :                     failureJumps[i].linkTo(masm.label(), &masm);
     250                 : 
     251                 :                 /*
     252                 :                  * Call InvariantFailure, setting up the return address to
     253                 :                  * patch and any value for the call to return.
     254                 :                  */
     255            8231 :                 InvariantCodePatch *patch = cc.getInvariantPatch(call.patchIndex);
     256            8231 :                 patch->hasPatch = true;
     257                 :                 patch->codePatch = masm.storePtrWithPatch(ImmPtr(NULL),
     258            8231 :                                                           FrameAddress(offsetof(VMFrame, scratch)));
     259                 :                 JS_STATIC_ASSERT(Registers::ReturnReg != Registers::ArgReg1);
     260            8231 :                 masm.move(Registers::ReturnReg, Registers::ArgReg1);
     261                 : 
     262            8231 :                 if (call.entry) {
     263                 :                     masm.fallibleVMCall(true, JS_FUNC_TO_DATA_PTR(void *, stubs::InvariantFailure),
     264            1126 :                                         pc, NULL, 0);
     265                 :                 } else {
     266                 :                     /* f.regs are already coherent, don't write new values to them. */
     267            7105 :                     masm.infallibleVMCall(JS_FUNC_TO_DATA_PTR(void *, stubs::InvariantFailure), -1);
     268                 :                 }
     269                 :             }
     270                 :         }
     271                 :     } else {
     272           54177 :         for (unsigned i = 0; i < restoreInvariantCalls.length(); i++) {
     273           22703 :             RestoreInvariantCall &call = restoreInvariantCalls[i];
     274           22703 :             Assembler &masm = cc.getAssembler(call.ool);
     275           22703 :             call.jump.linkTo(call.label, &masm);
     276                 :         }
     277                 :     }
     278           33590 :     restoreInvariantCalls.clear();
     279           33590 : }
     280                 : 
     281                 : void
     282          154271 : LoopState::clearLoopRegisters()
     283                 : {
     284          154271 :     alloc->clearLoops();
     285          154271 :     loopRegs = 0;
     286          154271 : }
     287                 : 
     288                 : bool
     289           15693 : LoopState::loopInvariantEntry(uint32_t slot)
     290                 : {
     291           15693 :     if (slot == UNASSIGNED)
     292            6297 :         return true;
     293                 : 
     294                 :     /* Watch for loop temporaries. :XXX: this is really gross. */
     295            9396 :     if (slot >= analyze::LocalSlot(outerScript, outerScript->nslots))
     296            1779 :         return true;
     297                 : 
     298            7617 :     if (slot == analyze::CalleeSlot() || outerAnalysis->slotEscapes(slot))
     299               0 :         return false;
     300            7617 :     return outerAnalysis->liveness(slot).firstWrite(lifetime) == UINT32_MAX;
     301                 : }
     302                 : 
     303                 : inline bool
     304            3946 : LoopState::entryRedundant(const InvariantEntry &e0, const InvariantEntry &e1)
     305                 : {
     306            3946 :     JS_ASSERT(e0.isCheck() && e1.isCheck());
     307                 : 
     308            3946 :     uint32_t array0 = e0.u.check.arraySlot;
     309            3946 :     uint32_t array1 = e1.u.check.arraySlot;
     310                 : 
     311            3946 :     uint32_t value01 = e0.u.check.valueSlot1;
     312            3946 :     uint32_t value02 = e0.u.check.valueSlot2;
     313                 : 
     314            3946 :     uint32_t value11 = e1.u.check.valueSlot1;
     315            3946 :     uint32_t value12 = e1.u.check.valueSlot2;
     316                 : 
     317            3946 :     int32_t c0 = e0.u.check.constant;
     318            3946 :     int32_t c1 = e1.u.check.constant;
     319                 : 
     320                 :     /*
     321                 :      * initialized lengths are always <= JSObject::NELEMENTS_LIMIT, check for
     322                 :      * integer overflow checks redundant given initialized length checks.
     323                 :      * If Y <= c0 and Y + c1 < initlen(array):
     324                 :      *
     325                 :      * Y <= c0
     326                 :      * initlen(array) - c1 <= c0
     327                 :      * NSLOTS_LIMIT <= c0 + c1
     328                 :      */
     329            3946 :     if (e0.kind == InvariantEntry::RANGE_CHECK && e1.isBoundsCheck() &&
     330                 :         value01 == value11 && value02 == value12) {
     331                 :         int32_t constant;
     332              12 :         if (c1 >= 0)
     333              12 :             constant = c0;
     334               0 :         else if (!SafeAdd(c0, c1, &constant))
     335               0 :             return false;
     336              12 :         return constant >= (int32_t) JSObject::NELEMENTS_LIMIT;
     337                 :     }
     338                 : 
     339                 :     /* Look for matching tests that differ only in their constants. */
     340            3934 :     if (e0.kind == e1.kind && array0 == array1 && value01 == value11 && value02 == value12) {
     341            1095 :         if (e0.isBoundsCheck()) {
     342                 :             /* If e0 is X >= Y + c0 and e1 is X >= Y + c1, e0 is redundant if c0 <= c1 */
     343             736 :             return (c0 <= c1);
     344                 :         } else {
     345                 :             /* If e0 is c0 >= Y and e1 is c1 >= Y, e0 is redundant if c0 >= c1 */
     346             359 :             return (c0 >= c1);
     347                 :         }
     348                 :     }
     349                 : 
     350            2839 :     return false;
     351                 : }
     352                 : 
     353                 : bool
     354            2005 : LoopState::checkRedundantEntry(const InvariantEntry &entry)
     355                 : {
     356                 :     /*
     357                 :      * Return true if entry is implied by an existing entry, otherwise filter
     358                 :      * out any existing entries which entry implies.
     359                 :      */
     360            2005 :     JS_ASSERT(entry.isCheck());
     361                 : 
     362                 :     /* Maintain this separately, GCC miscompiles if the loop test is invariantEntries.length(). */
     363            2005 :     unsigned length = invariantEntries.length();
     364                 : 
     365            4904 :     for (unsigned i = 0; i < length; i++) {
     366            3615 :         InvariantEntry &baseEntry = invariantEntries[i];
     367            3615 :         if (!baseEntry.isCheck())
     368            1284 :             continue;
     369            2331 :         if (entryRedundant(entry, baseEntry))
     370             716 :             return true;
     371            1615 :         if (entryRedundant(baseEntry, entry)) {
     372                 :             /*
     373                 :              * Make sure to maintain the existing ordering on how invariant
     374                 :              * entries are generated, this is required for e.g. entries which
     375                 :              * use temporaries or slot computations which appear before any
     376                 :              * bounds checks on the arrays.
     377                 :              */
     378             206 :             for (unsigned j = i; j < length - 1; j++)
     379               8 :                 invariantEntries[j] = invariantEntries[j + 1];
     380             198 :             invariantEntries.popBack();
     381             198 :             i--;
     382             198 :             length--;
     383                 :         }
     384                 :     }
     385                 : 
     386            1289 :     return false;
     387                 : }
     388                 : 
     389                 : bool
     390            1150 : LoopState::addHoistedCheck(InvariantArrayKind arrayKind, uint32_t arraySlot,
     391                 :                            uint32_t valueSlot1, uint32_t valueSlot2, int32_t constant)
     392                 : {
     393                 : #ifdef DEBUG
     394            1150 :     JS_ASSERT_IF(valueSlot1 == UNASSIGNED, valueSlot2 == UNASSIGNED);
     395            1150 :     const char *field = (arrayKind == DENSE_ARRAY) ? "initlen" : "length";
     396            1150 :     if (valueSlot1 == UNASSIGNED) {
     397             468 :         JaegerSpew(JSpew_Analysis, "Hoist %s > %d\n", field, constant);
     398             682 :     } else if (valueSlot2 == UNASSIGNED) {
     399                 :         JaegerSpew(JSpew_Analysis, "Hoisted as %s > %s + %d\n", field,
     400             665 :                    frame.entryName(valueSlot1), constant);
     401                 :     } else {
     402                 :         JaegerSpew(JSpew_Analysis, "Hoisted as %s > %s + %s + %d\n", field,
     403              17 :                    frame.entryName(valueSlot1), frame.entryName(valueSlot2), constant);
     404                 :     }
     405                 : #endif
     406                 : 
     407            1150 :     InvariantEntry entry;
     408                 :     entry.kind = (arrayKind == DENSE_ARRAY)
     409                 :                  ? InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK
     410            1150 :                  : InvariantEntry::TYPED_ARRAY_BOUNDS_CHECK;
     411            1150 :     entry.u.check.arraySlot = arraySlot;
     412            1150 :     entry.u.check.valueSlot1 = valueSlot1;
     413            1150 :     entry.u.check.valueSlot2 = valueSlot2;
     414            1150 :     entry.u.check.constant = constant;
     415                 : 
     416            1150 :     if (checkRedundantEntry(entry))
     417             354 :         return true;
     418                 : 
     419                 :     /*
     420                 :      * Maintain an invariant that for any array with a hoisted bounds check,
     421                 :      * we also have a loop invariant slot to hold the array's slots pointer.
     422                 :      * The compiler gets invariant array slots only for accesses with a hoisted
     423                 :      * bounds check, so this makes invariantSlots infallible.
     424                 :      */
     425             796 :     bool hasInvariantSlots = false;
     426                 :     InvariantEntry::EntryKind slotsKind = (arrayKind == DENSE_ARRAY)
     427                 :                                           ? InvariantEntry::DENSE_ARRAY_SLOTS
     428             796 :                                           : InvariantEntry::TYPED_ARRAY_SLOTS;
     429            2453 :     for (unsigned i = 0; !hasInvariantSlots && i < invariantEntries.length(); i++) {
     430            1657 :         InvariantEntry &entry = invariantEntries[i];
     431            1657 :         if (entry.kind == slotsKind && entry.u.array.arraySlot == arraySlot)
     432             191 :             hasInvariantSlots = true;
     433                 :     }
     434             796 :     if (!hasInvariantSlots) {
     435             605 :         uint32_t which = frame.allocTemporary();
     436             605 :         if (which == UINT32_MAX)
     437               0 :             return false;
     438             605 :         FrameEntry *fe = frame.getTemporary(which);
     439                 : 
     440                 :         JaegerSpew(JSpew_Analysis, "Using %s for loop invariant slots of %s\n",
     441             605 :                    frame.entryName(fe), frame.entryName(arraySlot));
     442                 : 
     443             605 :         InvariantEntry slotsEntry;
     444             605 :         slotsEntry.kind = slotsKind;
     445             605 :         slotsEntry.u.array.arraySlot = arraySlot;
     446             605 :         slotsEntry.u.array.temporary = which;
     447             605 :         invariantEntries.append(slotsEntry);
     448                 :     }
     449                 : 
     450             796 :     invariantEntries.append(entry);
     451             796 :     return true;
     452                 : }
     453                 : 
     454                 : void
     455             834 : LoopState::addNegativeCheck(uint32_t valueSlot, int32_t constant)
     456                 : {
     457                 :     JaegerSpew(JSpew_Analysis, "Nonnegative check %s + %d >= 0\n",
     458             834 :                frame.entryName(valueSlot), constant);
     459                 : 
     460             834 :     InvariantEntry entry;
     461             834 :     entry.kind = InvariantEntry::NEGATIVE_CHECK;
     462             834 :     entry.u.check.valueSlot1 = valueSlot;
     463             834 :     entry.u.check.constant = constant;
     464                 : 
     465             834 :     if (!checkRedundantEntry(entry))
     466             479 :         invariantEntries.append(entry);
     467             834 : }
     468                 : 
     469                 : void
     470              21 : LoopState::addRangeCheck(uint32_t valueSlot1, uint32_t valueSlot2, int32_t constant)
     471                 : {
     472                 :     JaegerSpew(JSpew_Analysis, "Range check %d >= %s + %s\n",
     473                 :                constant, frame.entryName(valueSlot1),
     474              21 :                valueSlot2 == UINT32_MAX ? "" : frame.entryName(valueSlot2));
     475                 : 
     476              21 :     InvariantEntry entry;
     477              21 :     entry.kind = InvariantEntry::RANGE_CHECK;
     478              21 :     entry.u.check.valueSlot1 = valueSlot1;
     479              21 :     entry.u.check.valueSlot2 = valueSlot2;
     480              21 :     entry.u.check.constant = constant;
     481                 : 
     482              21 :     if (!checkRedundantEntry(entry))
     483              14 :         invariantEntries.append(entry);
     484              21 : }
     485                 : 
     486                 : void
     487           16201 : LoopState::setLoopReg(AnyRegisterID reg, FrameEntry *fe)
     488                 : {
     489           16201 :     JS_ASSERT(alloc->loop(reg));
     490           16201 :     loopRegs.takeReg(reg);
     491                 : 
     492           16201 :     uint32_t slot = frame.outerSlot(fe);
     493                 :     JaegerSpew(JSpew_Regalloc, "allocating loop register %s for %s\n",
     494           16201 :                reg.name(), frame.entryName(fe));
     495                 : 
     496           16201 :     alloc->set(reg, slot, true);
     497                 : 
     498                 :     /*
     499                 :      * Mark pending rejoins to patch up with the load. We don't do this now as that would
     500                 :      * cause us to emit into the slow path, which may be in progress.
     501                 :      */
     502           50065 :     for (unsigned i = 0; i < loopJoins.length(); i++) {
     503           33864 :         StubJoinPatch p;
     504           33864 :         p.join = loopJoins[i];
     505           33864 :         p.address = frame.addressOf(fe);
     506           33864 :         p.reg = reg;
     507           33864 :         loopPatches.append(p);
     508                 :     }
     509                 : 
     510           16201 :     if (reachedEntryPoint) {
     511                 :         /*
     512                 :          * We've advanced past the entry point of the loop (we're analyzing the condition),
     513                 :          * so need to update the register state at that entry point so that the right
     514                 :          * things get loaded when we enter the loop.
     515                 :          */
     516             642 :         RegisterAllocation *alloc = outerAnalysis->getAllocation(lifetime->entry);
     517             642 :         JS_ASSERT(alloc && !alloc->assigned(reg));
     518             642 :         alloc->set(reg, slot, true);
     519                 :     }
     520           16201 : }
     521                 : 
     522                 : bool
     523           15148 : LoopState::hoistArrayLengthCheck(InvariantArrayKind arrayKind, const CrossSSAValue &obj,
     524                 :                                  const CrossSSAValue &index)
     525                 : {
     526                 :     /*
     527                 :      * Note: this method requires that the index is definitely an integer, and
     528                 :      * that obj is either a dense array, a typed array or not an object.
     529                 :      */
     530           15148 :     if (skipAnalysis)
     531           12751 :         return false;
     532                 : 
     533                 :     uint32_t objSlot;
     534                 :     int32_t objConstant;
     535            2397 :     if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0)
     536             462 :         return false;
     537                 : 
     538                 :     JaegerSpew(JSpew_Analysis, "Trying to hoist bounds check on %s\n",
     539            1935 :                frame.entryName(objSlot));
     540                 : 
     541            1935 :     if (!loopInvariantEntry(objSlot)) {
     542              40 :         JaegerSpew(JSpew_Analysis, "Object is not loop invariant\n");
     543              40 :         return false;
     544                 :     }
     545                 : 
     546                 :     /*
     547                 :      * Check for an overlap with the arrays we think might grow in this loop.
     548                 :      * This information is only a guess; if we don't think the array can grow
     549                 :      * but it actually can, we will probably recompile after the hoisted
     550                 :      * bounds check fails.
     551                 :      */
     552            1895 :     TypeSet *objTypes = ssa->getValueTypes(obj);
     553            1895 :     if (arrayKind == DENSE_ARRAY && !growArrays.empty()) {
     554             576 :         unsigned count = objTypes->getObjectCount();
     555            1299 :         for (unsigned i = 0; i < count; i++) {
     556             948 :             if (objTypes->getSingleObject(i) != NULL) {
     557               0 :                 JaegerSpew(JSpew_Analysis, "Object might be a singleton");
     558               0 :                 return false;
     559                 :             }
     560             948 :             TypeObject *object = objTypes->getTypeObject(i);
     561             948 :             if (object && hasGrowArray(object)) {
     562             225 :                 JaegerSpew(JSpew_Analysis, "Object might grow inside loop\n");
     563             225 :                 return false;
     564                 :             }
     565                 :         }
     566                 :     }
     567                 : 
     568                 :     /*
     569                 :      * Get an expression for the index 'index + indexConstant', where index
     570                 :      * is the value of a slot at loop entry.
     571                 :      */
     572                 :     uint32_t indexSlot;
     573                 :     int32_t indexConstant;
     574            1670 :     if (!getEntryValue(index, &indexSlot, &indexConstant)) {
     575             249 :         JaegerSpew(JSpew_Analysis, "Could not compute index in terms of loop entry state\n");
     576             249 :         return false;
     577                 :     }
     578                 : 
     579            1421 :     if (indexSlot == UNASSIGNED) {
     580                 :         /* Hoist checks on x[n] accesses for constant n. */
     581             318 :         if (indexConstant < 0) {
     582               0 :             JaegerSpew(JSpew_Analysis, "Constant index is negative\n");
     583               0 :             return false;
     584                 :         }
     585             318 :         return addHoistedCheck(arrayKind, objSlot, UNASSIGNED, UNASSIGNED, indexConstant);
     586                 :     }
     587                 : 
     588            1103 :     if (loopInvariantEntry(indexSlot)) {
     589                 :         /* Hoist checks on x[y] accesses when y is loop invariant. */
     590             416 :         addNegativeCheck(indexSlot, indexConstant);
     591             416 :         return addHoistedCheck(arrayKind, objSlot, indexSlot, UNASSIGNED, indexConstant);
     592                 :     }
     593                 : 
     594                 :     /*
     595                 :      * If the LHS can decrease in the loop, it could become negative and
     596                 :      * underflow the array. We currently only hoist bounds checks for loops
     597                 :      * which walk arrays going forward.
     598                 :      */
     599             687 :     if (!outerAnalysis->liveness(indexSlot).nonDecreasing(outerScript, lifetime)) {
     600              55 :         JaegerSpew(JSpew_Analysis, "Index may decrease in future iterations\n");
     601              55 :         return false;
     602                 :     }
     603                 : 
     604                 :     /*
     605                 :      * If the access is of the form x[y + a] where we know that y <= z + b
     606                 :      * (both in terms of state at the head of the loop), hoist as follows:
     607                 :      *
     608                 :      * y + a < initlen(x)
     609                 :      * y < initlen(x) - a
     610                 :      * z + b < initlen(x) - a
     611                 :      * z + b + a < initlen(x)
     612                 :      */
     613             632 :     if (indexSlot == testLHS && testLessEqual) {
     614                 :         int32_t constant;
     615             399 :         if (!SafeAdd(testConstant, indexConstant, &constant))
     616               0 :             return false;
     617                 : 
     618                 :         /*
     619                 :          * Check that the LHS is nonnegative every time we rejoin the loop.
     620                 :          * This is only really necessary on initial loop entry. Note that this
     621                 :          * test is not sensitive to changes to the LHS between when we make
     622                 :          * the test and the start of the next iteration, as we've ensured the
     623                 :          * LHS is nondecreasing within the body of the loop.
     624                 :          */
     625             399 :         addNegativeCheck(indexSlot, indexConstant);
     626                 : 
     627             399 :         return addHoistedCheck(arrayKind, objSlot, testRHS, UNASSIGNED, constant);
     628                 :     }
     629                 : 
     630                 :     /*
     631                 :      * If the access is of the form x[y + a] where we know that z >= b at the
     632                 :      * head of the loop and y has a linear relationship with z such that
     633                 :      * (y + z) always has the same value at the head of the loop, hoist as
     634                 :      * follows:
     635                 :      *
     636                 :      * y + a < initlen(x)
     637                 :      * y + z < initlen(x) + z - a
     638                 :      * y + z < initlen(x) + b - a
     639                 :      * y + z + a - b < initlen(x)
     640                 :      */
     641             233 :     if (hasTestLinearRelationship(indexSlot)) {
     642                 :         int32_t constant;
     643              17 :         if (!SafeSub(indexConstant, testConstant, &constant))
     644               0 :             return false;
     645                 : 
     646              17 :         addNegativeCheck(indexSlot, indexConstant);
     647              17 :         return addHoistedCheck(arrayKind, objSlot, indexSlot, testLHS, constant);
     648                 :     }
     649                 : 
     650             216 :     JaegerSpew(JSpew_Analysis, "No match found\n");
     651             216 :     return false;
     652                 : }
     653                 : 
     654                 : bool
     655              56 : LoopState::hoistArgsLengthCheck(const CrossSSAValue &index)
     656                 : {
     657              56 :     if (skipAnalysis)
     658              21 :         return false;
     659                 : 
     660              35 :     JaegerSpew(JSpew_Analysis, "Trying to hoist argument range check\n");
     661                 : 
     662                 :     uint32_t indexSlot;
     663                 :     int32_t indexConstant;
     664              35 :     if (!getEntryValue(index, &indexSlot, &indexConstant)) {
     665               1 :         JaegerSpew(JSpew_Analysis, "Could not compute index in terms of loop entry state\n");
     666               1 :         return false;
     667                 :     }
     668                 : 
     669                 :     /*
     670                 :      * We only hoist arguments checks which can be completely eliminated, for
     671                 :      * now just tests with 'i < arguments.length' or similar in the condition.
     672                 :      */
     673                 : 
     674              34 :     if (indexSlot == UNASSIGNED || loopInvariantEntry(indexSlot)) {
     675               2 :         JaegerSpew(JSpew_Analysis, "Index is constant or loop invariant\n");
     676               2 :         return false;
     677                 :     }
     678                 : 
     679              32 :     if (!outerAnalysis->liveness(indexSlot).nonDecreasing(outerScript, lifetime)) {
     680               3 :         JaegerSpew(JSpew_Analysis, "Index may decrease in future iterations\n");
     681               3 :         return false;
     682                 :     }
     683                 : 
     684              29 :     if (indexSlot == testLHS && indexConstant == 0 && testConstant == -1 && testLessEqual) {
     685               6 :         bool found = false;
     686               6 :         for (unsigned i = 0; i < invariantEntries.length(); i++) {
     687               2 :             const InvariantEntry &entry = invariantEntries[i];
     688               2 :             if (entry.kind == InvariantEntry::INVARIANT_ARGS_LENGTH) {
     689               2 :                 uint32_t slot = frame.outerSlot(frame.getTemporary(entry.u.array.temporary));
     690               2 :                 if (slot == testRHS)
     691               2 :                     found = true;
     692               2 :                 break;
     693                 :             }
     694                 :         }
     695               6 :         if (found) {
     696               2 :             addNegativeCheck(indexSlot, indexConstant);
     697               2 :             JaegerSpew(JSpew_Analysis, "Access implied by loop test\n");
     698               2 :             return true;
     699                 :         }
     700                 :     }
     701                 : 
     702              27 :     JaegerSpew(JSpew_Analysis, "No match found\n");
     703              27 :     return false;
     704                 : }
     705                 : 
     706                 : bool
     707             810 : LoopState::hasTestLinearRelationship(uint32_t slot)
     708                 : {
     709                 :     /*
     710                 :      * Determine whether slot has a linear relationship with the loop test
     711                 :      * variable 'test', such that (slot + test) always has the same value at
     712                 :      * the head of the loop.
     713                 :      */
     714                 : 
     715             810 :     if (testLHS == UNASSIGNED || testRHS != UNASSIGNED || testLessEqual)
     716             775 :         return false;
     717                 : 
     718              35 :     uint32_t incrementOffset = getIncrement(slot);
     719              35 :     if (incrementOffset == UINT32_MAX) {
     720                 :         /*
     721                 :          * Variable is not always incremented in the loop, or is incremented
     722                 :          * multiple times. Note that the nonDecreasing test done earlier
     723                 :          * ensures that if there is a single write, it is an increment.
     724                 :          */
     725               5 :         return false;
     726                 :     }
     727                 : 
     728              30 :     uint32_t decrementOffset = getIncrement(testLHS);
     729              30 :     if (decrementOffset == UINT32_MAX)
     730               0 :         return false;
     731                 : 
     732              30 :     JSOp op = JSOp(outerScript->code[decrementOffset]);
     733              30 :     switch (op) {
     734                 :       case JSOP_DECLOCAL:
     735                 :       case JSOP_LOCALDEC:
     736                 :       case JSOP_DECARG:
     737                 :       case JSOP_ARGDEC:
     738              30 :         return true;
     739                 :       default:
     740               0 :         return false;
     741                 :     }
     742                 : }
     743                 : 
     744                 : FrameEntry *
     745            1150 : LoopState::invariantArraySlots(const CrossSSAValue &obj)
     746                 : {
     747            1150 :     JS_ASSERT(!skipAnalysis);
     748                 : 
     749                 :     uint32_t objSlot;
     750                 :     int32_t objConstant;
     751            1150 :     if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0) {
     752               0 :         JS_NOT_REACHED("Bad value");
     753                 :         return NULL;
     754                 :     }
     755                 : 
     756                 :     /*
     757                 :      * Note: we don't have to check arrayKind (dense array or typed array) here,
     758                 :      * because an array cannot have entries for both dense array slots and typed
     759                 :      * array slots.
     760                 :      */
     761            3074 :     for (unsigned i = 0; i < invariantEntries.length(); i++) {
     762            3074 :         InvariantEntry &entry = invariantEntries[i];
     763            3074 :         if ((entry.kind == InvariantEntry::DENSE_ARRAY_SLOTS ||
     764                 :              entry.kind == InvariantEntry::TYPED_ARRAY_SLOTS) &&
     765                 :             entry.u.array.arraySlot == objSlot) {
     766            1150 :             return frame.getTemporary(entry.u.array.temporary);
     767                 :         }
     768                 :     }
     769                 : 
     770                 :     /* addHoistedCheck should have ensured there is an entry for the slots. */
     771               0 :     JS_NOT_REACHED("Missing invariant slots");
     772                 :     return NULL;
     773                 : }
     774                 : 
     775                 : FrameEntry *
     776              57 : LoopState::invariantArguments()
     777                 : {
     778              57 :     if (skipAnalysis)
     779              22 :         return NULL;
     780                 : 
     781              39 :     for (unsigned i = 0; i < invariantEntries.length(); i++) {
     782               4 :         InvariantEntry &entry = invariantEntries[i];
     783               4 :         if (entry.kind == InvariantEntry::INVARIANT_ARGS_BASE)
     784               0 :             return frame.getTemporary(entry.u.array.temporary);
     785                 :     }
     786                 : 
     787              35 :     uint32_t which = frame.allocTemporary();
     788              35 :     if (which == UINT32_MAX)
     789               0 :         return NULL;
     790              35 :     FrameEntry *fe = frame.getTemporary(which);
     791                 : 
     792              35 :     InvariantEntry entry;
     793              35 :     entry.kind = InvariantEntry::INVARIANT_ARGS_BASE;
     794              35 :     entry.u.array.temporary = which;
     795              35 :     invariantEntries.append(entry);
     796                 : 
     797                 :     JaegerSpew(JSpew_Analysis, "Using %s for loop invariant args base\n",
     798              35 :                frame.entryName(fe));
     799              35 :     return fe;
     800                 : }
     801                 : 
     802                 : FrameEntry *
     803            2169 : LoopState::invariantLength(const CrossSSAValue &obj)
     804                 : {
     805            2169 :     if (skipAnalysis)
     806               0 :         return NULL;
     807                 : 
     808                 :     uint32_t objSlot;
     809                 :     int32_t objConstant;
     810            2169 :     if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0)
     811             175 :         return NULL;
     812            1994 :     TypeSet *objTypes = ssa->getValueTypes(obj);
     813                 : 
     814                 :     /* Check for 'length' on the lazy arguments for the current frame. */
     815            1994 :     if (objTypes->isLazyArguments(cx)) {
     816               2 :         JS_ASSERT(obj.frame == CrossScriptSSA::OUTER_FRAME);
     817                 : 
     818               2 :         for (unsigned i = 0; i < invariantEntries.length(); i++) {
     819               0 :             InvariantEntry &entry = invariantEntries[i];
     820               0 :             if (entry.kind == InvariantEntry::INVARIANT_ARGS_LENGTH)
     821               0 :                 return frame.getTemporary(entry.u.array.temporary);
     822                 :         }
     823                 : 
     824               2 :         uint32_t which = frame.allocTemporary();
     825               2 :         if (which == UINT32_MAX)
     826               0 :             return NULL;
     827               2 :         FrameEntry *fe = frame.getTemporary(which);
     828                 : 
     829               2 :         InvariantEntry entry;
     830               2 :         entry.kind = InvariantEntry::INVARIANT_ARGS_LENGTH;
     831               2 :         entry.u.array.temporary = which;
     832               2 :         invariantEntries.append(entry);
     833                 : 
     834                 :         JaegerSpew(JSpew_Analysis, "Using %s for loop invariant args length\n",
     835               2 :                    frame.entryName(fe));
     836               2 :         return fe;
     837                 :     }
     838                 : 
     839                 :     /*
     840                 :      * Note: we don't have to check arrayKind (dense array or typed array) here,
     841                 :      * because an array cannot have entries for both dense array length and typed
     842                 :      * array length.
     843                 :      */
     844            2089 :     for (unsigned i = 0; i < invariantEntries.length(); i++) {
     845             256 :         InvariantEntry &entry = invariantEntries[i];
     846             256 :         if ((entry.kind == InvariantEntry::DENSE_ARRAY_LENGTH ||
     847                 :              entry.kind == InvariantEntry::TYPED_ARRAY_LENGTH) &&
     848                 :             entry.u.array.arraySlot == objSlot) {
     849             159 :             return frame.getTemporary(entry.u.array.temporary);
     850                 :         }
     851                 :     }
     852                 : 
     853            1833 :     if (!loopInvariantEntry(objSlot))
     854               0 :         return NULL;
     855                 : 
     856                 :     /* Hoist 'length' access on typed arrays. */
     857            1833 :     if (!objTypes->hasObjectFlags(cx, OBJECT_FLAG_NON_TYPED_ARRAY)) {
     858                 :         /* Recompile if object type changes. */
     859              68 :         objTypes->addFreeze(cx);
     860                 : 
     861              68 :         uint32_t which = frame.allocTemporary();
     862              68 :         if (which == UINT32_MAX)
     863               0 :             return NULL;
     864              68 :         FrameEntry *fe = frame.getTemporary(which);
     865                 : 
     866                 :         JaegerSpew(JSpew_Analysis, "Using %s for loop invariant typed array length of %s\n",
     867              68 :                    frame.entryName(fe), frame.entryName(objSlot));
     868                 : 
     869              68 :         InvariantEntry entry;
     870              68 :         entry.kind = InvariantEntry::TYPED_ARRAY_LENGTH;
     871              68 :         entry.u.array.arraySlot = objSlot;
     872              68 :         entry.u.array.temporary = which;
     873              68 :         invariantEntries.append(entry);
     874                 : 
     875              68 :         return fe;
     876                 :     }
     877                 : 
     878            1765 :     if (objTypes->hasObjectFlags(cx, OBJECT_FLAG_NON_DENSE_ARRAY))
     879             145 :         return NULL;
     880                 : 
     881                 :     /*
     882                 :      * Don't make 'length' loop invariant if the loop might directly write
     883                 :      * to the elements of any of the accessed arrays. This could invoke an
     884                 :      * inline path which updates the length. There is no need to check the
     885                 :      * modset for direct 'length' writes, as we don't generate inline paths
     886                 :      * updating array lengths.
     887                 :      */
     888            4739 :     for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
     889            3119 :         if (objTypes->getSingleObject(i) != NULL)
     890               0 :             return NULL;
     891            3119 :         TypeObject *object = objTypes->getTypeObject(i);
     892            3119 :         if (object && hasModifiedProperty(object, JSID_VOID))
     893               0 :             return NULL;
     894                 :     }
     895            1620 :     objTypes->addFreeze(cx);
     896                 : 
     897            1620 :     uint32_t which = frame.allocTemporary();
     898            1620 :     if (which == UINT32_MAX)
     899               0 :         return NULL;
     900            1620 :     FrameEntry *fe = frame.getTemporary(which);
     901                 : 
     902                 :     JaegerSpew(JSpew_Analysis, "Using %s for loop invariant dense array length of %s\n",
     903            1620 :                frame.entryName(fe), frame.entryName(objSlot));
     904                 : 
     905            1620 :     InvariantEntry entry;
     906            1620 :     entry.kind = InvariantEntry::DENSE_ARRAY_LENGTH;
     907            1620 :     entry.u.array.arraySlot = objSlot;
     908            1620 :     entry.u.array.temporary = which;
     909            1620 :     invariantEntries.append(entry);
     910                 : 
     911            1620 :     return fe;
     912                 : }
     913                 : 
     914                 : FrameEntry *
     915            1286 : LoopState::invariantProperty(const CrossSSAValue &obj, jsid id)
     916                 : {
     917            1286 :     if (skipAnalysis)
     918               0 :         return NULL;
     919                 : 
     920            1286 :     if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))
     921             209 :         return NULL;
     922                 : 
     923                 :     uint32_t objSlot;
     924                 :     int32_t objConstant;
     925            1077 :     if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0)
     926             457 :         return NULL;
     927                 : 
     928             694 :     for (unsigned i = 0; i < invariantEntries.length(); i++) {
     929             251 :         InvariantEntry &entry = invariantEntries[i];
     930             431 :         if (entry.kind == InvariantEntry::INVARIANT_PROPERTY &&
     931                 :             entry.u.property.objectSlot == objSlot &&
     932             180 :             entry.u.property.id == id) {
     933             177 :             return frame.getTemporary(entry.u.property.temporary);
     934                 :         }
     935                 :     }
     936                 : 
     937             443 :     if (!loopInvariantEntry(objSlot))
     938              51 :         return NULL;
     939                 : 
     940                 :     /* Check that the property is definite and not written anywhere in the loop. */
     941             392 :     TypeSet *objTypes = ssa->getValueTypes(obj);
     942             392 :     if (objTypes->unknownObject() || objTypes->getObjectCount() != 1)
     943              23 :         return NULL;
     944             369 :     TypeObject *object = objTypes->getTypeObject(0);
     945             369 :     if (!object || object->unknownProperties() || hasModifiedProperty(object, id) || id != MakeTypeId(cx, id))
     946             172 :         return NULL;
     947             197 :     TypeSet *propertyTypes = object->getProperty(cx, id, false);
     948             197 :     if (!propertyTypes)
     949               0 :         return NULL;
     950             197 :     if (!propertyTypes->isDefiniteProperty() || propertyTypes->isOwnProperty(cx, object, true))
     951              98 :         return NULL;
     952              99 :     objTypes->addFreeze(cx);
     953                 : 
     954              99 :     uint32_t which = frame.allocTemporary();
     955              99 :     if (which == UINT32_MAX)
     956               0 :         return NULL;
     957              99 :     FrameEntry *fe = frame.getTemporary(which);
     958                 : 
     959                 :     JaegerSpew(JSpew_Analysis, "Using %s for loop invariant property of %s\n",
     960              99 :                frame.entryName(fe), frame.entryName(objSlot));
     961                 : 
     962              99 :     InvariantEntry entry;
     963              99 :     entry.kind = InvariantEntry::INVARIANT_PROPERTY;
     964              99 :     entry.u.property.objectSlot = objSlot;
     965              99 :     entry.u.property.propertySlot = propertyTypes->definiteSlot();
     966              99 :     entry.u.property.temporary = which;
     967              99 :     entry.u.property.id = id;
     968              99 :     invariantEntries.append(entry);
     969                 : 
     970              99 :     return fe;
     971                 : }
     972                 : 
     973                 : bool
     974           43307 : LoopState::cannotIntegerOverflow(const CrossSSAValue &pushed)
     975                 : {
     976           43307 :     if (skipAnalysis)
     977           38388 :         return false;
     978                 : 
     979                 :     int32_t min, max;
     980            4919 :     if (computeInterval(pushed, &min, &max)) {
     981             116 :         JaegerSpew(JSpew_Analysis, "Integer operation fits in range [%d, %d]\n", min, max);
     982             116 :         return true;
     983                 :     }
     984                 : 
     985                 :     /*
     986                 :      * Compute a slot and constant such that the result of the binary op is
     987                 :      * 'slot + constant', where slot is expressed in terms of its value at
     988                 :      * the head of the loop.
     989                 :      */
     990            4803 :     JS_ASSERT(pushed.v.kind() == SSAValue::PUSHED);
     991            4803 :     jsbytecode *PC = ssa->getFrame(pushed.frame).script->code + pushed.v.pushedOffset();
     992            4803 :     ScriptAnalysis *analysis = ssa->getFrame(pushed.frame).script->analysis();
     993                 : 
     994            4803 :     if (!analysis->integerOperation(cx, PC))
     995            1490 :         return false;
     996                 : 
     997            3313 :     uint32_t baseSlot = UNASSIGNED;
     998            3313 :     int32_t baseConstant = 0;
     999            3313 :     JSOp op = JSOp(*PC);
    1000            3313 :     switch (op) {
    1001                 : 
    1002                 :       case JSOP_INCLOCAL:
    1003                 :       case JSOP_LOCALINC:
    1004                 :       case JSOP_INCARG:
    1005                 :       case JSOP_ARGINC: {
    1006            1517 :         CrossSSAValue cv(pushed.frame, analysis->poppedValue(PC, 0));
    1007            1517 :         if (!getEntryValue(cv, &baseSlot, &baseConstant))
    1008              11 :             return false;
    1009            1506 :         if (!SafeAdd(baseConstant, 1, &baseConstant))
    1010               0 :             return false;
    1011            1506 :         break;
    1012                 :       }
    1013                 : 
    1014                 :       case JSOP_DECLOCAL:
    1015                 :       case JSOP_LOCALDEC:
    1016                 :       case JSOP_DECARG:
    1017                 :       case JSOP_ARGDEC: {
    1018             165 :         CrossSSAValue cv(pushed.frame, analysis->poppedValue(PC, 0));
    1019             165 :         if (!getEntryValue(cv, &baseSlot, &baseConstant))
    1020               0 :             return false;
    1021             165 :         if (!SafeSub(baseConstant, 1, &baseConstant))
    1022               0 :             return false;
    1023             165 :         break;
    1024                 :       }
    1025                 : 
    1026                 :       case JSOP_ADD:
    1027                 :       case JSOP_SUB: {
    1028            1372 :         uint32_t lhs = UNASSIGNED, rhs = UNASSIGNED;
    1029            1372 :         int32_t lhsconstant = 0, rhsconstant = 0;
    1030            1372 :         CrossSSAValue lcv(pushed.frame, analysis->poppedValue(PC, 1));
    1031            1372 :         CrossSSAValue rcv(pushed.frame, analysis->poppedValue(PC, 0));
    1032            1372 :         if (!getEntryValue(lcv, &lhs, &lhsconstant))
    1033             883 :             return false;
    1034             489 :         if (!getEntryValue(rcv, &rhs, &rhsconstant))
    1035              73 :             return false;
    1036             416 :         if (op == JSOP_ADD) {
    1037             255 :             if (!SafeAdd(lhsconstant, rhsconstant, &baseConstant))
    1038               0 :                 return false;
    1039             255 :             if (lhs != UNASSIGNED && rhs != UNASSIGNED)
    1040              90 :                 return false;
    1041             165 :             baseSlot = (lhs == UNASSIGNED) ? rhs : lhs;
    1042                 :         } else {
    1043             161 :             if (!SafeSub(lhsconstant, rhsconstant, &baseConstant))
    1044               0 :                 return false;
    1045             161 :             if (rhs != UNASSIGNED)
    1046              57 :                 return false;
    1047             104 :             baseSlot = lhs;
    1048                 :         }
    1049             269 :         break;
    1050                 :       }
    1051                 : 
    1052                 :       default:
    1053             259 :         return false;
    1054                 :     }
    1055                 : 
    1056            1940 :     if (baseSlot == UNASSIGNED)
    1057               0 :         return false;
    1058                 : 
    1059                 :     JaegerSpew(JSpew_Analysis, "Trying to hoist integer overflow check on %s + %d\n",
    1060            1940 :                frame.entryName(baseSlot), baseConstant);
    1061                 : 
    1062            1940 :     if (baseConstant == 0) {
    1063               6 :         JaegerSpew(JSpew_Analysis, "Vacuously holds\n");
    1064               6 :         return true;
    1065                 :     }
    1066                 : 
    1067            1934 :     if (baseConstant < 0) {
    1068                 :         /*
    1069                 :          * If the access is of the form 'y + a' where a is negative and we know
    1070                 :          * that y >= b at the head of the loop, we can eliminate as follows:
    1071                 :          *
    1072                 :          * y + a >= INT_MIN
    1073                 :          * b + a >= INT_MIN
    1074                 :          */
    1075             269 :         if (baseSlot == testLHS && !testLessEqual && testRHS == UNASSIGNED) {
    1076                 :             int32_t constant;
    1077             125 :             if (!SafeAdd(testConstant, baseConstant, &constant))
    1078               0 :                 return false;
    1079                 : 
    1080             125 :             JaegerSpew(JSpew_Analysis, "Loop test comparison must hold\n");
    1081             125 :             return true;
    1082                 :         }
    1083                 : 
    1084             144 :         JaegerSpew(JSpew_Analysis, "No match found\n");
    1085             144 :         return false;
    1086                 :     }
    1087                 : 
    1088                 :     /*
    1089                 :      * If the access is of the form 'y + a' where we know that y <= z + b
    1090                 :      * (both in terms of state at the head of the loop), hoist as follows:
    1091                 :      *
    1092                 :      * y + a <= INT_MAX
    1093                 :      * y <= INT_MAX - a
    1094                 :      * z + b <= INT_MAX - a
    1095                 :      * z <= INT_MAX - (a + b)
    1096                 :      */
    1097            1665 :     if (baseSlot == testLHS && testLessEqual) {
    1098                 :         int32_t constant;
    1099            1088 :         if (!SafeAdd(testConstant, baseConstant, &constant))
    1100               2 :             return false;
    1101                 : 
    1102            1086 :         if (testRHS == UNASSIGNED || constant <= 0) {
    1103                 :             /*
    1104                 :              * Reduces to '(a + b) <= INT_MAX', which SafeAdd ensures,
    1105                 :              * or 'z <= INT_MAX', which integer checks on z ensure.
    1106                 :              */
    1107            1078 :             JaegerSpew(JSpew_Analysis, "Loop test comparison must hold\n");
    1108            1078 :             return true;
    1109                 :         }
    1110                 : 
    1111               8 :         constant = JSVAL_INT_MAX - constant;
    1112                 : 
    1113               8 :         addRangeCheck(testRHS, UNASSIGNED, constant);
    1114               8 :         return true;
    1115                 :     }
    1116                 : 
    1117                 :     /*
    1118                 :      * If the access is of the form 'y + a' where we know that z >= b at the
    1119                 :      * head of the loop and y has a linear relationship with z such that
    1120                 :      * (y + z) always has the same value at the head of the loop, hoist as
    1121                 :      * follows:
    1122                 :      *
    1123                 :      * y + a <= INT_MAX
    1124                 :      * y + z <= INT_MAX + z - a
    1125                 :      * y + z <= INT_MAX + b - a
    1126                 :      */
    1127             577 :     if (hasTestLinearRelationship(baseSlot)) {
    1128                 :         int32_t constant;
    1129              13 :         if (!SafeSub(testConstant, baseConstant, &constant))
    1130               0 :             return false;
    1131                 : 
    1132              13 :         if (constant >= 0)
    1133               0 :             constant = 0;
    1134              13 :         constant = JSVAL_INT_MAX + constant;
    1135                 : 
    1136              13 :         addRangeCheck(baseSlot, testLHS, constant);
    1137              13 :         return true;
    1138                 :     }
    1139                 : 
    1140             564 :     JaegerSpew(JSpew_Analysis, "No match found\n");
    1141             564 :     return false;
    1142                 : }
    1143                 : 
    1144                 : bool
    1145           43307 : LoopState::ignoreIntegerOverflow(const CrossSSAValue &pushed)
    1146                 : {
    1147           43307 :     if (skipAnalysis || unknownModset || !constrainedLoop)
    1148           42856 :         return false;
    1149                 : 
    1150                 :     /*
    1151                 :      * Under certain circumstances, we can ignore arithmetic overflow in adds
    1152                 :      * and multiplies. As long as the result of the add/mul is either only used
    1153                 :      * in bitwise arithmetic or is only used in additions whose result is only
    1154                 :      * used in bitwise arithmetic, then the conversion to integer performed by
    1155                 :      * the bitop will undo the effect of the earlier overflow. There are two
    1156                 :      * additional things to watch for before performing this transformation:
    1157                 :      *
    1158                 :      * 1. If the overflowing double is sufficiently large that it loses
    1159                 :      * precision in its lower bits (with a 48 bit mantissa, this may happen for
    1160                 :      * values of N >= 2^48), the resulting rounding could change the result.
    1161                 :      * We don't ignore overflow on multiplications without range information,
    1162                 :      * though assume that no amount of integer additions we perform in a single
    1163                 :      * loop iteration will overflow 2^48.
    1164                 :      *
    1165                 :      * 2. If used in an addition with a string, the overflowing and truncated
    1166                 :      * results may produce different values (e.g. '(x + "e3") & y'). We must
    1167                 :      * restrict the loop body in such a way that no string operand is possible
    1168                 :      * or becomes possible due to dynamic type changes for such additions.
    1169                 :      * constrainedLoop indicates whether the only operations which can happen
    1170                 :      * in the loop body are int/double arithmetic and bitops, and reads/writes
    1171                 :      * from known dense arrays which can only produce ints and doubles.
    1172                 :      */
    1173                 : 
    1174                 :     /* This value must be in the outer loop: loops with inline calls are not constrained. */
    1175             451 :     JS_ASSERT(pushed.frame == CrossScriptSSA::OUTER_FRAME);
    1176                 : 
    1177             451 :     JS_ASSERT(pushed.v.kind() == SSAValue::PUSHED);
    1178             451 :     jsbytecode *PC = outerScript->code + pushed.v.pushedOffset();
    1179                 : 
    1180             451 :     JSOp op = JSOp(*PC);
    1181             451 :     if (op != JSOP_MUL && op != JSOP_ADD)
    1182             348 :         return false;
    1183                 : 
    1184             103 :     if (valueFlowsToBitops(pushed.v)) {
    1185              23 :         JaegerSpew(JSpew_Analysis, "Integer result flows to bitops\n");
    1186              23 :         return true;
    1187                 :     }
    1188                 : 
    1189              80 :     if (op == JSOP_MUL) {
    1190                 :         /*
    1191                 :          * If the multiply will only be used in an addition, negative zero can
    1192                 :          * be ignored as long as the other operand in the addition cannot be
    1193                 :          * negative zero.
    1194                 :          */
    1195               7 :         if (!outerAnalysis->trackUseChain(pushed.v))
    1196               0 :             return false;
    1197                 : 
    1198               7 :         SSAUseChain *use = outerAnalysis->useChain(pushed.v);
    1199               7 :         if (!use || use->next || !use->popped || outerScript->code[use->offset] != JSOP_ADD)
    1200               3 :             return false;
    1201                 : 
    1202               4 :         if (use->u.which == 1) {
    1203                 :             /*
    1204                 :              * Only ignore negative zero if this is the RHS of an addition.
    1205                 :              * Otherwise the result of the other side could change to a double
    1206                 :              * after the first LHS has been computed, and be affected by a
    1207                 :              * negative zero LHS.
    1208                 :              */
    1209               0 :             return false;
    1210                 :         }
    1211                 : 
    1212               4 :         TypeSet *lhsTypes = outerAnalysis->poppedTypes(use->offset, 1);
    1213               4 :         if (lhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
    1214               2 :             return false;
    1215                 : 
    1216               2 :         JaegerSpew(JSpew_Analysis, "Integer result is RHS in integer addition\n");
    1217               2 :         return true;
    1218                 :     }
    1219                 : 
    1220              73 :     return false;
    1221                 : }
    1222                 : 
    1223                 : bool
    1224             242 : LoopState::valueFlowsToBitops(const analyze::SSAValue &v)
    1225                 : {
    1226                 :     /*
    1227                 :      * Determine whether v can only be used in a bitop later in the same
    1228                 :      * iteration of this loop, or in additions whose result is also only
    1229                 :      * used in such a bitop.
    1230                 :      */
    1231             242 :     if (!outerAnalysis->trackUseChain(v))
    1232               0 :         return false;
    1233                 : 
    1234             242 :     SSAUseChain *use = outerAnalysis->useChain(v);
    1235             597 :     while (use) {
    1236             270 :         if (!use->popped) {
    1237                 :             /*
    1238                 :              * Ignore variables used in phi nodes, so long as the variable is
    1239                 :              * dead at the phi. We don't track live variables across back edges
    1240                 :              * or complex control flow.
    1241                 :              */
    1242              85 :             if (v.kind() == SSAValue::VAR) {
    1243              85 :                 analyze::Lifetime *lifetime = outerAnalysis->liveness(v.varSlot()).live(use->offset);
    1244              85 :                 if (!lifetime) {
    1245              14 :                     use = use->next;
    1246              14 :                     continue;
    1247                 :                 }
    1248                 :             }
    1249              71 :             return false;
    1250                 :         }
    1251                 : 
    1252             185 :         if (use->offset > lifetime->backedge)
    1253               0 :             return false;
    1254                 : 
    1255             185 :         jsbytecode *pc = outerScript->code + use->offset;
    1256             185 :         JSOp op = JSOp(*pc);
    1257             185 :         switch (op) {
    1258                 :           case JSOP_ADD:
    1259                 :           case JSOP_GETLOCAL: {
    1260                 :             SSAValue pushv;
    1261              54 :             pushv.initPushed(use->offset, 0);
    1262              54 :             if (!valueFlowsToBitops(pushv))
    1263               6 :                 return false;
    1264              48 :             break;
    1265                 :           }
    1266                 : 
    1267                 :           case JSOP_SETLOCAL: {
    1268              85 :             uint32_t slot = GetBytecodeSlot(outerScript, pc);
    1269              85 :             if (!outerAnalysis->trackSlot(slot))
    1270               0 :                 return false;
    1271                 :             SSAValue writev;
    1272              85 :             writev.initWritten(slot, use->offset);
    1273              85 :             if (!valueFlowsToBitops(writev))
    1274              71 :                 return false;
    1275              14 :             break;
    1276                 :           }
    1277                 : 
    1278                 :           case JSOP_BITAND:
    1279                 :           case JSOP_BITOR:
    1280                 :           case JSOP_BITXOR:
    1281                 :           case JSOP_RSH:
    1282                 :           case JSOP_LSH:
    1283                 :           case JSOP_URSH:
    1284                 :           case JSOP_BITNOT:
    1285              37 :             break;
    1286                 : 
    1287                 :           default:
    1288               9 :             return false;
    1289                 :         }
    1290                 : 
    1291              99 :         use = use->next;
    1292                 :     }
    1293                 : 
    1294              85 :     return true;
    1295                 : }
    1296                 : 
    1297                 : void
    1298            8588 : LoopState::restoreInvariants(jsbytecode *pc, Assembler &masm,
    1299                 :                              Vector<TemporaryCopy> *temporaryCopies, Vector<Jump> *jumps)
    1300                 : {
    1301                 :     /*
    1302                 :      * Restore all invariants in memory when entering the loop or after any
    1303                 :      * scripted or C++ call, and check that all hoisted conditions still hold.
    1304                 :      * Care should be taken not to clobber the return register or callee-saved
    1305                 :      * registers, which may still be live after some calls.
    1306                 :      */
    1307                 : 
    1308            8588 :     Registers regs(Registers::TempRegs);
    1309            8588 :     regs.takeReg(Registers::ReturnReg);
    1310            8588 :     if (regs.hasReg(JSReturnReg_Data))
    1311               0 :         regs.takeReg(JSReturnReg_Data);
    1312            8588 :     if (regs.hasReg(JSReturnReg_Type))
    1313               0 :         regs.takeReg(JSReturnReg_Type);
    1314                 : 
    1315            8588 :     RegisterID T0 = regs.takeAnyReg().reg();
    1316            8588 :     RegisterID T1 = regs.takeAnyReg().reg();
    1317                 : 
    1318           42154 :     for (unsigned i = 0; i < invariantEntries.length(); i++) {
    1319           33566 :         const InvariantEntry &entry = invariantEntries[i];
    1320           33566 :         switch (entry.kind) {
    1321                 : 
    1322                 :           case InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK:
    1323                 :           case InvariantEntry::TYPED_ARRAY_BOUNDS_CHECK: {
    1324                 :             /*
    1325                 :              * Hoisted bounds checks always have preceding invariant slots
    1326                 :              * in the invariant list, so don't recheck this is an object.
    1327                 :              */
    1328           11362 :             masm.loadPayload(frame.addressOf(entry.u.check.arraySlot), T0);
    1329           11362 :             if (entry.kind == InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK) {
    1330           11277 :                 masm.loadPtr(Address(T0, JSObject::offsetOfElements()), T0);
    1331           11277 :                 masm.load32(Address(T0, ObjectElements::offsetOfInitializedLength()), T0);
    1332                 :             } else {
    1333              85 :                 masm.loadPayload(Address(T0, TypedArray::lengthOffset()), T0);
    1334                 :             }
    1335                 : 
    1336           11362 :             int32_t constant = entry.u.check.constant;
    1337                 : 
    1338           11362 :             if (entry.u.check.valueSlot1 != UINT32_MAX) {
    1339            7586 :                 constant += adjustConstantForIncrement(pc, entry.u.check.valueSlot1);
    1340            7586 :                 masm.loadPayload(frame.addressOf(entry.u.check.valueSlot1), T1);
    1341            7586 :                 if (entry.u.check.valueSlot2 != UINT32_MAX) {
    1342             216 :                     constant += adjustConstantForIncrement(pc, entry.u.check.valueSlot2);
    1343                 :                     Jump overflow = masm.branchAdd32(Assembler::Overflow,
    1344             216 :                                                      frame.addressOf(entry.u.check.valueSlot2), T1);
    1345             216 :                     jumps->append(overflow);
    1346                 :                 }
    1347            7586 :                 if (constant != 0) {
    1348                 :                     Jump overflow = masm.branchAdd32(Assembler::Overflow,
    1349            3171 :                                                      Imm32(constant), T1);
    1350            3171 :                     jumps->append(overflow);
    1351                 :                 }
    1352            7586 :                 Jump j = masm.branch32(Assembler::LessThanOrEqual, T0, T1);
    1353            7586 :                 jumps->append(j);
    1354                 :             } else {
    1355                 :                 Jump j = masm.branch32(Assembler::LessThanOrEqual, T0,
    1356            3776 :                                        Imm32(constant));
    1357            3776 :                 jumps->append(j);
    1358                 :             }
    1359           11362 :             break;
    1360                 :           }
    1361                 : 
    1362                 :           case InvariantEntry::RANGE_CHECK: {
    1363              97 :             int32_t constant = 0;
    1364                 : 
    1365              97 :             constant += adjustConstantForIncrement(pc, entry.u.check.valueSlot1);
    1366              97 :             masm.loadPayload(frame.addressOf(entry.u.check.valueSlot1), T0);
    1367              97 :             if (entry.u.check.valueSlot2 != UINT32_MAX) {
    1368              60 :                 constant += adjustConstantForIncrement(pc, entry.u.check.valueSlot2);
    1369                 :                 Jump overflow = masm.branchAdd32(Assembler::Overflow,
    1370              60 :                                                  frame.addressOf(entry.u.check.valueSlot2), T0);
    1371              60 :                 jumps->append(overflow);
    1372                 :             }
    1373              97 :             if (constant != 0) {
    1374              28 :                 Jump overflow = masm.branchAdd32(Assembler::Overflow, Imm32(constant), T0);
    1375              28 :                 jumps->append(overflow);
    1376                 :             }
    1377              97 :             Jump j = masm.branch32(Assembler::GreaterThan, T0, Imm32(entry.u.check.constant));
    1378              97 :             jumps->append(j);
    1379              97 :             break;
    1380                 :           }
    1381                 : 
    1382                 :           case InvariantEntry::NEGATIVE_CHECK: {
    1383            7062 :             masm.loadPayload(frame.addressOf(entry.u.check.valueSlot1), T0);
    1384            7062 :             if (entry.u.check.constant != 0) {
    1385                 :                 Jump overflow = masm.branchAdd32(Assembler::Overflow,
    1386              12 :                                                  Imm32(entry.u.check.constant), T0);
    1387              12 :                 jumps->append(overflow);
    1388                 :             }
    1389            7062 :             Jump j = masm.branch32(Assembler::LessThan, T0, Imm32(0));
    1390            7062 :             jumps->append(j);
    1391            7062 :             break;
    1392                 :           }
    1393                 : 
    1394                 :           case InvariantEntry::DENSE_ARRAY_SLOTS:
    1395                 :           case InvariantEntry::DENSE_ARRAY_LENGTH: {
    1396           12956 :             uint32_t array = entry.u.array.arraySlot;
    1397           12956 :             Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(array));
    1398           12956 :             jumps->append(notObject);
    1399           12956 :             masm.loadPayload(frame.addressOf(array), T0);
    1400           12956 :             masm.loadPtr(Address(T0, JSObject::offsetOfElements()), T0);
    1401                 : 
    1402           12956 :             Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
    1403                 : 
    1404           12956 :             if (entry.kind == InvariantEntry::DENSE_ARRAY_LENGTH) {
    1405            1679 :                 masm.load32(Address(T0, ObjectElements::offsetOfLength()), T0);
    1406            1679 :                 masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), T0, address);
    1407                 :             } else {
    1408           11277 :                 masm.storePayload(T0, address);
    1409                 :             }
    1410           12956 :             break;
    1411                 :           }
    1412                 : 
    1413                 :           case InvariantEntry::TYPED_ARRAY_SLOTS:
    1414                 :           case InvariantEntry::TYPED_ARRAY_LENGTH: {
    1415             433 :             uint32_t array = entry.u.array.arraySlot;
    1416             433 :             Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(array));
    1417             433 :             jumps->append(notObject);
    1418             433 :             masm.loadPayload(frame.addressOf(array), T0);
    1419                 : 
    1420             433 :             Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
    1421                 : 
    1422             433 :             if (entry.kind == InvariantEntry::TYPED_ARRAY_LENGTH) {
    1423             348 :                 masm.loadPayload(Address(T0, TypedArray::lengthOffset()), T0);
    1424             348 :                 masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), T0, address);
    1425                 :             } else {
    1426              85 :                 masm.loadPtr(Address(T0, js::TypedArray::dataOffset()), T0);
    1427              85 :                 masm.storePayload(T0, address);
    1428                 :             }
    1429             433 :             break;
    1430                 :           }
    1431                 : 
    1432                 :           case InvariantEntry::INVARIANT_ARGS_BASE: {
    1433             377 :             Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
    1434             377 :             masm.loadFrameActuals(outerScript->function(), T0);
    1435             377 :             masm.storePayload(T0, address);
    1436             377 :             break;
    1437                 :           }
    1438                 : 
    1439                 :           case InvariantEntry::INVARIANT_ARGS_LENGTH: {
    1440              20 :             Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
    1441              20 :             masm.load32(Address(JSFrameReg, StackFrame::offsetOfNumActual()), T0);
    1442              20 :             masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), T0, address);
    1443              20 :             break;
    1444                 :           }
    1445                 : 
    1446                 :           case InvariantEntry::INVARIANT_PROPERTY: {
    1447            1259 :             uint32_t object = entry.u.property.objectSlot;
    1448            1259 :             Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(object));
    1449            1259 :             jumps->append(notObject);
    1450            1259 :             masm.loadPayload(frame.addressOf(object), T0);
    1451                 : 
    1452            1259 :             masm.loadInlineSlot(T0, entry.u.property.propertySlot, T1, T0);
    1453                 :             masm.storeValueFromComponents(T1, T0,
    1454            1259 :                 frame.addressOf(frame.getTemporary(entry.u.property.temporary)));
    1455            1259 :             break;
    1456                 :           }
    1457                 : 
    1458                 :           default:
    1459               0 :             JS_NOT_REACHED("Bad invariant kind");
    1460                 :         }
    1461                 :     }
    1462                 : 
    1463                 :     /*
    1464                 :      * If there were any copies of temporaries on the stack, make sure the
    1465                 :      * value we just reconstructed matches the stored value of that temporary.
    1466                 :      * We sync the entire stack before calls, so the copy's slot holds the old
    1467                 :      * value, but in future code we will assume the copy is valid and use the
    1468                 :      * changed value of the invariant.
    1469                 :      */
    1470                 : 
    1471            8698 :     for (unsigned i = 0; temporaryCopies && i < temporaryCopies->length(); i++) {
    1472             110 :         const TemporaryCopy &copy = (*temporaryCopies)[i];
    1473             110 :         masm.compareValue(copy.copy, copy.temporary, T0, T1, jumps);
    1474                 :     }
    1475                 : 
    1476            8588 :     if (temporaryCopies)
    1477              90 :         cx->delete_(temporaryCopies);
    1478            8588 : }
    1479                 : 
    1480                 : /* Loop analysis methods. */
    1481                 : 
    1482                 : /*
    1483                 :  * Get any slot/constant accessed by a loop test operand, in terms of its value
    1484                 :  * at the start of the next loop iteration.
    1485                 :  */
    1486                 : bool
    1487           23088 : LoopState::getLoopTestAccess(const SSAValue &v, uint32_t *pslot, int32_t *pconstant)
    1488                 : {
    1489           23088 :     *pslot = UNASSIGNED;
    1490           23088 :     *pconstant = 0;
    1491                 : 
    1492           23088 :     if (v.kind() == SSAValue::PHI || v.kind() == SSAValue::VAR) {
    1493                 :         /*
    1494                 :          * Getting the value of a variable at a previous offset. Check that it
    1495                 :          * is not updated before the start of the next loop iteration.
    1496                 :          */
    1497                 :         uint32_t slot;
    1498                 :         uint32_t offset;
    1499            9965 :         if (v.kind() == SSAValue::PHI) {
    1500            9183 :             slot = v.phiSlot();
    1501            9183 :             offset = v.phiOffset();
    1502                 :         } else {
    1503             782 :             slot = v.varSlot();
    1504             782 :             offset = v.varInitial() ? 0 : v.varOffset();
    1505                 :         }
    1506            9965 :         if (outerAnalysis->slotEscapes(slot))
    1507               0 :             return false;
    1508            9965 :         if (outerAnalysis->liveness(slot).firstWrite(offset + 1, lifetime->backedge) != UINT32_MAX)
    1509               0 :             return false;
    1510            9965 :         *pslot = slot;
    1511            9965 :         *pconstant = 0;
    1512            9965 :         return true;
    1513                 :     }
    1514                 : 
    1515           13123 :     jsbytecode *pc = outerScript->code + v.pushedOffset();
    1516                 : 
    1517           13123 :     JSOp op = JSOp(*pc);
    1518           13123 :     const JSCodeSpec *cs = &js_CodeSpec[op];
    1519                 : 
    1520                 :     /*
    1521                 :      * If the pc is modifying a variable and the value tested is its earlier value
    1522                 :      * (e.g. 'x++ < n'), we need to account for the modification --- at the start
    1523                 :      * of the next iteration, the value compared will have been 'x - 1'.
    1524                 :      * Note that we don't need to worry about other accesses to the variable
    1525                 :      * in the condition like 'x++ < x', as loop tests where both operands are
    1526                 :      * modified by the loop are rejected.
    1527                 :      */
    1528                 : 
    1529           13123 :     switch (op) {
    1530                 : 
    1531                 :       case JSOP_INCLOCAL:
    1532                 :       case JSOP_DECLOCAL:
    1533                 :       case JSOP_LOCALINC:
    1534                 :       case JSOP_LOCALDEC:
    1535                 :       case JSOP_INCARG:
    1536                 :       case JSOP_DECARG:
    1537                 :       case JSOP_ARGINC:
    1538                 :       case JSOP_ARGDEC: {
    1539             107 :         if (!outerAnalysis->integerOperation(cx, pc))
    1540               0 :             return false;
    1541             107 :         uint32_t slot = GetBytecodeSlot(outerScript, pc);
    1542             107 :         if (outerAnalysis->slotEscapes(slot))
    1543               0 :             return false;
    1544                 : 
    1545             107 :         *pslot = slot;
    1546             107 :         if (cs->format & JOF_POST) {
    1547              28 :             if (cs->format & JOF_INC)
    1548               0 :                 *pconstant = -1;
    1549                 :             else
    1550              28 :                 *pconstant = 1;
    1551                 :         }
    1552             107 :         return true;
    1553                 :       }
    1554                 : 
    1555                 :       case JSOP_ZERO:
    1556                 :       case JSOP_ONE:
    1557                 :       case JSOP_UINT16:
    1558                 :       case JSOP_UINT24:
    1559                 :       case JSOP_INT8:
    1560                 :       case JSOP_INT32:
    1561            8139 :         *pconstant = GetBytecodeInteger(pc);
    1562            8139 :         return true;
    1563                 : 
    1564                 :       default:
    1565            4877 :         return false;
    1566                 :     }
    1567                 : }
    1568                 : 
    1569                 : void
    1570           33602 : LoopState::analyzeLoopTest()
    1571                 : {
    1572           33602 :     if (cc.debugMode())
    1573           15500 :         return;
    1574                 : 
    1575                 :     /* Don't handle do-while loops. */
    1576           18102 :     if (lifetime->entry == lifetime->head)
    1577              33 :         return;
    1578                 : 
    1579                 :     /* Don't handle loops with branching inside their condition. */
    1580           18069 :     if (lifetime->entry < lifetime->lastBlock)
    1581              54 :         return;
    1582                 : 
    1583                 :     /* Get the test performed before branching. */
    1584           18015 :     jsbytecode *backedge = outerScript->code + lifetime->backedge;
    1585           18015 :     if (JSOp(*backedge) != JSOP_IFNE)
    1586               0 :         return;
    1587           18015 :     const SSAValue &test = outerAnalysis->poppedValue(backedge, 0);
    1588           18015 :     if (test.kind() != SSAValue::PUSHED)
    1589             129 :         return;
    1590           17886 :     JSOp cmpop = JSOp(outerScript->code[test.pushedOffset()]);
    1591           17886 :     switch (cmpop) {
    1592                 :       case JSOP_GT:
    1593                 :       case JSOP_GE:
    1594                 :       case JSOP_LT:
    1595                 :       case JSOP_LE:
    1596                 :         break;
    1597                 :       default:
    1598            2424 :         return;
    1599                 :     }
    1600                 : 
    1601           15462 :     SSAValue one = outerAnalysis->poppedValue(test.pushedOffset(), 1);
    1602           15462 :     SSAValue two = outerAnalysis->poppedValue(test.pushedOffset(), 0);
    1603                 : 
    1604                 :     /* The test must be comparing known integers. */
    1605           27808 :     if (outerAnalysis->getValueTypes(one)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32 ||
    1606           12346 :         outerAnalysis->getValueTypes(two)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) {
    1607            3918 :         return;
    1608                 :     }
    1609                 : 
    1610                 :     /* Reverse the condition if the RHS is modified by the loop. */
    1611                 :     uint32_t swapRHS;
    1612                 :     int32_t swapConstant;
    1613           11544 :     if (getLoopTestAccess(two, &swapRHS, &swapConstant)) {
    1614            8961 :         if (swapRHS != UNASSIGNED && outerAnalysis->liveness(swapRHS).firstWrite(lifetime) != UINT32_MAX) {
    1615               0 :             SSAValue tmp = one;
    1616               0 :             one = two;
    1617               0 :             two = tmp;
    1618               0 :             cmpop = ReverseCompareOp(cmpop);
    1619                 :         }
    1620                 :     }
    1621                 : 
    1622                 :     uint32_t lhs;
    1623                 :     int32_t lhsConstant;
    1624           11544 :     if (!getLoopTestAccess(one, &lhs, &lhsConstant))
    1625            2294 :         return;
    1626                 : 
    1627            9250 :     uint32_t rhs = UNASSIGNED;
    1628            9250 :     int32_t rhsConstant = 0;
    1629            9250 :     CrossSSAValue rhsv(CrossScriptSSA::OUTER_FRAME, two);
    1630            9250 :     if (!getEntryValue(rhsv, &rhs, &rhsConstant))
    1631             525 :         return;
    1632            8725 :     if (!loopInvariantEntry(rhs))
    1633               0 :         return;
    1634                 : 
    1635            8725 :     if (lhs == UNASSIGNED)
    1636               0 :         return;
    1637                 : 
    1638                 :     int32_t constant;
    1639            8725 :     if (!SafeSub(rhsConstant, lhsConstant, &constant))
    1640               0 :         return;
    1641                 : 
    1642                 :     /* x > y ==> x >= y + 1 */
    1643            8725 :     if (cmpop == JSOP_GT && !SafeAdd(constant, 1, &constant))
    1644               0 :         return;
    1645                 : 
    1646                 :     /* x < y ==> x <= y - 1 */
    1647            8725 :     if (cmpop == JSOP_LT && !SafeSub(constant, 1, &constant))
    1648               0 :         return;
    1649                 : 
    1650                 :     /* Passed all filters, this is a loop test we can capture. */
    1651                 : 
    1652            8725 :     this->testLHS = lhs;
    1653            8725 :     this->testRHS = rhs;
    1654            8725 :     this->testConstant = constant;
    1655            8725 :     this->testLessEqual = (cmpop == JSOP_LT || cmpop == JSOP_LE);
    1656                 : }
    1657                 : 
    1658                 : void
    1659           33602 : LoopState::analyzeLoopIncrements()
    1660                 : {
    1661           33602 :     if (cc.debugMode())
    1662           15500 :         return;
    1663                 : 
    1664                 :     /*
    1665                 :      * Find locals and arguments which are used in exactly one inc/dec operation in every
    1666                 :      * iteration of the loop (we only match against the last basic block, but could
    1667                 :      * also handle the first basic block).
    1668                 :      */
    1669                 : 
    1670           82762 :     for (uint32_t slot = ArgSlot(0); slot < LocalSlot(outerScript, outerScript->nfixed); slot++) {
    1671           64660 :         if (outerAnalysis->slotEscapes(slot))
    1672            8293 :             continue;
    1673                 : 
    1674           56367 :         uint32_t offset = outerAnalysis->liveness(slot).onlyWrite(lifetime);
    1675           56367 :         if (offset == UINT32_MAX || offset < lifetime->lastBlock)
    1676           43745 :             continue;
    1677                 : 
    1678           12622 :         jsbytecode *pc = outerScript->code + offset;
    1679           12622 :         JSOp op = JSOp(*pc);
    1680           12622 :         const JSCodeSpec *cs = &js_CodeSpec[op];
    1681           12622 :         if (cs->format & (JOF_INC | JOF_DEC)) {
    1682            9936 :             if (!outerAnalysis->integerOperation(cx, pc))
    1683              47 :                 continue;
    1684                 : 
    1685                 :             Increment inc;
    1686            9889 :             inc.slot = slot;
    1687            9889 :             inc.offset = offset;
    1688            9889 :             increments.append(inc);
    1689                 :         }
    1690                 :     }
    1691                 : }
    1692                 : 
    1693                 : bool
    1694            2211 : LoopState::definiteArrayAccess(const SSAValue &obj, const SSAValue &index)
    1695                 : {
    1696                 :     /*
    1697                 :      * Check that an index on obj is definitely accessing a dense array, giving
    1698                 :      * either a value modelled by the pushed types or a hole. This needs to be
    1699                 :      * robust against recompilations that could be triggered inside the loop:
    1700                 :      * the array must be loop invariant, and the index must definitely be an
    1701                 :      * integer.
    1702                 :      *
    1703                 :      * This is used to determine if we can ignore possible integer overflow in
    1704                 :      * an operation; if this site could read a non-integer element out of the
    1705                 :      * array or invoke a scripted getter/setter, it could produce a string or
    1706                 :      * other value by which the overflow could be observed.
    1707                 :      */
    1708                 : 
    1709            2211 :     TypeSet *objTypes = outerAnalysis->getValueTypes(obj);
    1710            2211 :     TypeSet *elemTypes = outerAnalysis->getValueTypes(index);
    1711                 : 
    1712            4191 :     if (objTypes->getKnownTypeTag(cx) != JSVAL_TYPE_OBJECT ||
    1713            1980 :         elemTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) {
    1714             275 :         return false;
    1715                 :     }
    1716                 : 
    1717            1936 :     if (objTypes->hasObjectFlags(cx, OBJECT_FLAG_NON_DENSE_ARRAY))
    1718             225 :         return false;
    1719                 : 
    1720            1711 :     if (ArrayPrototypeHasIndexedProperty(cx, outerScript))
    1721               1 :         return false;
    1722                 : 
    1723                 :     uint32_t objSlot;
    1724                 :     int32_t objConstant;
    1725            1710 :     CrossSSAValue objv(CrossScriptSSA::OUTER_FRAME, obj);
    1726            1710 :     if (!getEntryValue(objv, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0)
    1727              88 :         return false;
    1728            1622 :     if (!loopInvariantEntry(objSlot))
    1729               0 :         return false;
    1730                 : 
    1731                 :     /* Bitops must produce integers. */
    1732            1622 :     if (index.kind() == SSAValue::PUSHED) {
    1733             341 :         JSOp op = JSOp(outerScript->code[index.pushedOffset()]);
    1734             341 :         switch (op) {
    1735                 :           case JSOP_BITAND:
    1736                 :           case JSOP_BITOR:
    1737                 :           case JSOP_BITXOR:
    1738                 :           case JSOP_BITNOT:
    1739                 :           case JSOP_RSH:
    1740                 :           case JSOP_LSH:
    1741                 :           case JSOP_URSH:
    1742               7 :             return true;
    1743                 :           default:;
    1744                 :         }
    1745                 :     }
    1746                 : 
    1747                 :     uint32_t indexSlot;
    1748                 :     int32_t indexConstant;
    1749            1615 :     CrossSSAValue indexv(CrossScriptSSA::OUTER_FRAME, index);
    1750            1615 :     if (!getEntryValue(indexv, &indexSlot, &indexConstant))
    1751             259 :         return false;
    1752                 : 
    1753                 :     /*
    1754                 :      * The index is determined from a variable's value at loop entry. We don't
    1755                 :      * carry values with ignored overflows around loop back edges, so will know
    1756                 :      * the index is a non-integer before such overflows are ignored.
    1757                 :      */
    1758            1356 :     return true;
    1759                 : }
    1760                 : 
    1761                 : void
    1762           35202 : LoopState::analyzeLoopBody(unsigned frame)
    1763                 : {
    1764           35202 :     if (cc.debugMode()) {
    1765           15500 :         skipAnalysis = true;
    1766           15500 :         return;
    1767                 :     }
    1768                 : 
    1769           19702 :     JSScript *script = ssa->getFrame(frame).script;
    1770           19702 :     analyze::ScriptAnalysis *analysis = script->analysis();
    1771           19702 :     JS_ASSERT(analysis && !analysis->failed() && analysis->ranInference());
    1772                 : 
    1773                 :     /*
    1774                 :      * The temporaries need to be positioned after all values in the deepest
    1775                 :      * inlined frame plus another stack frame pushed by, e.g. ic::Call.
    1776                 :      * This new frame will have been partially initialized by the call, and
    1777                 :      * we don't want to scribble on that frame when restoring invariants.
    1778                 :      */
    1779                 :     temporariesStart =
    1780                 :         Max<uint32_t>(temporariesStart,
    1781           19702 :                     ssa->getFrame(frame).depth + VALUES_PER_STACK_FRAME * 2 + script->nslots);
    1782                 : 
    1783           19702 :     if (script->failedBoundsCheck || analysis->localsAliasStack())
    1784            3190 :         skipAnalysis = true;
    1785                 : 
    1786                 :     /* Analyze the entire script for frames inlined in the loop body. */
    1787           19702 :     unsigned start = (frame == CrossScriptSSA::OUTER_FRAME) ? lifetime->head + JSOP_LOOPHEAD_LENGTH : 0;
    1788           19702 :     unsigned end = (frame == CrossScriptSSA::OUTER_FRAME) ? lifetime->backedge : script->length;
    1789                 : 
    1790           19702 :     unsigned offset = start;
    1791          659515 :     while (offset < end) {
    1792          620111 :         jsbytecode *pc = script->code + offset;
    1793          620111 :         uint32_t successorOffset = offset + GetBytecodeLength(pc);
    1794                 : 
    1795          620111 :         analyze::Bytecode *opinfo = analysis->maybeCode(offset);
    1796          620111 :         if (!opinfo) {
    1797            1592 :             offset = successorOffset;
    1798            1592 :             continue;
    1799                 :         }
    1800                 : 
    1801          618519 :         JSOp op = JSOp(*pc);
    1802                 : 
    1803                 :         /* Don't do any hoisting for outer loops in case of nesting. */
    1804          618519 :         if (op == JSOP_LOOPHEAD)
    1805            2746 :             skipAnalysis = true;
    1806                 : 
    1807          618519 :         switch (op) {
    1808                 : 
    1809                 :           case JSOP_CALL: {
    1810                 :             /*
    1811                 :              * Don't hoist within this loop unless calls at this site are inlined.
    1812                 :              * :XXX: also recognize native calls which will be inlined.
    1813                 :              */
    1814           34393 :             bool foundInline = false;
    1815           92126 :             for (unsigned i = 0; !foundInline && i < ssa->numFrames(); i++) {
    1816           57733 :                 if (ssa->iterFrame(i).parent == frame && ssa->iterFrame(i).parentpc == pc)
    1817            1478 :                     foundInline = true;
    1818                 :             }
    1819           34393 :             if (!foundInline)
    1820           32915 :                 skipAnalysis = true;
    1821           34393 :             break;
    1822                 :           }
    1823                 : 
    1824                 :           case JSOP_EVAL:
    1825                 :           case JSOP_FUNCALL:
    1826                 :           case JSOP_FUNAPPLY:
    1827                 :           case JSOP_NEW:
    1828            3153 :             skipAnalysis = true;
    1829            3153 :             break;
    1830                 : 
    1831                 :           case JSOP_SETELEM: {
    1832            5506 :             SSAValue objValue = analysis->poppedValue(pc, 2);
    1833            5506 :             SSAValue elemValue = analysis->poppedValue(pc, 1);
    1834                 : 
    1835            5506 :             TypeSet *objTypes = analysis->getValueTypes(objValue);
    1836            5506 :             TypeSet *elemTypes = analysis->getValueTypes(elemValue);
    1837                 : 
    1838                 :             /*
    1839                 :              * Mark the modset as unknown if the index might be non-integer,
    1840                 :              * we don't want to consider the SETELEM PIC here.
    1841                 :              */
    1842            5506 :             if (objTypes->unknownObject() || elemTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) {
    1843             641 :                 unknownModset = true;
    1844             641 :                 break;
    1845                 :             }
    1846                 : 
    1847            4865 :             objTypes->addFreeze(cx);
    1848            9983 :             for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
    1849            5118 :                 TypeObject *object = objTypes->getTypeObject(i);
    1850            5118 :                 if (!object)
    1851              18 :                     continue;
    1852            5100 :                 if (!addModifiedProperty(object, JSID_VOID))
    1853               0 :                     return;
    1854            5100 :                 if (analysis->getCode(pc).arrayWriteHole && !addGrowArray(object))
    1855               0 :                     return;
    1856                 :             }
    1857                 : 
    1858            4865 :             if (constrainedLoop && !definiteArrayAccess(objValue, elemValue))
    1859             290 :                 constrainedLoop = false;
    1860            4865 :             break;
    1861                 :           }
    1862                 : 
    1863                 :           case JSOP_GETELEM: {
    1864           17499 :             SSAValue objValue = analysis->poppedValue(pc, 1);
    1865           17499 :             SSAValue elemValue = analysis->poppedValue(pc, 0);
    1866                 : 
    1867           17499 :             if (constrainedLoop && !definiteArrayAccess(objValue, elemValue))
    1868             558 :                 constrainedLoop = false;
    1869           17499 :             break;
    1870                 :           }
    1871                 : 
    1872                 :           case JSOP_SETPROP:
    1873                 :           case JSOP_SETMETHOD: {
    1874            1376 :             JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc));
    1875            1376 :             jsid id = MakeTypeId(cx, ATOM_TO_JSID(atom));
    1876                 : 
    1877            1376 :             TypeSet *objTypes = analysis->poppedTypes(pc, 1);
    1878            1376 :             if (objTypes->unknownObject()) {
    1879             206 :                 unknownModset = true;
    1880             206 :                 break;
    1881                 :             }
    1882                 : 
    1883            1170 :             objTypes->addFreeze(cx);
    1884            2155 :             for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
    1885             985 :                 TypeObject *object = objTypes->getTypeObject(i);
    1886             985 :                 if (!object)
    1887              36 :                     continue;
    1888             949 :                 if (!addModifiedProperty(object, id))
    1889               0 :                     continue;
    1890                 :             }
    1891                 : 
    1892            1170 :             constrainedLoop = false;
    1893            1170 :             break;
    1894                 :           }
    1895                 : 
    1896                 :           case JSOP_ENUMELEM:
    1897                 :           case JSOP_ENUMCONSTELEM:
    1898               0 :             unknownModset = true;
    1899               0 :             break;
    1900                 : 
    1901                 :           case JSOP_LOOPHEAD:
    1902                 :           case JSOP_LOOPENTRY:
    1903                 :           case JSOP_POP:
    1904                 :           case JSOP_ZERO:
    1905                 :           case JSOP_ONE:
    1906                 :           case JSOP_INT8:
    1907                 :           case JSOP_INT32:
    1908                 :           case JSOP_UINT16:
    1909                 :           case JSOP_UINT24:
    1910                 :           case JSOP_FALSE:
    1911                 :           case JSOP_TRUE:
    1912                 :           case JSOP_GETARG:
    1913                 :           case JSOP_SETARG:
    1914                 :           case JSOP_INCARG:
    1915                 :           case JSOP_DECARG:
    1916                 :           case JSOP_ARGINC:
    1917                 :           case JSOP_ARGDEC:
    1918                 :           case JSOP_THIS:
    1919                 :           case JSOP_GETLOCAL:
    1920                 :           case JSOP_SETLOCAL:
    1921                 :           case JSOP_SETLOCALPOP:
    1922                 :           case JSOP_INCLOCAL:
    1923                 :           case JSOP_DECLOCAL:
    1924                 :           case JSOP_LOCALINC:
    1925                 :           case JSOP_LOCALDEC:
    1926                 :           case JSOP_IFEQ:
    1927                 :           case JSOP_IFNE:
    1928                 :           case JSOP_AND:
    1929                 :           case JSOP_OR:
    1930                 :           case JSOP_GOTO:
    1931          268386 :             break;
    1932                 : 
    1933                 :           case JSOP_ADD:
    1934                 :           case JSOP_SUB:
    1935                 :           case JSOP_MUL:
    1936                 :           case JSOP_MOD:
    1937                 :           case JSOP_DIV:
    1938                 :           case JSOP_BITAND:
    1939                 :           case JSOP_BITOR:
    1940                 :           case JSOP_BITXOR:
    1941                 :           case JSOP_RSH:
    1942                 :           case JSOP_LSH:
    1943                 :           case JSOP_URSH:
    1944                 :           case JSOP_EQ:
    1945                 :           case JSOP_NE:
    1946                 :           case JSOP_LT:
    1947                 :           case JSOP_LE:
    1948                 :           case JSOP_GT:
    1949                 :           case JSOP_GE:
    1950                 :           case JSOP_STRICTEQ:
    1951                 :           case JSOP_STRICTNE: {
    1952           45667 :             JSValueType type = analysis->poppedTypes(pc, 1)->getKnownTypeTag(cx);
    1953           45667 :             if (type != JSVAL_TYPE_INT32 && type != JSVAL_TYPE_DOUBLE)
    1954           15366 :                 constrainedLoop = false;
    1955                 :           }
    1956                 :           /* FALLTHROUGH */
    1957                 : 
    1958                 :           case JSOP_POS:
    1959                 :           case JSOP_NEG:
    1960                 :           case JSOP_BITNOT: {
    1961           50275 :             JSValueType type = analysis->poppedTypes(pc, 0)->getKnownTypeTag(cx);
    1962           50275 :             if (type != JSVAL_TYPE_INT32 && type != JSVAL_TYPE_DOUBLE)
    1963           12566 :                 constrainedLoop = false;
    1964           50275 :             break;
    1965                 :           }
    1966                 : 
    1967                 :           default:
    1968          237931 :             constrainedLoop = false;
    1969          237931 :             break;
    1970                 :         }
    1971                 : 
    1972          618519 :         offset = successorOffset;
    1973                 :     }
    1974                 : }
    1975                 : 
    1976                 : bool
    1977            1035 : LoopState::addGrowArray(TypeObject *object)
    1978                 : {
    1979                 :     static const uint32_t MAX_SIZE = 10;
    1980            1468 :     for (unsigned i = 0; i < growArrays.length(); i++) {
    1981             486 :         if (growArrays[i] == object)
    1982              53 :             return true;
    1983                 :     }
    1984             982 :     if (growArrays.length() >= MAX_SIZE) {
    1985               0 :         unknownModset = true;
    1986               0 :         return false;
    1987                 :     }
    1988             982 :     growArrays.append(object);
    1989                 : 
    1990             982 :     return true;
    1991                 : }
    1992                 : 
    1993                 : bool
    1994            6049 : LoopState::addModifiedProperty(TypeObject *object, jsid id)
    1995                 : {
    1996                 :     static const uint32_t MAX_SIZE = 20;
    1997            9519 :     for (unsigned i = 0; i < modifiedProperties.length(); i++) {
    1998            4928 :         if (modifiedProperties[i].object == object && modifiedProperties[i].id == id)
    1999            1458 :             return true;
    2000                 :     }
    2001            4591 :     if (modifiedProperties.length() >= MAX_SIZE) {
    2002               0 :         unknownModset = true;
    2003               0 :         return false;
    2004                 :     }
    2005                 : 
    2006                 :     ModifiedProperty property;
    2007            4591 :     property.object = object;
    2008            4591 :     property.id = id;
    2009            4591 :     modifiedProperties.append(property);
    2010                 : 
    2011            4591 :     return true;
    2012                 : }
    2013                 : 
    2014                 : bool
    2015             948 : LoopState::hasGrowArray(TypeObject *object)
    2016                 : {
    2017             948 :     if (unknownModset)
    2018               0 :         return true;
    2019            3389 :     for (unsigned i = 0; i < growArrays.length(); i++) {
    2020            2666 :         if (growArrays[i] == object)
    2021             225 :             return true;
    2022                 :     }
    2023             723 :     return false;
    2024                 : }
    2025                 : 
    2026                 : bool
    2027            2516 : LoopState::hasModifiedProperty(TypeObject *object, jsid id)
    2028                 : {
    2029            2516 :     if (unknownModset)
    2030              14 :         return true;
    2031            2502 :     id = MakeTypeId(cx, id);
    2032            2562 :     for (unsigned i = 0; i < modifiedProperties.length(); i++) {
    2033             215 :         if (modifiedProperties[i].object == object && modifiedProperties[i].id == id)
    2034             155 :             return true;
    2035                 :     }
    2036            2347 :     return false;
    2037                 : }
    2038                 : 
    2039                 : uint32_t
    2040            8024 : LoopState::getIncrement(uint32_t slot)
    2041                 : {
    2042           15646 :     for (unsigned i = 0; i < increments.length(); i++) {
    2043            8234 :         if (increments[i].slot == slot)
    2044             612 :             return increments[i].offset;
    2045                 :     }
    2046            7412 :     return UINT32_MAX;
    2047                 : }
    2048                 : 
    2049                 : int32_t
    2050            7959 : LoopState::adjustConstantForIncrement(jsbytecode *pc, uint32_t slot)
    2051                 : {
    2052                 :     /*
    2053                 :      * The only terms that can appear in a hoisted bounds check are either
    2054                 :      * loop invariant or are incremented or decremented exactly once in each
    2055                 :      * iteration of the loop. Depending on the current pc in the body of the
    2056                 :      * loop, return a constant adjustment if an increment/decrement for slot
    2057                 :      * has not yet happened, such that 'slot + n' at this point is the value
    2058                 :      * of slot at the start of the next iteration.
    2059                 :      */
    2060            7959 :     uint32_t offset = getIncrement(slot);
    2061                 : 
    2062                 :     /*
    2063                 :      * Note the '<' here. If this PC is at one of the increment opcodes, then
    2064                 :      * behave as if the increment has not happened yet. This is needed for loop
    2065                 :      * entry points, which can be directly at an increment. We won't rejoin
    2066                 :      * after the increment, as we only take stub calls in such situations on
    2067                 :      * integer overflow, which will disable hoisted conditions involving the
    2068                 :      * variable anyways.
    2069                 :      */
    2070            7959 :     if (offset == UINT32_MAX || offset < uint32_t(pc - outerScript->code))
    2071            7536 :         return 0;
    2072                 : 
    2073             423 :     switch (JSOp(outerScript->code[offset])) {
    2074                 :       case JSOP_INCLOCAL:
    2075                 :       case JSOP_LOCALINC:
    2076                 :       case JSOP_INCARG:
    2077                 :       case JSOP_ARGINC:
    2078             147 :         return 1;
    2079                 :       case JSOP_DECLOCAL:
    2080                 :       case JSOP_LOCALDEC:
    2081                 :       case JSOP_DECARG:
    2082                 :       case JSOP_ARGDEC:
    2083             276 :         return -1;
    2084                 :       default:
    2085               0 :         JS_NOT_REACHED("Bad op");
    2086                 :         return 0;
    2087                 :     }
    2088                 : }
    2089                 : 
    2090                 : bool
    2091           24616 : LoopState::getEntryValue(const CrossSSAValue &iv, uint32_t *pslot, int32_t *pconstant)
    2092                 : {
    2093           24616 :     CrossSSAValue cv = ssa->foldValue(iv);
    2094                 : 
    2095           24616 :     JSScript *script = ssa->getFrame(cv.frame).script;
    2096           24616 :     ScriptAnalysis *analysis = script->analysis();
    2097           24616 :     const SSAValue &v = cv.v;
    2098                 : 
    2099                 :     /*
    2100                 :      * For a stack value popped by the bytecode at offset, try to get an
    2101                 :      * expression 'slot + constant' with the same value as the stack value
    2102                 :      * and expressed in terms of the state at loop entry.
    2103                 :      */
    2104                 : 
    2105           24616 :     if (v.kind() == SSAValue::PHI) {
    2106            4602 :         if (cv.frame != CrossScriptSSA::OUTER_FRAME)
    2107               1 :             return false;
    2108            4601 :         if (v.phiSlot() >= TotalSlots(script))
    2109               5 :             return false;
    2110            4744 :         if (v.phiOffset() > lifetime->head &&
    2111             148 :             outerAnalysis->liveness(v.phiSlot()).firstWrite(lifetime) < v.phiOffset()) {
    2112              70 :             return false;
    2113                 :         }
    2114            4526 :         *pslot = v.phiSlot();
    2115            4526 :         *pconstant = 0;
    2116            4526 :         return true;
    2117                 :     }
    2118                 : 
    2119           20014 :     if (v.kind() == SSAValue::VAR) {
    2120            7961 :         if (cv.frame != CrossScriptSSA::OUTER_FRAME)
    2121              16 :             return false;
    2122            7945 :         if (v.varInitial() || v.varOffset() < lifetime->head) {
    2123            7619 :             *pslot = v.varSlot();
    2124            7619 :             *pconstant = 0;
    2125            7619 :             return true;
    2126                 :         }
    2127                 :     }
    2128                 : 
    2129           12379 :     if (v.kind() != SSAValue::PUSHED)
    2130             326 :         return false;
    2131                 : 
    2132           12053 :     jsbytecode *pc = script->code + v.pushedOffset();
    2133           12053 :     JSOp op = (JSOp)*pc;
    2134                 : 
    2135           12053 :     switch (op) {
    2136                 : 
    2137                 :       case JSOP_GETLOCAL:
    2138                 :       case JSOP_LOCALINC:
    2139                 :       case JSOP_INCLOCAL:
    2140                 :       case JSOP_GETARG:
    2141                 :       case JSOP_ARGINC:
    2142                 :       case JSOP_INCARG: {
    2143             196 :         if (cv.frame != CrossScriptSSA::OUTER_FRAME || !analysis->integerOperation(cx, pc))
    2144               0 :             return false;
    2145             196 :         uint32_t slot = GetBytecodeSlot(outerScript, pc);
    2146             196 :         if (outerAnalysis->slotEscapes(slot))
    2147              23 :             return false;
    2148             173 :         uint32_t write = outerAnalysis->liveness(slot).firstWrite(lifetime);
    2149             173 :         if (write != UINT32_MAX && write < v.pushedOffset()) {
    2150                 :             /* Variable has been modified since the start of the loop. */
    2151               3 :             return false;
    2152                 :         }
    2153             170 :         *pslot = slot;
    2154             170 :         *pconstant = (op == JSOP_INCLOCAL || op == JSOP_INCARG) ? 1 : 0;
    2155             170 :         return true;
    2156                 :       }
    2157                 : 
    2158                 :       case JSOP_THIS:
    2159             260 :         if (cv.frame != CrossScriptSSA::OUTER_FRAME)
    2160               0 :             return false;
    2161             260 :         *pslot = ThisSlot();
    2162             260 :         *pconstant = 0;
    2163             260 :         return true;
    2164                 : 
    2165                 :       case JSOP_ZERO:
    2166                 :       case JSOP_ONE:
    2167                 :       case JSOP_UINT16:
    2168                 :       case JSOP_UINT24:
    2169                 :       case JSOP_INT8:
    2170                 :       case JSOP_INT32:
    2171            6985 :         *pslot = UNASSIGNED;
    2172            6985 :         *pconstant = GetBytecodeInteger(pc);
    2173            6985 :         return true;
    2174                 : 
    2175                 :       case JSOP_LENGTH: {
    2176            1934 :         CrossSSAValue lengthcv(cv.frame, analysis->poppedValue(v.pushedOffset(), 0));
    2177            1934 :         FrameEntry *tmp = invariantLength(lengthcv);
    2178            1934 :         if (!tmp)
    2179             255 :             return false;
    2180            1679 :         *pslot = frame.outerSlot(tmp);
    2181            1679 :         *pconstant = 0;
    2182            1679 :         return true;
    2183                 :       }
    2184                 : 
    2185                 :       case JSOP_GETPROP: {
    2186             350 :         JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc));
    2187             350 :         jsid id = ATOM_TO_JSID(atom);
    2188             350 :         CrossSSAValue objcv(cv.frame, analysis->poppedValue(v.pushedOffset(), 0));
    2189             350 :         FrameEntry *tmp = invariantProperty(objcv, id);
    2190             350 :         if (!tmp)
    2191             156 :             return false;
    2192             194 :         *pslot = frame.outerSlot(tmp);
    2193             194 :         *pconstant = 0;
    2194             194 :         return true;
    2195                 :       }
    2196                 : 
    2197                 :       default:
    2198            2328 :         return false;
    2199                 :     }
    2200                 : }
    2201                 : 
    2202                 : bool
    2203            9584 : LoopState::computeInterval(const CrossSSAValue &cv, int32_t *pmin, int32_t *pmax)
    2204                 : {
    2205            9584 :     JSScript *script = ssa->getFrame(cv.frame).script;
    2206            9584 :     ScriptAnalysis *analysis = script->analysis();
    2207            9584 :     const SSAValue &v = cv.v;
    2208                 : 
    2209            9584 :     if (v.kind() == SSAValue::VAR && !v.varInitial()) {
    2210             168 :         jsbytecode *pc = script->code + v.varOffset();
    2211             168 :         switch (JSOp(*pc)) {
    2212                 :           case JSOP_SETLOCAL:
    2213                 :           case JSOP_SETARG: {
    2214             166 :             CrossSSAValue ncv(cv.frame, analysis->poppedValue(pc, 0));
    2215             166 :             return computeInterval(ncv, pmin, pmax);
    2216                 :           }
    2217                 : 
    2218                 :           default:
    2219               2 :             return false;
    2220                 :         }
    2221                 :     }
    2222                 : 
    2223            9416 :     if (v.kind() != SSAValue::PUSHED)
    2224             971 :         return false;
    2225                 : 
    2226            8445 :     jsbytecode *pc = script->code + v.pushedOffset();
    2227            8445 :     JSOp op = (JSOp)*pc;
    2228                 : 
    2229                 :     /* Note: this was adapted from similar code in nanojit/LIR.cpp */
    2230            8445 :     switch (op) {
    2231                 : 
    2232                 :       case JSOP_ZERO:
    2233                 :       case JSOP_ONE:
    2234                 :       case JSOP_UINT16:
    2235                 :       case JSOP_UINT24:
    2236                 :       case JSOP_INT8:
    2237                 :       case JSOP_INT32: {
    2238             476 :         int32_t constant = GetBytecodeInteger(pc);
    2239             476 :         *pmin = constant;
    2240             476 :         *pmax = constant;
    2241             476 :         return true;
    2242                 :       }
    2243                 : 
    2244                 :       case JSOP_BITAND: {
    2245                 :         int32_t lhsmin, lhsmax, rhsmin, rhsmax;
    2246             164 :         CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1));
    2247             164 :         CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
    2248             164 :         bool haslhs = computeInterval(lhsv, &lhsmin, &lhsmax);
    2249             164 :         bool hasrhs = computeInterval(rhsv, &rhsmin, &rhsmax);
    2250                 : 
    2251                 :         /* Only handle bitand with a constant operand. */
    2252             164 :         haslhs = haslhs && lhsmin == lhsmax && lhsmin >= 0;
    2253             164 :         hasrhs = hasrhs && rhsmin == rhsmax && rhsmin >= 0;
    2254                 : 
    2255             164 :         if (haslhs && hasrhs) {
    2256               0 :             *pmin = 0;
    2257               0 :             *pmax = Min(lhsmax, rhsmax);
    2258             164 :         } else if (haslhs) {
    2259              10 :             *pmin = 0;
    2260              10 :             *pmax = lhsmax;
    2261             154 :         } else if (hasrhs) {
    2262             122 :             *pmin = 0;
    2263             122 :             *pmax = rhsmax;
    2264                 :         } else {
    2265              32 :             return false;
    2266                 :         }
    2267             132 :         return true;
    2268                 :       }
    2269                 : 
    2270                 :       case JSOP_RSH: {
    2271                 :         int32_t rhsmin, rhsmax;
    2272             231 :         CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
    2273             231 :         if (!computeInterval(rhsv, &rhsmin, &rhsmax) || rhsmin != rhsmax)
    2274              17 :             return false;
    2275                 : 
    2276                 :         /* Only use the bottom 5 bits. */
    2277             214 :         int32_t shift = rhsmin & 0x1f;
    2278             214 :         *pmin = -(1 << (31 - shift));
    2279             214 :         *pmax = (1 << (31 - shift)) - 1;
    2280             214 :         return true;
    2281                 :       }
    2282                 : 
    2283                 :       case JSOP_URSH: {
    2284                 :         int32_t rhsmin, rhsmax;
    2285               0 :         CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
    2286               0 :         if (!computeInterval(rhsv, &rhsmin, &rhsmax) || rhsmin != rhsmax)
    2287               0 :             return false;
    2288                 : 
    2289                 :         /* Only use the bottom 5 bits. */
    2290               0 :         int32_t shift = rhsmin & 0x1f;
    2291               0 :         if (shift == 0)
    2292               0 :             return false;
    2293               0 :         *pmin = 0;
    2294               0 :         *pmax = (1 << (31 - shift)) - 1;
    2295               0 :         return true;
    2296                 :       }
    2297                 : 
    2298                 :       case JSOP_MOD: {
    2299                 :         int32_t rhsmin, rhsmax;
    2300               0 :         CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
    2301               0 :         if (!computeInterval(rhsv, &rhsmin, &rhsmax) || rhsmin != rhsmax)
    2302               0 :             return false;
    2303                 : 
    2304               0 :         int32_t rhs = abs(rhsmax);
    2305               0 :         *pmin = -(rhs - 1);
    2306               0 :         *pmax = rhs - 1;
    2307               0 :         return true;
    2308                 :       }
    2309                 : 
    2310                 :       case JSOP_ADD: {
    2311                 :         int32_t lhsmin, lhsmax, rhsmin, rhsmax;
    2312            2369 :         CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1));
    2313            2369 :         CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
    2314            2369 :         if (!computeInterval(lhsv, &lhsmin, &lhsmax) || !computeInterval(rhsv, &rhsmin, &rhsmax))
    2315            2223 :             return false;
    2316             146 :         return SafeAdd(lhsmin, rhsmin, pmin) && SafeAdd(lhsmax, rhsmax, pmax);
    2317                 :       }
    2318                 : 
    2319                 :       case JSOP_SUB: {
    2320                 :         int32_t lhsmin, lhsmax, rhsmin, rhsmax;
    2321             592 :         CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1));
    2322             592 :         CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
    2323             592 :         if (!computeInterval(lhsv, &lhsmin, &lhsmax) || !computeInterval(rhsv, &rhsmin, &rhsmax))
    2324             592 :             return false;
    2325               0 :         return SafeSub(lhsmin, rhsmax, pmin) && SafeSub(lhsmax, rhsmin, pmax);
    2326                 :       }
    2327                 : 
    2328                 :       case JSOP_MUL: {
    2329                 :         int32_t lhsmin, lhsmax, rhsmin, rhsmax;
    2330             647 :         CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1));
    2331             647 :         CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
    2332             647 :         if (!computeInterval(lhsv, &lhsmin, &lhsmax) || !computeInterval(rhsv, &rhsmin, &rhsmax))
    2333             607 :             return false;
    2334              40 :         int32_t nlhs = Max(abs(lhsmin), abs(lhsmax));
    2335              40 :         int32_t nrhs = Max(abs(rhsmin), abs(rhsmax));
    2336                 : 
    2337              40 :         if (!SafeMul(nlhs, nrhs, pmax))
    2338               8 :             return false;
    2339                 : 
    2340              32 :         if (lhsmin < 0 || rhsmin < 0) {
    2341                 :             /* pmax is nonnegative, so can be negated without overflow. */
    2342              16 :             *pmin = -*pmax;
    2343                 :         } else {
    2344              16 :             *pmin = 0;
    2345                 :         }
    2346                 : 
    2347              32 :         return true;
    2348                 :       }
    2349                 : 
    2350                 :       default:
    2351            3966 :         return false;
    2352                 :     }
    2353                 : }

Generated by: LCOV version 1.7