LCOV - code coverage report
Current view: directory - js/src/methodjit - LoopState.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1091 991 90.8 %
Date: 2012-04-07 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            3821 : SafeAdd(int32_t one, int32_t two, int32_t *res)
      53                 : {
      54            3821 :     *res = one + two;
      55            3821 :     int64_t ores = (int64_t)one + (int64_t)two;
      56            3821 :     if (ores == (int64_t)*res)
      57            3815 :         return true;
      58               6 :     JaegerSpew(JSpew_Analysis, "Overflow computing %d + %d\n", one, two);
      59               6 :     return false;
      60                 : }
      61                 : 
      62                 : inline bool
      63           17441 : SafeSub(int32_t one, int32_t two, int32_t *res)
      64                 : {
      65           17441 :     *res = one - two;
      66           17441 :     int64_t ores = (int64_t)one - (int64_t)two;
      67           17441 :     if (ores == (int64_t)*res)
      68           17441 :         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           33573 : LoopState::LoopState(JSContext *cx, analyze::CrossScriptSSA *ssa,
      85                 :                      mjit::Compiler *cc, FrameState *frame)
      86                 :     : cx(cx), ssa(ssa),
      87           67146 :       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          100719 :       constrainedLoop(true)
     101                 : {
     102           33573 :     JS_ASSERT(cx->typeInferenceEnabled());
     103           33573 : }
     104                 : 
     105                 : bool
     106           33573 : LoopState::init(jsbytecode *head, Jump entry, jsbytecode *entryTarget)
     107                 : {
     108           33573 :     this->lifetime = outerAnalysis->getLoop(head);
     109               0 :     JS_ASSERT(lifetime &&
     110                 :               lifetime->head == uint32_t(head - outerScript->code) &&
     111           33573 :               lifetime->entry == uint32_t(entryTarget - outerScript->code));
     112                 : 
     113           33573 :     this->entry = entry;
     114                 : 
     115           33573 :     analyzeLoopTest();
     116           33573 :     analyzeLoopIncrements();
     117           70567 :     for (unsigned i = 0; i < ssa->numFrames(); i++) {
     118                 :         /* Only analyze this frame if it is nested within the loop itself. */
     119           36994 :         uint32_t index = ssa->iterFrame(i).index;
     120           36994 :         if (index != CrossScriptSSA::OUTER_FRAME) {
     121            3421 :             unsigned pframe = index;
     122            7976 :             while (ssa->getFrame(pframe).parent != CrossScriptSSA::OUTER_FRAME)
     123            1134 :                 pframe = ssa->getFrame(pframe).parent;
     124            3421 :             uint32_t offset = ssa->getFrame(pframe).parentpc - outerScript->code;
     125            3421 :             JS_ASSERT(offset < outerScript->length);
     126            3421 :             if (offset < lifetime->head || offset > lifetime->backedge)
     127            1821 :                 continue;
     128                 :         }
     129           35173 :         analyzeLoopBody(index);
     130                 :     }
     131                 : 
     132           33573 :     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           11157 :                    testConstant);
     138                 :     }
     139                 : 
     140           43467 :     for (unsigned i = 0; i < increments.length(); i++) {
     141                 :         JaegerSpew(JSpew_Analysis, "loop increment at %u for %s: %u\n", lifetime->head,
     142            9894 :                    frame.entryName(increments[i].slot),
     143           19788 :                    increments[i].offset);
     144                 :     }
     145                 : 
     146           34555 :     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           38136 :     for (unsigned i = 0; i < modifiedProperties.length(); i++) {
     152                 :         JaegerSpew(JSpew_Analysis, "loop modified property at %u: %s %s\n", lifetime->head,
     153            4563 :                    types::TypeString(types::Type::ObjectType(modifiedProperties[i].object)),
     154            9126 :                    TypeIdString(modifiedProperties[i].id));
     155                 :     }
     156                 : 
     157           33573 :     RegisterAllocation *&alloc = outerAnalysis->getAllocation(head);
     158           33573 :     JS_ASSERT(!alloc);
     159                 : 
     160           33573 :     alloc = cx->typeLifoAlloc().new_<RegisterAllocation>(true);
     161           33573 :     if (!alloc) {
     162               0 :         js_ReportOutOfMemory(cx);
     163               0 :         return false;
     164                 :     }
     165                 : 
     166           33573 :     this->alloc = alloc;
     167           33573 :     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           33573 :     if (outerScript->function()) {
     174           27017 :         if (TypeSet::HasObjectFlags(cx, outerScript->function()->getType(cx), OBJECT_FLAG_UNINLINEABLE))
     175            8589 :             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           33573 :     if (lifetime->hasSafePoints)
     185             281 :         this->skipAnalysis = true;
     186                 : 
     187           33573 :     return true;
     188                 : }
     189                 : 
     190                 : void
     191          446367 : LoopState::addJoin(unsigned index, bool script)
     192                 : {
     193                 :     StubJoin r;
     194          446367 :     r.index = index;
     195          446367 :     r.script = script;
     196          446367 :     loopJoins.append(r);
     197          446367 : }
     198                 : 
     199                 : void
     200           31932 : LoopState::addInvariantCall(Jump jump, Label label, bool ool, bool entry, unsigned patchIndex, Uses uses)
     201                 : {
     202           31932 :     RestoreInvariantCall call;
     203           31932 :     call.jump = jump;
     204           31932 :     call.label = label;
     205           31932 :     call.ool = ool;
     206           31932 :     call.entry = entry;
     207           31932 :     call.patchIndex = patchIndex;
     208           31932 :     call.temporaryCopies = frame.getTemporaryCopies(uses);
     209                 : 
     210           31932 :     restoreInvariantCalls.append(call);
     211           31932 : }
     212                 : 
     213                 : void
     214           33560 : LoopState::flushLoop(StubCompiler &stubcc)
     215                 : {
     216           33560 :     clearLoopRegisters();
     217                 : 
     218                 :     /*
     219                 :      * Patch stub compiler rejoins with loads of loop carried registers
     220                 :      * discovered after the fact.
     221                 :      */
     222           67391 :     for (unsigned i = 0; i < loopPatches.length(); i++) {
     223           33831 :         const StubJoinPatch &p = loopPatches[i];
     224           33831 :         stubcc.patchJoin(p.join.index, p.join.script, p.address, p.reg);
     225                 :     }
     226           33560 :     loopJoins.clear();
     227           33560 :     loopPatches.clear();
     228                 : 
     229           33560 :     if (hasInvariants()) {
     230           10860 :         for (unsigned i = 0; i < restoreInvariantCalls.length(); i++) {
     231            8740 :             RestoreInvariantCall &call = restoreInvariantCalls[i];
     232            8740 :             Assembler &masm = cc.getAssembler(true);
     233           17480 :             Vector<Jump> failureJumps(cx);
     234                 : 
     235            8740 :             jsbytecode *pc = cc.getInvariantPC(call.patchIndex);
     236                 : 
     237            8740 :             if (call.ool) {
     238            8531 :                 call.jump.linkTo(masm.label(), &masm);
     239            8531 :                 restoreInvariants(pc, masm, call.temporaryCopies, &failureJumps);
     240            8531 :                 masm.jump().linkTo(call.label, &masm);
     241                 :             } else {
     242             209 :                 stubcc.linkExitDirect(call.jump, masm.label());
     243             209 :                 restoreInvariants(pc, masm, call.temporaryCopies, &failureJumps);
     244             209 :                 stubcc.crossJump(masm.jump(), call.label);
     245                 :             }
     246                 : 
     247            8740 :             if (!failureJumps.empty()) {
     248           45827 :                 for (unsigned i = 0; i < failureJumps.length(); i++)
     249           37476 :                     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            8351 :                 InvariantCodePatch *patch = cc.getInvariantPatch(call.patchIndex);
     256            8351 :                 patch->hasPatch = true;
     257                 :                 patch->codePatch = masm.storePtrWithPatch(ImmPtr(NULL),
     258            8351 :                                                           FrameAddress(offsetof(VMFrame, scratch)));
     259                 :                 JS_STATIC_ASSERT(Registers::ReturnReg != Registers::ArgReg1);
     260            8351 :                 masm.move(Registers::ReturnReg, Registers::ArgReg1);
     261                 : 
     262            8351 :                 if (call.entry) {
     263                 :                     masm.fallibleVMCall(true, JS_FUNC_TO_DATA_PTR(void *, stubs::InvariantFailure),
     264            1146 :                                         pc, NULL, 0);
     265                 :                 } else {
     266                 :                     /* f.regs are already coherent, don't write new values to them. */
     267            7205 :                     masm.infallibleVMCall(JS_FUNC_TO_DATA_PTR(void *, stubs::InvariantFailure), -1);
     268                 :                 }
     269                 :             }
     270                 :         }
     271                 :     } else {
     272           54630 :         for (unsigned i = 0; i < restoreInvariantCalls.length(); i++) {
     273           23190 :             RestoreInvariantCall &call = restoreInvariantCalls[i];
     274           23190 :             Assembler &masm = cc.getAssembler(call.ool);
     275           23190 :             call.jump.linkTo(call.label, &masm);
     276                 :         }
     277                 :     }
     278           33560 :     restoreInvariantCalls.clear();
     279           33560 : }
     280                 : 
     281                 : void
     282          154366 : LoopState::clearLoopRegisters()
     283                 : {
     284          154366 :     alloc->clearLoops();
     285          154366 :     loopRegs = 0;
     286          154366 : }
     287                 : 
     288                 : bool
     289           15731 : LoopState::loopInvariantEntry(uint32_t slot)
     290                 : {
     291           15731 :     if (slot == UNASSIGNED)
     292            6301 :         return true;
     293                 : 
     294                 :     /* Watch for loop temporaries. :XXX: this is really gross. */
     295            9430 :     if (slot >= analyze::LocalSlot(outerScript, outerScript->nslots))
     296            1779 :         return true;
     297                 : 
     298            7651 :     if (slot == analyze::CalleeSlot() || outerAnalysis->slotEscapes(slot))
     299               0 :         return false;
     300            7651 :     return outerAnalysis->liveness(slot).firstWrite(lifetime) == UINT32_MAX;
     301                 : }
     302                 : 
     303                 : inline bool
     304            3966 : LoopState::entryRedundant(const InvariantEntry &e0, const InvariantEntry &e1)
     305                 : {
     306            3966 :     JS_ASSERT(e0.isCheck() && e1.isCheck());
     307                 : 
     308            3966 :     uint32_t array0 = e0.u.check.arraySlot;
     309            3966 :     uint32_t array1 = e1.u.check.arraySlot;
     310                 : 
     311            3966 :     uint32_t value01 = e0.u.check.valueSlot1;
     312            3966 :     uint32_t value02 = e0.u.check.valueSlot2;
     313                 : 
     314            3966 :     uint32_t value11 = e1.u.check.valueSlot1;
     315            3966 :     uint32_t value12 = e1.u.check.valueSlot2;
     316                 : 
     317            3966 :     int32_t c0 = e0.u.check.constant;
     318            3966 :     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            3966 :     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            3954 :     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            2859 :     return false;
     351                 : }
     352                 : 
     353                 : bool
     354            2025 : 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            2025 :     JS_ASSERT(entry.isCheck());
     361                 : 
     362                 :     /* Maintain this separately, GCC miscompiles if the loop test is invariantEntries.length(). */
     363            2025 :     unsigned length = invariantEntries.length();
     364                 : 
     365            4954 :     for (unsigned i = 0; i < length; i++) {
     366            3645 :         InvariantEntry &baseEntry = invariantEntries[i];
     367            3645 :         if (!baseEntry.isCheck())
     368            1304 :             continue;
     369            2341 :         if (entryRedundant(entry, baseEntry))
     370             716 :             return true;
     371            1625 :         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            1309 :     return false;
     387                 : }
     388                 : 
     389                 : bool
     390            1160 : LoopState::addHoistedCheck(InvariantArrayKind arrayKind, uint32_t arraySlot,
     391                 :                            uint32_t valueSlot1, uint32_t valueSlot2, int32_t constant)
     392                 : {
     393                 : #ifdef DEBUG
     394            1160 :     JS_ASSERT_IF(valueSlot1 == UNASSIGNED, valueSlot2 == UNASSIGNED);
     395            1160 :     const char *field = (arrayKind == DENSE_ARRAY) ? "initlen" : "length";
     396            1160 :     if (valueSlot1 == UNASSIGNED) {
     397             468 :         JaegerSpew(JSpew_Analysis, "Hoist %s > %d\n", field, constant);
     398             692 :     } else if (valueSlot2 == UNASSIGNED) {
     399                 :         JaegerSpew(JSpew_Analysis, "Hoisted as %s > %s + %d\n", field,
     400             675 :                    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            1160 :     InvariantEntry entry;
     408                 :     entry.kind = (arrayKind == DENSE_ARRAY)
     409                 :                  ? InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK
     410            1160 :                  : InvariantEntry::TYPED_ARRAY_BOUNDS_CHECK;
     411            1160 :     entry.u.check.arraySlot = arraySlot;
     412            1160 :     entry.u.check.valueSlot1 = valueSlot1;
     413            1160 :     entry.u.check.valueSlot2 = valueSlot2;
     414            1160 :     entry.u.check.constant = constant;
     415                 : 
     416            1160 :     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             806 :     bool hasInvariantSlots = false;
     426                 :     InvariantEntry::EntryKind slotsKind = (arrayKind == DENSE_ARRAY)
     427                 :                                           ? InvariantEntry::DENSE_ARRAY_SLOTS
     428             806 :                                           : InvariantEntry::TYPED_ARRAY_SLOTS;
     429            2483 :     for (unsigned i = 0; !hasInvariantSlots && i < invariantEntries.length(); i++) {
     430            1677 :         InvariantEntry &entry = invariantEntries[i];
     431            1677 :         if (entry.kind == slotsKind && entry.u.array.arraySlot == arraySlot)
     432             191 :             hasInvariantSlots = true;
     433                 :     }
     434             806 :     if (!hasInvariantSlots) {
     435             615 :         uint32_t which = frame.allocTemporary();
     436             615 :         if (which == UINT32_MAX)
     437               0 :             return false;
     438             615 :         FrameEntry *fe = frame.getTemporary(which);
     439                 : 
     440                 :         JaegerSpew(JSpew_Analysis, "Using %s for loop invariant slots of %s\n",
     441             615 :                    frame.entryName(fe), frame.entryName(arraySlot));
     442                 : 
     443             615 :         InvariantEntry slotsEntry;
     444             615 :         slotsEntry.kind = slotsKind;
     445             615 :         slotsEntry.u.array.arraySlot = arraySlot;
     446             615 :         slotsEntry.u.array.temporary = which;
     447             615 :         invariantEntries.append(slotsEntry);
     448                 :     }
     449                 : 
     450             806 :     invariantEntries.append(entry);
     451             806 :     return true;
     452                 : }
     453                 : 
     454                 : void
     455             844 : LoopState::addNegativeCheck(uint32_t valueSlot, int32_t constant)
     456                 : {
     457                 :     JaegerSpew(JSpew_Analysis, "Nonnegative check %s + %d >= 0\n",
     458             844 :                frame.entryName(valueSlot), constant);
     459                 : 
     460             844 :     InvariantEntry entry;
     461             844 :     entry.kind = InvariantEntry::NEGATIVE_CHECK;
     462             844 :     entry.u.check.valueSlot1 = valueSlot;
     463             844 :     entry.u.check.constant = constant;
     464                 : 
     465             844 :     if (!checkRedundantEntry(entry))
     466             489 :         invariantEntries.append(entry);
     467             844 : }
     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           16187 : LoopState::setLoopReg(AnyRegisterID reg, FrameEntry *fe)
     488                 : {
     489           16187 :     JS_ASSERT(alloc->loop(reg));
     490           16187 :     loopRegs.takeReg(reg);
     491                 : 
     492           16187 :     uint32_t slot = frame.outerSlot(fe);
     493                 :     JaegerSpew(JSpew_Regalloc, "allocating loop register %s for %s\n",
     494           16187 :                reg.name(), frame.entryName(fe));
     495                 : 
     496           16187 :     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           50018 :     for (unsigned i = 0; i < loopJoins.length(); i++) {
     503           33831 :         StubJoinPatch p;
     504           33831 :         p.join = loopJoins[i];
     505           33831 :         p.address = frame.addressOf(fe);
     506           33831 :         p.reg = reg;
     507           33831 :         loopPatches.append(p);
     508                 :     }
     509                 : 
     510           16187 :     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           16187 : }
     521                 : 
     522                 : bool
     523           15150 : 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           15150 :     if (skipAnalysis)
     531           12734 :         return false;
     532                 : 
     533                 :     uint32_t objSlot;
     534                 :     int32_t objConstant;
     535            2416 :     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            1954 :                frame.entryName(objSlot));
     540                 : 
     541            1954 :     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            1914 :     TypeSet *objTypes = ssa->getValueTypes(obj);
     553            1914 :     if (arrayKind == DENSE_ARRAY && !growArrays.empty()) {
     554             578 :         unsigned count = objTypes->getObjectCount();
     555            1301 :         for (unsigned i = 0; i < count; i++) {
     556             950 :             if (objTypes->getSingleObject(i) != NULL) {
     557               0 :                 JaegerSpew(JSpew_Analysis, "Object might be a singleton");
     558               0 :                 return false;
     559                 :             }
     560             950 :             TypeObject *object = objTypes->getTypeObject(i);
     561             950 :             if (object && hasGrowArray(object)) {
     562             227 :                 JaegerSpew(JSpew_Analysis, "Object might grow inside loop\n");
     563             227 :                 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            1687 :     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            1438 :     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            1120 :     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             704 :     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             649 :     if (indexSlot == testLHS && testLessEqual) {
     614                 :         int32_t constant;
     615             409 :         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             409 :         addNegativeCheck(indexSlot, indexConstant);
     626                 : 
     627             409 :         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             240 :     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             223 :     JaegerSpew(JSpew_Analysis, "No match found\n");
     651             223 :     return false;
     652                 : }
     653                 : 
     654                 : bool
     655              62 : LoopState::hoistArgsLengthCheck(const CrossSSAValue &index)
     656                 : {
     657              62 :     if (skipAnalysis)
     658              24 :         return false;
     659                 : 
     660              38 :     JaegerSpew(JSpew_Analysis, "Trying to hoist argument range check\n");
     661                 : 
     662                 :     uint32_t indexSlot;
     663                 :     int32_t indexConstant;
     664              38 :     if (!getEntryValue(index, &indexSlot, &indexConstant)) {
     665               4 :         JaegerSpew(JSpew_Analysis, "Could not compute index in terms of loop entry state\n");
     666               4 :         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               4 :         JaegerSpew(JSpew_Analysis, "Index is constant or loop invariant\n");
     676               4 :         return false;
     677                 :     }
     678                 : 
     679              30 :     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              27 :     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              25 :     JaegerSpew(JSpew_Analysis, "No match found\n");
     703              25 :     return false;
     704                 : }
     705                 : 
     706                 : bool
     707             830 : 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             830 :     if (testLHS == UNASSIGNED || testRHS != UNASSIGNED || testLessEqual)
     716             795 :         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            1160 : LoopState::invariantArraySlots(const CrossSSAValue &obj)
     746                 : {
     747            1160 :     JS_ASSERT(!skipAnalysis);
     748                 : 
     749                 :     uint32_t objSlot;
     750                 :     int32_t objConstant;
     751            1160 :     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            3104 :     for (unsigned i = 0; i < invariantEntries.length(); i++) {
     762            3104 :         InvariantEntry &entry = invariantEntries[i];
     763            3104 :         if ((entry.kind == InvariantEntry::DENSE_ARRAY_SLOTS ||
     764                 :              entry.kind == InvariantEntry::TYPED_ARRAY_SLOTS) &&
     765                 :             entry.u.array.arraySlot == objSlot) {
     766            1160 :             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              99 : LoopState::invariantArguments()
     777                 : {
     778              99 :     if (skipAnalysis)
     779              60 :         return NULL;
     780                 : 
     781              43 :     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              39 :     uint32_t which = frame.allocTemporary();
     788              39 :     if (which == UINT32_MAX)
     789               0 :         return NULL;
     790              39 :     FrameEntry *fe = frame.getTemporary(which);
     791                 : 
     792              39 :     InvariantEntry entry;
     793              39 :     entry.kind = InvariantEntry::INVARIANT_ARGS_BASE;
     794              39 :     entry.u.array.temporary = which;
     795              39 :     invariantEntries.append(entry);
     796                 : 
     797                 :     JaegerSpew(JSpew_Analysis, "Using %s for loop invariant args base\n",
     798              39 :                frame.entryName(fe));
     799              39 :     return fe;
     800                 : }
     801                 : 
     802                 : FrameEntry *
     803            2181 : LoopState::invariantLength(const CrossSSAValue &obj)
     804                 : {
     805            2181 :     if (skipAnalysis)
     806               0 :         return NULL;
     807                 : 
     808                 :     uint32_t objSlot;
     809                 :     int32_t objConstant;
     810            2181 :     if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0)
     811             177 :         return NULL;
     812            2004 :     TypeSet *objTypes = ssa->getValueTypes(obj);
     813                 : 
     814                 :     /* Check for 'length' on the lazy arguments for the current frame. */
     815            2004 :     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            2099 :     for (unsigned i = 0; i < invariantEntries.length(); i++) {
     845             266 :         InvariantEntry &entry = invariantEntries[i];
     846             266 :         if ((entry.kind == InvariantEntry::DENSE_ARRAY_LENGTH ||
     847                 :              entry.kind == InvariantEntry::TYPED_ARRAY_LENGTH) &&
     848                 :             entry.u.array.arraySlot == objSlot) {
     849             169 :             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            1291 : LoopState::invariantProperty(const CrossSSAValue &obj, jsid id)
     916                 : {
     917            1291 :     if (skipAnalysis)
     918               0 :         return NULL;
     919                 : 
     920            1291 :     if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))
     921             210 :         return NULL;
     922                 : 
     923                 :     uint32_t objSlot;
     924                 :     int32_t objConstant;
     925            1081 :     if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0)
     926             461 :         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           43350 : LoopState::cannotIntegerOverflow(const CrossSSAValue &pushed)
     975                 : {
     976           43350 :     if (skipAnalysis)
     977           38371 :         return false;
     978                 : 
     979                 :     int32_t min, max;
     980            4979 :     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            4863 :     JS_ASSERT(pushed.v.kind() == SSAValue::PUSHED);
     991            4863 :     jsbytecode *PC = ssa->getFrame(pushed.frame).script->code + pushed.v.pushedOffset();
     992            4863 :     ScriptAnalysis *analysis = ssa->getFrame(pushed.frame).script->analysis();
     993                 : 
     994            4863 :     if (!analysis->integerOperation(cx, PC))
     995            1508 :         return false;
     996                 : 
     997            3355 :     uint32_t baseSlot = UNASSIGNED;
     998            3355 :     int32_t baseConstant = 0;
     999            3355 :     JSOp op = JSOp(*PC);
    1000            3355 :     switch (op) {
    1001                 : 
    1002                 :       case JSOP_INCLOCAL:
    1003                 :       case JSOP_LOCALINC:
    1004                 :       case JSOP_INCARG:
    1005                 :       case JSOP_ARGINC: {
    1006            1551 :         CrossSSAValue cv(pushed.frame, analysis->poppedValue(PC, 0));
    1007            1551 :         if (!getEntryValue(cv, &baseSlot, &baseConstant))
    1008              11 :             return false;
    1009            1540 :         if (!SafeAdd(baseConstant, 1, &baseConstant))
    1010               0 :             return false;
    1011            1540 :         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            1380 :         uint32_t lhs = UNASSIGNED, rhs = UNASSIGNED;
    1029            1380 :         int32_t lhsconstant = 0, rhsconstant = 0;
    1030            1380 :         CrossSSAValue lcv(pushed.frame, analysis->poppedValue(PC, 1));
    1031            1380 :         CrossSSAValue rcv(pushed.frame, analysis->poppedValue(PC, 0));
    1032            1380 :         if (!getEntryValue(lcv, &lhs, &lhsconstant))
    1033             891 :             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            1974 :     if (baseSlot == UNASSIGNED)
    1057               0 :         return false;
    1058                 : 
    1059                 :     JaegerSpew(JSpew_Analysis, "Trying to hoist integer overflow check on %s + %d\n",
    1060            1974 :                frame.entryName(baseSlot), baseConstant);
    1061                 : 
    1062            1974 :     if (baseConstant == 0) {
    1063               6 :         JaegerSpew(JSpew_Analysis, "Vacuously holds\n");
    1064               6 :         return true;
    1065                 :     }
    1066                 : 
    1067            1968 :     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            1699 :     if (baseSlot == testLHS && testLessEqual) {
    1098                 :         int32_t constant;
    1099            1109 :         if (!SafeAdd(testConstant, baseConstant, &constant))
    1100               2 :             return false;
    1101                 : 
    1102            1107 :         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            1099 :             JaegerSpew(JSpew_Analysis, "Loop test comparison must hold\n");
    1108            1099 :             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             590 :     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             577 :     JaegerSpew(JSpew_Analysis, "No match found\n");
    1141             577 :     return false;
    1142                 : }
    1143                 : 
    1144                 : bool
    1145           43350 : LoopState::ignoreIntegerOverflow(const CrossSSAValue &pushed)
    1146                 : {
    1147           43350 :     if (skipAnalysis || unknownModset || !constrainedLoop)
    1148           42899 :         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            8740 : 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            8740 :     Registers regs(Registers::TempRegs);
    1309            8740 :     regs.takeReg(Registers::ReturnReg);
    1310            8740 :     if (regs.hasReg(JSReturnReg_Data))
    1311               0 :         regs.takeReg(JSReturnReg_Data);
    1312            8740 :     if (regs.hasReg(JSReturnReg_Type))
    1313               0 :         regs.takeReg(JSReturnReg_Type);
    1314                 : 
    1315            8740 :     RegisterID T0 = regs.takeAnyReg().reg();
    1316            8740 :     RegisterID T1 = regs.takeAnyReg().reg();
    1317                 : 
    1318           42818 :     for (unsigned i = 0; i < invariantEntries.length(); i++) {
    1319           34078 :         const InvariantEntry &entry = invariantEntries[i];
    1320           34078 :         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           11482 :             masm.loadPayload(frame.addressOf(entry.u.check.arraySlot), T0);
    1329           11482 :             if (entry.kind == InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK) {
    1330           11397 :                 masm.loadPtr(Address(T0, JSObject::offsetOfElements()), T0);
    1331           11397 :                 masm.load32(Address(T0, ObjectElements::offsetOfInitializedLength()), T0);
    1332                 :             } else {
    1333              85 :                 masm.loadPayload(Address(T0, TypedArray::lengthOffset()), T0);
    1334                 :             }
    1335                 : 
    1336           11482 :             int32_t constant = entry.u.check.constant;
    1337                 : 
    1338           11482 :             if (entry.u.check.valueSlot1 != UINT32_MAX) {
    1339            7706 :                 constant += adjustConstantForIncrement(pc, entry.u.check.valueSlot1);
    1340            7706 :                 masm.loadPayload(frame.addressOf(entry.u.check.valueSlot1), T1);
    1341            7706 :                 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            7706 :                 if (constant != 0) {
    1348                 :                     Jump overflow = masm.branchAdd32(Assembler::Overflow,
    1349            3291 :                                                      Imm32(constant), T1);
    1350            3291 :                     jumps->append(overflow);
    1351                 :                 }
    1352            7706 :                 Jump j = masm.branch32(Assembler::LessThanOrEqual, T0, T1);
    1353            7706 :                 jumps->append(j);
    1354                 :             } else {
    1355                 :                 Jump j = masm.branch32(Assembler::LessThanOrEqual, T0,
    1356            3776 :                                        Imm32(constant));
    1357            3776 :                 jumps->append(j);
    1358                 :             }
    1359           11482 :             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            7182 :             masm.loadPayload(frame.addressOf(entry.u.check.valueSlot1), T0);
    1384            7182 :             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            7182 :             Jump j = masm.branch32(Assembler::LessThan, T0, Imm32(0));
    1390            7182 :             jumps->append(j);
    1391            7182 :             break;
    1392                 :           }
    1393                 : 
    1394                 :           case InvariantEntry::DENSE_ARRAY_SLOTS:
    1395                 :           case InvariantEntry::DENSE_ARRAY_LENGTH: {
    1396           13196 :             uint32_t array = entry.u.array.arraySlot;
    1397           13196 :             Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(array));
    1398           13196 :             jumps->append(notObject);
    1399           13196 :             masm.loadPayload(frame.addressOf(array), T0);
    1400           13196 :             masm.loadPtr(Address(T0, JSObject::offsetOfElements()), T0);
    1401                 : 
    1402           13196 :             Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
    1403                 : 
    1404           13196 :             if (entry.kind == InvariantEntry::DENSE_ARRAY_LENGTH) {
    1405            1799 :                 masm.load32(Address(T0, ObjectElements::offsetOfLength()), T0);
    1406            1799 :                 masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), T0, address);
    1407                 :             } else {
    1408           11397 :                 masm.storePayload(T0, address);
    1409                 :             }
    1410           13196 :             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             409 :             Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
    1434             409 :             masm.loadFrameActuals(outerScript->function(), T0);
    1435             409 :             masm.storePayload(T0, address);
    1436             409 :             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            8850 :     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            8740 :     if (temporaryCopies)
    1477              90 :         cx->delete_(temporaryCopies);
    1478            8740 : }
    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           23116 : LoopState::getLoopTestAccess(const SSAValue &v, uint32_t *pslot, int32_t *pconstant)
    1488                 : {
    1489           23116 :     *pslot = UNASSIGNED;
    1490           23116 :     *pconstant = 0;
    1491                 : 
    1492           23116 :     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            9969 :         if (v.kind() == SSAValue::PHI) {
    1500            9187 :             slot = v.phiSlot();
    1501            9187 :             offset = v.phiOffset();
    1502                 :         } else {
    1503             782 :             slot = v.varSlot();
    1504             782 :             offset = v.varInitial() ? 0 : v.varOffset();
    1505                 :         }
    1506            9969 :         if (outerAnalysis->slotEscapes(slot))
    1507               0 :             return false;
    1508            9969 :         if (outerAnalysis->liveness(slot).firstWrite(offset + 1, lifetime->backedge) != UINT32_MAX)
    1509               0 :             return false;
    1510            9969 :         *pslot = slot;
    1511            9969 :         *pconstant = 0;
    1512            9969 :         return true;
    1513                 :     }
    1514                 : 
    1515           13147 :     jsbytecode *pc = outerScript->code + v.pushedOffset();
    1516                 : 
    1517           13147 :     JSOp op = JSOp(*pc);
    1518           13147 :     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           13147 :     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            8153 :         *pconstant = GetBytecodeInteger(pc);
    1562            8153 :         return true;
    1563                 : 
    1564                 :       default:
    1565            4887 :         return false;
    1566                 :     }
    1567                 : }
    1568                 : 
    1569                 : void
    1570           33573 : LoopState::analyzeLoopTest()
    1571                 : {
    1572           33573 :     if (cc.debugMode())
    1573           15533 :         return;
    1574                 : 
    1575                 :     /* Don't handle do-while loops. */
    1576           18040 :     if (lifetime->entry == lifetime->head)
    1577              33 :         return;
    1578                 : 
    1579                 :     /* Don't handle loops with branching inside their condition. */
    1580           18007 :     if (lifetime->entry < lifetime->lastBlock)
    1581              54 :         return;
    1582                 : 
    1583                 :     /* Get the test performed before branching. */
    1584           17953 :     jsbytecode *backedge = outerScript->code + lifetime->backedge;
    1585           17953 :     if (JSOp(*backedge) != JSOP_IFNE)
    1586               0 :         return;
    1587           17953 :     const SSAValue &test = outerAnalysis->poppedValue(backedge, 0);
    1588           17953 :     if (test.kind() != SSAValue::PUSHED)
    1589             129 :         return;
    1590           17824 :     JSOp cmpop = JSOp(outerScript->code[test.pushedOffset()]);
    1591           17824 :     switch (cmpop) {
    1592                 :       case JSOP_GT:
    1593                 :       case JSOP_GE:
    1594                 :       case JSOP_LT:
    1595                 :       case JSOP_LE:
    1596                 :         break;
    1597                 :       default:
    1598            2337 :         return;
    1599                 :     }
    1600                 : 
    1601           15487 :     SSAValue one = outerAnalysis->poppedValue(test.pushedOffset(), 1);
    1602           15487 :     SSAValue two = outerAnalysis->poppedValue(test.pushedOffset(), 0);
    1603                 : 
    1604                 :     /* The test must be comparing known integers. */
    1605           27852 :     if (outerAnalysis->getValueTypes(one)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32 ||
    1606           12365 :         outerAnalysis->getValueTypes(two)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) {
    1607            3929 :         return;
    1608                 :     }
    1609                 : 
    1610                 :     /* Reverse the condition if the RHS is modified by the loop. */
    1611                 :     uint32_t swapRHS;
    1612                 :     int32_t swapConstant;
    1613           11558 :     if (getLoopTestAccess(two, &swapRHS, &swapConstant)) {
    1614            8975 :         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           11558 :     if (!getLoopTestAccess(one, &lhs, &lhsConstant))
    1625            2304 :         return;
    1626                 : 
    1627            9254 :     uint32_t rhs = UNASSIGNED;
    1628            9254 :     int32_t rhsConstant = 0;
    1629            9254 :     CrossSSAValue rhsv(CrossScriptSSA::OUTER_FRAME, two);
    1630            9254 :     if (!getEntryValue(rhsv, &rhs, &rhsConstant))
    1631             525 :         return;
    1632            8729 :     if (!loopInvariantEntry(rhs))
    1633               0 :         return;
    1634                 : 
    1635            8729 :     if (lhs == UNASSIGNED)
    1636               0 :         return;
    1637                 : 
    1638                 :     int32_t constant;
    1639            8729 :     if (!SafeSub(rhsConstant, lhsConstant, &constant))
    1640               0 :         return;
    1641                 : 
    1642                 :     /* x > y ==> x >= y + 1 */
    1643            8729 :     if (cmpop == JSOP_GT && !SafeAdd(constant, 1, &constant))
    1644               0 :         return;
    1645                 : 
    1646                 :     /* x < y ==> x <= y - 1 */
    1647            8729 :     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            8729 :     this->testLHS = lhs;
    1653            8729 :     this->testRHS = rhs;
    1654            8729 :     this->testConstant = constant;
    1655            8729 :     this->testLessEqual = (cmpop == JSOP_LT || cmpop == JSOP_LE);
    1656                 : }
    1657                 : 
    1658                 : void
    1659           33573 : LoopState::analyzeLoopIncrements()
    1660                 : {
    1661           33573 :     if (cc.debugMode())
    1662           15533 :         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           82557 :     for (uint32_t slot = ArgSlot(0); slot < LocalSlot(outerScript, outerScript->nfixed); slot++) {
    1671           64517 :         if (outerAnalysis->slotEscapes(slot))
    1672            8274 :             continue;
    1673                 : 
    1674           56243 :         uint32_t offset = outerAnalysis->liveness(slot).onlyWrite(lifetime);
    1675           56243 :         if (offset == UINT32_MAX || offset < lifetime->lastBlock)
    1676           43710 :             continue;
    1677                 : 
    1678           12533 :         jsbytecode *pc = outerScript->code + offset;
    1679           12533 :         JSOp op = JSOp(*pc);
    1680           12533 :         const JSCodeSpec *cs = &js_CodeSpec[op];
    1681           12533 :         if (cs->format & (JOF_INC | JOF_DEC)) {
    1682            9941 :             if (!outerAnalysis->integerOperation(cx, pc))
    1683              47 :                 continue;
    1684                 : 
    1685                 :             Increment inc;
    1686            9894 :             inc.slot = slot;
    1687            9894 :             inc.offset = offset;
    1688            9894 :             increments.append(inc);
    1689                 :         }
    1690                 :     }
    1691                 : }
    1692                 : 
    1693                 : bool
    1694            2212 : 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            2212 :     TypeSet *objTypes = outerAnalysis->getValueTypes(obj);
    1710            2212 :     TypeSet *elemTypes = outerAnalysis->getValueTypes(index);
    1711                 : 
    1712            4192 :     if (objTypes->getKnownTypeTag(cx) != JSVAL_TYPE_OBJECT ||
    1713            1980 :         elemTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) {
    1714             276 :         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           35173 : LoopState::analyzeLoopBody(unsigned frame)
    1763                 : {
    1764           35173 :     if (cc.debugMode()) {
    1765           15533 :         skipAnalysis = true;
    1766           15533 :         return;
    1767                 :     }
    1768                 : 
    1769           19640 :     JSScript *script = ssa->getFrame(frame).script;
    1770           19640 :     analyze::ScriptAnalysis *analysis = script->analysis();
    1771           19640 :     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           19640 :                     ssa->getFrame(frame).depth + VALUES_PER_STACK_FRAME * 2 + script->nslots);
    1782                 : 
    1783           19640 :     if (script->failedBoundsCheck || analysis->localsAliasStack())
    1784            3106 :         skipAnalysis = true;
    1785                 : 
    1786                 :     /* Analyze the entire script for frames inlined in the loop body. */
    1787           19640 :     unsigned start = (frame == CrossScriptSSA::OUTER_FRAME) ? lifetime->head + JSOP_LOOPHEAD_LENGTH : 0;
    1788           19640 :     unsigned end = (frame == CrossScriptSSA::OUTER_FRAME) ? lifetime->backedge : script->length;
    1789                 : 
    1790           19640 :     unsigned offset = start;
    1791          659497 :     while (offset < end) {
    1792          620217 :         jsbytecode *pc = script->code + offset;
    1793          620217 :         uint32_t successorOffset = offset + GetBytecodeLength(pc);
    1794                 : 
    1795          620217 :         analyze::Bytecode *opinfo = analysis->maybeCode(offset);
    1796          620217 :         if (!opinfo) {
    1797            1597 :             offset = successorOffset;
    1798            1597 :             continue;
    1799                 :         }
    1800                 : 
    1801          618620 :         JSOp op = JSOp(*pc);
    1802                 : 
    1803                 :         /* Don't do any hoisting for outer loops in case of nesting. */
    1804          618620 :         if (op == JSOP_LOOPHEAD)
    1805            2746 :             skipAnalysis = true;
    1806                 : 
    1807          618620 :         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           34407 :             bool foundInline = false;
    1815           92155 :             for (unsigned i = 0; !foundInline && i < ssa->numFrames(); i++) {
    1816           57748 :                 if (ssa->iterFrame(i).parent == frame && ssa->iterFrame(i).parentpc == pc)
    1817            1478 :                     foundInline = true;
    1818                 :             }
    1819           34407 :             if (!foundInline)
    1820           32929 :                 skipAnalysis = true;
    1821           34407 :             break;
    1822                 :           }
    1823                 : 
    1824                 :           case JSOP_EVAL:
    1825                 :           case JSOP_FUNCALL:
    1826                 :           case JSOP_FUNAPPLY:
    1827                 :           case JSOP_NEW:
    1828            3154 :             skipAnalysis = true;
    1829            3154 :             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              27 :                     continue;
    1852            5091 :                 if (!addModifiedProperty(object, JSID_VOID))
    1853               0 :                     return;
    1854            5091 :                 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           17490 :             SSAValue objValue = analysis->poppedValue(pc, 1);
    1865           17490 :             SSAValue elemValue = analysis->poppedValue(pc, 0);
    1866                 : 
    1867           17490 :             if (constrainedLoop && !definiteArrayAccess(objValue, elemValue))
    1868             559 :                 constrainedLoop = false;
    1869           17490 :             break;
    1870                 :           }
    1871                 : 
    1872                 :           case JSOP_SETPROP: {
    1873            1378 :             JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc));
    1874            1378 :             jsid id = MakeTypeId(cx, ATOM_TO_JSID(atom));
    1875                 : 
    1876            1378 :             TypeSet *objTypes = analysis->poppedTypes(pc, 1);
    1877            1378 :             if (objTypes->unknownObject()) {
    1878             207 :                 unknownModset = true;
    1879             207 :                 break;
    1880                 :             }
    1881                 : 
    1882            1171 :             objTypes->addFreeze(cx);
    1883            2157 :             for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
    1884             986 :                 TypeObject *object = objTypes->getTypeObject(i);
    1885             986 :                 if (!object)
    1886              77 :                     continue;
    1887             909 :                 if (!addModifiedProperty(object, id))
    1888               0 :                     continue;
    1889                 :             }
    1890                 : 
    1891            1171 :             constrainedLoop = false;
    1892            1171 :             break;
    1893                 :           }
    1894                 : 
    1895                 :           case JSOP_ENUMELEM:
    1896                 :           case JSOP_ENUMCONSTELEM:
    1897               0 :             unknownModset = true;
    1898               0 :             break;
    1899                 : 
    1900                 :           case JSOP_LOOPHEAD:
    1901                 :           case JSOP_LOOPENTRY:
    1902                 :           case JSOP_POP:
    1903                 :           case JSOP_ZERO:
    1904                 :           case JSOP_ONE:
    1905                 :           case JSOP_INT8:
    1906                 :           case JSOP_INT32:
    1907                 :           case JSOP_UINT16:
    1908                 :           case JSOP_UINT24:
    1909                 :           case JSOP_FALSE:
    1910                 :           case JSOP_TRUE:
    1911                 :           case JSOP_GETARG:
    1912                 :           case JSOP_SETARG:
    1913                 :           case JSOP_INCARG:
    1914                 :           case JSOP_DECARG:
    1915                 :           case JSOP_ARGINC:
    1916                 :           case JSOP_ARGDEC:
    1917                 :           case JSOP_THIS:
    1918                 :           case JSOP_GETLOCAL:
    1919                 :           case JSOP_SETLOCAL:
    1920                 :           case JSOP_INCLOCAL:
    1921                 :           case JSOP_DECLOCAL:
    1922                 :           case JSOP_LOCALINC:
    1923                 :           case JSOP_LOCALDEC:
    1924                 :           case JSOP_IFEQ:
    1925                 :           case JSOP_IFNE:
    1926                 :           case JSOP_AND:
    1927                 :           case JSOP_OR:
    1928                 :           case JSOP_GOTO:
    1929          268347 :             break;
    1930                 : 
    1931                 :           case JSOP_ADD:
    1932                 :           case JSOP_SUB:
    1933                 :           case JSOP_MUL:
    1934                 :           case JSOP_MOD:
    1935                 :           case JSOP_DIV:
    1936                 :           case JSOP_BITAND:
    1937                 :           case JSOP_BITOR:
    1938                 :           case JSOP_BITXOR:
    1939                 :           case JSOP_RSH:
    1940                 :           case JSOP_LSH:
    1941                 :           case JSOP_URSH:
    1942                 :           case JSOP_EQ:
    1943                 :           case JSOP_NE:
    1944                 :           case JSOP_LT:
    1945                 :           case JSOP_LE:
    1946                 :           case JSOP_GT:
    1947                 :           case JSOP_GE:
    1948                 :           case JSOP_STRICTEQ:
    1949                 :           case JSOP_STRICTNE: {
    1950           45710 :             JSValueType type = analysis->poppedTypes(pc, 1)->getKnownTypeTag(cx);
    1951           45710 :             if (type != JSVAL_TYPE_INT32 && type != JSVAL_TYPE_DOUBLE)
    1952           15392 :                 constrainedLoop = false;
    1953                 :           }
    1954                 :           /* FALLTHROUGH */
    1955                 : 
    1956                 :           case JSOP_POS:
    1957                 :           case JSOP_NEG:
    1958                 :           case JSOP_BITNOT: {
    1959           50335 :             JSValueType type = analysis->poppedTypes(pc, 0)->getKnownTypeTag(cx);
    1960           50335 :             if (type != JSVAL_TYPE_INT32 && type != JSVAL_TYPE_DOUBLE)
    1961           12586 :                 constrainedLoop = false;
    1962           50335 :             break;
    1963                 :           }
    1964                 : 
    1965                 :           default:
    1966          238003 :             constrainedLoop = false;
    1967          238003 :             break;
    1968                 :         }
    1969                 : 
    1970          618620 :         offset = successorOffset;
    1971                 :     }
    1972                 : }
    1973                 : 
    1974                 : bool
    1975            1035 : LoopState::addGrowArray(TypeObject *object)
    1976                 : {
    1977                 :     static const uint32_t MAX_SIZE = 10;
    1978            1468 :     for (unsigned i = 0; i < growArrays.length(); i++) {
    1979             486 :         if (growArrays[i] == object)
    1980              53 :             return true;
    1981                 :     }
    1982             982 :     if (growArrays.length() >= MAX_SIZE) {
    1983               0 :         unknownModset = true;
    1984               0 :         return false;
    1985                 :     }
    1986             982 :     growArrays.append(object);
    1987                 : 
    1988             982 :     return true;
    1989                 : }
    1990                 : 
    1991                 : bool
    1992            6000 : LoopState::addModifiedProperty(TypeObject *object, jsid id)
    1993                 : {
    1994                 :     static const uint32_t MAX_SIZE = 20;
    1995            9469 :     for (unsigned i = 0; i < modifiedProperties.length(); i++) {
    1996            4906 :         if (modifiedProperties[i].object == object && modifiedProperties[i].id == id)
    1997            1437 :             return true;
    1998                 :     }
    1999            4563 :     if (modifiedProperties.length() >= MAX_SIZE) {
    2000               0 :         unknownModset = true;
    2001               0 :         return false;
    2002                 :     }
    2003                 : 
    2004                 :     ModifiedProperty property;
    2005            4563 :     property.object = object;
    2006            4563 :     property.id = id;
    2007            4563 :     modifiedProperties.append(property);
    2008                 : 
    2009            4563 :     return true;
    2010                 : }
    2011                 : 
    2012                 : bool
    2013             950 : LoopState::hasGrowArray(TypeObject *object)
    2014                 : {
    2015             950 :     if (unknownModset)
    2016               0 :         return true;
    2017            3391 :     for (unsigned i = 0; i < growArrays.length(); i++) {
    2018            2668 :         if (growArrays[i] == object)
    2019             227 :             return true;
    2020                 :     }
    2021             723 :     return false;
    2022                 : }
    2023                 : 
    2024                 : bool
    2025            2514 : LoopState::hasModifiedProperty(TypeObject *object, jsid id)
    2026                 : {
    2027            2514 :     if (unknownModset)
    2028              14 :         return true;
    2029            2500 :     id = MakeTypeId(cx, id);
    2030            2560 :     for (unsigned i = 0; i < modifiedProperties.length(); i++) {
    2031             213 :         if (modifiedProperties[i].object == object && modifiedProperties[i].id == id)
    2032             153 :             return true;
    2033                 :     }
    2034            2347 :     return false;
    2035                 : }
    2036                 : 
    2037                 : uint32_t
    2038            8144 : LoopState::getIncrement(uint32_t slot)
    2039                 : {
    2040           15886 :     for (unsigned i = 0; i < increments.length(); i++) {
    2041            8354 :         if (increments[i].slot == slot)
    2042             612 :             return increments[i].offset;
    2043                 :     }
    2044            7532 :     return UINT32_MAX;
    2045                 : }
    2046                 : 
    2047                 : int32_t
    2048            8079 : LoopState::adjustConstantForIncrement(jsbytecode *pc, uint32_t slot)
    2049                 : {
    2050                 :     /*
    2051                 :      * The only terms that can appear in a hoisted bounds check are either
    2052                 :      * loop invariant or are incremented or decremented exactly once in each
    2053                 :      * iteration of the loop. Depending on the current pc in the body of the
    2054                 :      * loop, return a constant adjustment if an increment/decrement for slot
    2055                 :      * has not yet happened, such that 'slot + n' at this point is the value
    2056                 :      * of slot at the start of the next iteration.
    2057                 :      */
    2058            8079 :     uint32_t offset = getIncrement(slot);
    2059                 : 
    2060                 :     /*
    2061                 :      * Note the '<' here. If this PC is at one of the increment opcodes, then
    2062                 :      * behave as if the increment has not happened yet. This is needed for loop
    2063                 :      * entry points, which can be directly at an increment. We won't rejoin
    2064                 :      * after the increment, as we only take stub calls in such situations on
    2065                 :      * integer overflow, which will disable hoisted conditions involving the
    2066                 :      * variable anyways.
    2067                 :      */
    2068            8079 :     if (offset == UINT32_MAX || offset < uint32_t(pc - outerScript->code))
    2069            7656 :         return 0;
    2070                 : 
    2071             423 :     switch (JSOp(outerScript->code[offset])) {
    2072                 :       case JSOP_INCLOCAL:
    2073                 :       case JSOP_LOCALINC:
    2074                 :       case JSOP_INCARG:
    2075                 :       case JSOP_ARGINC:
    2076             147 :         return 1;
    2077                 :       case JSOP_DECLOCAL:
    2078                 :       case JSOP_LOCALDEC:
    2079                 :       case JSOP_DECARG:
    2080                 :       case JSOP_ARGDEC:
    2081             276 :         return -1;
    2082                 :       default:
    2083               0 :         JS_NOT_REACHED("Bad op");
    2084                 :         return 0;
    2085                 :     }
    2086                 : }
    2087                 : 
    2088                 : bool
    2089           24727 : LoopState::getEntryValue(const CrossSSAValue &iv, uint32_t *pslot, int32_t *pconstant)
    2090                 : {
    2091           24727 :     CrossSSAValue cv = ssa->foldValue(iv);
    2092                 : 
    2093           24727 :     JSScript *script = ssa->getFrame(cv.frame).script;
    2094           24727 :     ScriptAnalysis *analysis = script->analysis();
    2095           24727 :     const SSAValue &v = cv.v;
    2096                 : 
    2097                 :     /*
    2098                 :      * For a stack value popped by the bytecode at offset, try to get an
    2099                 :      * expression 'slot + constant' with the same value as the stack value
    2100                 :      * and expressed in terms of the state at loop entry.
    2101                 :      */
    2102                 : 
    2103           24727 :     if (v.kind() == SSAValue::PHI) {
    2104            4651 :         if (cv.frame != CrossScriptSSA::OUTER_FRAME)
    2105               1 :             return false;
    2106            4650 :         if (v.phiSlot() >= TotalSlots(script))
    2107               5 :             return false;
    2108            4793 :         if (v.phiOffset() > lifetime->head &&
    2109             148 :             outerAnalysis->liveness(v.phiSlot()).firstWrite(lifetime) < v.phiOffset()) {
    2110              70 :             return false;
    2111                 :         }
    2112            4575 :         *pslot = v.phiSlot();
    2113            4575 :         *pconstant = 0;
    2114            4575 :         return true;
    2115                 :     }
    2116                 : 
    2117           20076 :     if (v.kind() == SSAValue::VAR) {
    2118            8000 :         if (cv.frame != CrossScriptSSA::OUTER_FRAME)
    2119              16 :             return false;
    2120            7984 :         if (v.varInitial() || v.varOffset() < lifetime->head) {
    2121            7658 :             *pslot = v.varSlot();
    2122            7658 :             *pconstant = 0;
    2123            7658 :             return true;
    2124                 :         }
    2125                 :     }
    2126                 : 
    2127           12402 :     if (v.kind() != SSAValue::PUSHED)
    2128             326 :         return false;
    2129                 : 
    2130           12076 :     jsbytecode *pc = script->code + v.pushedOffset();
    2131           12076 :     JSOp op = (JSOp)*pc;
    2132                 : 
    2133           12076 :     switch (op) {
    2134                 : 
    2135                 :       case JSOP_GETLOCAL:
    2136                 :       case JSOP_LOCALINC:
    2137                 :       case JSOP_INCLOCAL:
    2138                 :       case JSOP_GETARG:
    2139                 :       case JSOP_ARGINC:
    2140                 :       case JSOP_INCARG: {
    2141             197 :         if (cv.frame != CrossScriptSSA::OUTER_FRAME || !analysis->integerOperation(cx, pc))
    2142               0 :             return false;
    2143             197 :         uint32_t slot = GetBytecodeSlot(outerScript, pc);
    2144             197 :         if (outerAnalysis->slotEscapes(slot))
    2145              24 :             return false;
    2146             173 :         uint32_t write = outerAnalysis->liveness(slot).firstWrite(lifetime);
    2147             173 :         if (write != UINT32_MAX && write < v.pushedOffset()) {
    2148                 :             /* Variable has been modified since the start of the loop. */
    2149               3 :             return false;
    2150                 :         }
    2151             170 :         *pslot = slot;
    2152             170 :         *pconstant = (op == JSOP_INCLOCAL || op == JSOP_INCARG) ? 1 : 0;
    2153             170 :         return true;
    2154                 :       }
    2155                 : 
    2156                 :       case JSOP_THIS:
    2157             260 :         if (cv.frame != CrossScriptSSA::OUTER_FRAME)
    2158               0 :             return false;
    2159             260 :         *pslot = ThisSlot();
    2160             260 :         *pconstant = 0;
    2161             260 :         return true;
    2162                 : 
    2163                 :       case JSOP_ZERO:
    2164                 :       case JSOP_ONE:
    2165                 :       case JSOP_UINT16:
    2166                 :       case JSOP_UINT24:
    2167                 :       case JSOP_INT8:
    2168                 :       case JSOP_INT32:
    2169            6991 :         *pslot = UNASSIGNED;
    2170            6991 :         *pconstant = GetBytecodeInteger(pc);
    2171            6991 :         return true;
    2172                 : 
    2173                 :       case JSOP_LENGTH: {
    2174            1936 :         CrossSSAValue lengthcv(cv.frame, analysis->poppedValue(v.pushedOffset(), 0));
    2175            1936 :         FrameEntry *tmp = invariantLength(lengthcv);
    2176            1936 :         if (!tmp)
    2177             257 :             return false;
    2178            1679 :         *pslot = frame.outerSlot(tmp);
    2179            1679 :         *pconstant = 0;
    2180            1679 :         return true;
    2181                 :       }
    2182                 : 
    2183                 :       case JSOP_GETPROP: {
    2184             350 :         JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc));
    2185             350 :         jsid id = ATOM_TO_JSID(atom);
    2186             350 :         CrossSSAValue objcv(cv.frame, analysis->poppedValue(v.pushedOffset(), 0));
    2187             350 :         FrameEntry *tmp = invariantProperty(objcv, id);
    2188             350 :         if (!tmp)
    2189             156 :             return false;
    2190             194 :         *pslot = frame.outerSlot(tmp);
    2191             194 :         *pconstant = 0;
    2192             194 :         return true;
    2193                 :       }
    2194                 : 
    2195                 :       default:
    2196            2342 :         return false;
    2197                 :     }
    2198                 : }
    2199                 : 
    2200                 : bool
    2201            9669 : LoopState::computeInterval(const CrossSSAValue &cv, int32_t *pmin, int32_t *pmax)
    2202                 : {
    2203            9669 :     JSScript *script = ssa->getFrame(cv.frame).script;
    2204            9669 :     ScriptAnalysis *analysis = script->analysis();
    2205            9669 :     const SSAValue &v = cv.v;
    2206                 : 
    2207            9669 :     if (v.kind() == SSAValue::VAR && !v.varInitial()) {
    2208             168 :         jsbytecode *pc = script->code + v.varOffset();
    2209             168 :         switch (JSOp(*pc)) {
    2210                 :           case JSOP_SETLOCAL:
    2211                 :           case JSOP_SETARG: {
    2212             168 :             CrossSSAValue ncv(cv.frame, analysis->poppedValue(pc, 0));
    2213             168 :             return computeInterval(ncv, pmin, pmax);
    2214                 :           }
    2215                 : 
    2216                 :           default:
    2217               0 :             return false;
    2218                 :         }
    2219                 :     }
    2220                 : 
    2221            9501 :     if (v.kind() != SSAValue::PUSHED)
    2222             971 :         return false;
    2223                 : 
    2224            8530 :     jsbytecode *pc = script->code + v.pushedOffset();
    2225            8530 :     JSOp op = (JSOp)*pc;
    2226                 : 
    2227                 :     /* Note: this was adapted from similar code in nanojit/LIR.cpp */
    2228            8530 :     switch (op) {
    2229                 : 
    2230                 :       case JSOP_ZERO:
    2231                 :       case JSOP_ONE:
    2232                 :       case JSOP_UINT16:
    2233                 :       case JSOP_UINT24:
    2234                 :       case JSOP_INT8:
    2235                 :       case JSOP_INT32: {
    2236             476 :         int32_t constant = GetBytecodeInteger(pc);
    2237             476 :         *pmin = constant;
    2238             476 :         *pmax = constant;
    2239             476 :         return true;
    2240                 :       }
    2241                 : 
    2242                 :       case JSOP_BITAND: {
    2243                 :         int32_t lhsmin, lhsmax, rhsmin, rhsmax;
    2244             164 :         CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1));
    2245             164 :         CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
    2246             164 :         bool haslhs = computeInterval(lhsv, &lhsmin, &lhsmax);
    2247             164 :         bool hasrhs = computeInterval(rhsv, &rhsmin, &rhsmax);
    2248                 : 
    2249                 :         /* Only handle bitand with a constant operand. */
    2250             164 :         haslhs = haslhs && lhsmin == lhsmax && lhsmin >= 0;
    2251             164 :         hasrhs = hasrhs && rhsmin == rhsmax && rhsmin >= 0;
    2252                 : 
    2253             164 :         if (haslhs && hasrhs) {
    2254               0 :             *pmin = 0;
    2255               0 :             *pmax = Min(lhsmax, rhsmax);
    2256             164 :         } else if (haslhs) {
    2257              10 :             *pmin = 0;
    2258              10 :             *pmax = lhsmax;
    2259             154 :         } else if (hasrhs) {
    2260             122 :             *pmin = 0;
    2261             122 :             *pmax = rhsmax;
    2262                 :         } else {
    2263              32 :             return false;
    2264                 :         }
    2265             132 :         return true;
    2266                 :       }
    2267                 : 
    2268                 :       case JSOP_RSH: {
    2269                 :         int32_t rhsmin, rhsmax;
    2270             231 :         CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
    2271             231 :         if (!computeInterval(rhsv, &rhsmin, &rhsmax) || rhsmin != rhsmax)
    2272              17 :             return false;
    2273                 : 
    2274                 :         /* Only use the bottom 5 bits. */
    2275             214 :         int32_t shift = rhsmin & 0x1f;
    2276             214 :         *pmin = -(1 << (31 - shift));
    2277             214 :         *pmax = (1 << (31 - shift)) - 1;
    2278             214 :         return true;
    2279                 :       }
    2280                 : 
    2281                 :       case JSOP_URSH: {
    2282                 :         int32_t rhsmin, rhsmax;
    2283               0 :         CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
    2284               0 :         if (!computeInterval(rhsv, &rhsmin, &rhsmax) || rhsmin != rhsmax)
    2285               0 :             return false;
    2286                 : 
    2287                 :         /* Only use the bottom 5 bits. */
    2288               0 :         int32_t shift = rhsmin & 0x1f;
    2289               0 :         if (shift == 0)
    2290               0 :             return false;
    2291               0 :         *pmin = 0;
    2292               0 :         *pmax = (1 << (31 - shift)) - 1;
    2293               0 :         return true;
    2294                 :       }
    2295                 : 
    2296                 :       case JSOP_MOD: {
    2297                 :         int32_t rhsmin, rhsmax;
    2298               0 :         CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
    2299               0 :         if (!computeInterval(rhsv, &rhsmin, &rhsmax) || rhsmin != rhsmax)
    2300               0 :             return false;
    2301                 : 
    2302               0 :         int32_t rhs = abs(rhsmax);
    2303               0 :         *pmin = -(rhs - 1);
    2304               0 :         *pmax = rhs - 1;
    2305               0 :         return true;
    2306                 :       }
    2307                 : 
    2308                 :       case JSOP_ADD: {
    2309                 :         int32_t lhsmin, lhsmax, rhsmin, rhsmax;
    2310            2380 :         CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1));
    2311            2380 :         CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
    2312            2380 :         if (!computeInterval(lhsv, &lhsmin, &lhsmax) || !computeInterval(rhsv, &rhsmin, &rhsmax))
    2313            2234 :             return false;
    2314             146 :         return SafeAdd(lhsmin, rhsmin, pmin) && SafeAdd(lhsmax, rhsmax, pmax);
    2315                 :       }
    2316                 : 
    2317                 :       case JSOP_SUB: {
    2318                 :         int32_t lhsmin, lhsmax, rhsmin, rhsmax;
    2319             603 :         CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1));
    2320             603 :         CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
    2321             603 :         if (!computeInterval(lhsv, &lhsmin, &lhsmax) || !computeInterval(rhsv, &rhsmin, &rhsmax))
    2322             603 :             return false;
    2323               0 :         return SafeSub(lhsmin, rhsmax, pmin) && SafeSub(lhsmax, rhsmin, pmax);
    2324                 :       }
    2325                 : 
    2326                 :       case JSOP_MUL: {
    2327                 :         int32_t lhsmin, lhsmax, rhsmin, rhsmax;
    2328             648 :         CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1));
    2329             648 :         CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
    2330             648 :         if (!computeInterval(lhsv, &lhsmin, &lhsmax) || !computeInterval(rhsv, &rhsmin, &rhsmax))
    2331             608 :             return false;
    2332              40 :         int32_t nlhs = Max(abs(lhsmin), abs(lhsmax));
    2333              40 :         int32_t nrhs = Max(abs(rhsmin), abs(rhsmax));
    2334                 : 
    2335              40 :         if (!SafeMul(nlhs, nrhs, pmax))
    2336               8 :             return false;
    2337                 : 
    2338              32 :         if (lhsmin < 0 || rhsmin < 0) {
    2339                 :             /* pmax is nonnegative, so can be negated without overflow. */
    2340              16 :             *pmin = -*pmax;
    2341                 :         } else {
    2342              16 :             *pmin = 0;
    2343                 :         }
    2344                 : 
    2345              32 :         return true;
    2346                 :       }
    2347                 : 
    2348                 :       default:
    2349            4028 :         return false;
    2350                 :     }
    2351                 : }

Generated by: LCOV version 1.7