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

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