LCOV - app.info - js/src/jsanalyze.cpp

LCOV - code coverage report
| Current view: | directory - js/src - jsanalyze.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 993 | 852 | 85.8 %
| Date: | 2012-04-07 | Functions: | 31 | 27 | 87.1 %

           1                 : /* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */
           2                 : /* vim: set ts=40 sw=4 et tw=99: */
           3                 : /* ***** BEGIN LICENSE BLOCK *****
           4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
           5                 :  *
           6                 :  * The contents of this file are subject to the Mozilla Public License Version
           7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
           8                 :  * the License. You may obtain a copy of the License at
           9                 :  * http://www.mozilla.org/MPL/
          10                 :  *
          11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
          12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
          13                 :  * for the specific language governing rights and limitations under the
          14                 :  * License.
          15                 :  *
          16                 :  * The Original Code is the Mozilla SpiderMonkey bytecode analysis
          17                 :  *
          18                 :  * The Initial Developer of the Original Code is
          19                 :  *   Mozilla Foundation
          20                 :  * Portions created by the Initial Developer are Copyright (C) 2010
          21                 :  * the Initial Developer. All Rights Reserved.
          22                 :  *
          23                 :  * Contributor(s):
          24                 :  *   Brian Hackett <bhackett@mozilla.com>
          25                 :  *
          26                 :  * Alternatively, the contents of this file may be used under the terms of
          27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
          28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
          29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
          30                 :  * of those above. If you wish to allow use of your version of this file only
          31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
          32                 :  * use your version of this file under the terms of the MPL, indicate your
          33                 :  * decision by deleting the provisions above and replace them with the notice
          34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
          35                 :  * the provisions above, a recipient may use your version of this file under
          36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
          37                 :  *
          38                 :  * ***** END LICENSE BLOCK ***** */
          39                 :
          40                 : #include "jsanalyze.h"
          41                 : #include "jsautooplen.h"
          42                 : #include "jscompartment.h"
          43                 : #include "jscntxt.h"
          44                 :
          45                 : #include "jsinferinlines.h"
          46                 : #include "jsobjinlines.h"
          47                 :
          48                 : namespace js {
          49                 : namespace analyze {
          50                 :
          51                 : /////////////////////////////////////////////////////////////////////
          52                 : // Bytecode
          53                 : /////////////////////////////////////////////////////////////////////
          54                 :
          55                 : #ifdef DEBUG
          56                 : void
          57               0 : PrintBytecode(JSContext *cx, JSScript *script, jsbytecode *pc)
          58                 : {
          59               0 :     printf("#%u:", script->id());
          60               0 :     Sprinter sprinter(cx);
          61               0 :     if (!sprinter.init())
          62                 :         return;
          63               0 :     js_Disassemble1(cx, script, pc, pc - script->code, true, &sprinter);
          64               0 :     fprintf(stdout, "%s", sprinter.string());
          65                 : }
          66                 : #endif
          67                 :
          68                 : /////////////////////////////////////////////////////////////////////
          69                 : // Bytecode Analysis
          70                 : /////////////////////////////////////////////////////////////////////
          71                 :
          72                 : inline bool
          73          404363 : ScriptAnalysis::addJump(JSContext *cx, unsigned offset,
          74                 :                         unsigned *currentOffset, unsigned *forwardJump, unsigned *forwardLoop,
          75                 :                         unsigned stackDepth)
          76                 : {
          77          404363 :     JS_ASSERT(offset < script->length);
          78                 :
          79          404363 :     Bytecode *&code = codeArray[offset];
          80          404363 :     if (!code) {
          81          329838 :         code = cx->typeLifoAlloc().new_<Bytecode>();
          82          329838 :         if (!code) {
          83               0 :             setOOM(cx);
          84               0 :             return false;
          85                 :         }
          86          329838 :         code->stackDepth = stackDepth;
          87                 :     }
          88          404363 :     JS_ASSERT(code->stackDepth == stackDepth);
          89                 :
          90          404363 :     code->jumpTarget = true;
          91                 :
          92          404363 :     if (offset < *currentOffset) {
          93                 :         /* Scripts containing loops are never inlined. */
          94           39193 :         isInlineable = false;
          95                 :
          96           39193 :         if (code->analyzed) {
          97                 :             /*
          98                 :              * Backedge in a do-while loop, the body has been analyzed. Rewalk
          99                 :              * the body to set inLoop bits.
         100                 :              */
         101          896449 :             for (unsigned i = offset; i <= *currentOffset; i++) {
         102          896144 :                 Bytecode *code = maybeCode(i);
         103          896144 :                 if (code)
         104          299332 :                     code->inLoop = true;
         105                 :             }
         106                 :         } else {
         107                 :             /*
         108                 :              * Backedge in a while/for loop, whose body has not been analyzed
         109                 :              * due to a lack of fallthrough at the loop head. Roll back the
         110                 :              * offset to analyze the body.
         111                 :              */
         112           38888 :             if (*forwardJump == 0)
         113           31576 :                 *forwardJump = *currentOffset;
         114           38888 :             if (*forwardLoop == 0)
         115           33813 :                 *forwardLoop = *currentOffset;
         116           38888 :             *currentOffset = offset;
         117                 :         }
         118          365170 :     } else if (offset > *forwardJump) {
         119          134036 :         *forwardJump = offset;
         120                 :     }
         121                 :
         122          404363 :     return true;
         123                 : }
         124                 :
         125                 : void
         126          186629 : ScriptAnalysis::checkAliasedName(JSContext *cx, jsbytecode *pc)
         127                 : {
         128                 :     /*
         129                 :      * Check to see if an accessed name aliases a local or argument in the
         130                 :      * current script, and mark that local/arg as escaping. We don't need to
         131                 :      * worry about marking locals/arguments in scripts this is nested in, as
         132                 :      * the escaping name will be caught by the parser and the nested local/arg
         133                 :      * will be marked as closed.
         134                 :      */
         135                 :
         136                 :     JSAtom *atom;
         137          186629 :     if (JSOp(*pc) == JSOP_DEFFUN) {
         138            3262 :         JSFunction *fun = script->getFunction(GET_UINT32_INDEX(pc));
         139            3262 :         atom = fun->atom;
         140                 :     } else {
         141          183367 :         JS_ASSERT(JOF_TYPE(js_CodeSpec[*pc].format) == JOF_ATOM);
         142          183367 :         atom = script->getAtom(GET_UINT32_INDEX(pc));
         143                 :     }
         144                 :
         145                 :     unsigned index;
         146          186629 :     BindingKind kind = script->bindings.lookup(cx, atom, &index);
         147                 :
         148          186629 :     if (kind == ARGUMENT)
         149               0 :         escapedSlots[ArgSlot(index)] = true;
         150          186629 :     else if (kind == VARIABLE)
         151             180 :         escapedSlots[LocalSlot(script, index)] = true;
         152          186629 : }
         153                 :
         154                 : void
         155          166090 : ScriptAnalysis::analyzeBytecode(JSContext *cx)
         156                 : {
         157          166090 :     JS_ASSERT(cx->compartment->activeAnalysis);
         158          166090 :     JS_ASSERT(!ranBytecode());
         159          166090 :     LifoAlloc &tla = cx->typeLifoAlloc();
         160                 :
         161          166090 :     unsigned length = script->length;
         162          166090 :     unsigned nargs = script->function() ? script->function()->nargs : 0;
         163                 :
         164          166090 :     numSlots = TotalSlots(script);
         165                 :
         166          166090 :     codeArray = tla.newArray<Bytecode*>(length);
         167          166090 :     escapedSlots = tla.newArray<bool>(numSlots);
         168                 :
         169          166090 :     if (!codeArray || !escapedSlots) {
         170               0 :         setOOM(cx);
         171               0 :         return;
         172                 :     }
         173                 :
         174          166090 :     PodZero(codeArray, length);
         175                 :
         176                 :     /*
         177                 :      * Populate arg and local slots which can escape and be accessed in ways
         178                 :      * other than through ARG* and LOCAL* opcodes (though arguments can still
         179                 :      * be indirectly read but not written through 'arguments' properties).
         180                 :      * All escaping locals are treated as having possible use-before-defs.
         181                 :      * Conservatively use 'mayNeedArgsObj' instead of 'needsArgsObj'
         182                 :      * (needsArgsObj requires SSA which requires escapedSlots).
         183                 :      */
         184                 :
         185          166090 :     PodZero(escapedSlots, numSlots);
         186                 :
         187          166090 :     if (script->usesEval || script->mayNeedArgsObj() || script->compartment()->debugMode()) {
         188          241559 :         for (unsigned i = 0; i < nargs; i++)
         189          168066 :             escapedSlots[ArgSlot(i)] = true;
         190                 :     } else {
         191           93512 :         for (uint32_t i = 0; i < script->nClosedArgs(); i++) {
         192             915 :             unsigned arg = script->getClosedArg(i);
         193             915 :             JS_ASSERT(arg < nargs);
         194             915 :             escapedSlots[ArgSlot(arg)] = true;
         195                 :         }
         196                 :     }
         197                 :
         198          166090 :     if (script->usesEval || script->compartment()->debugMode()) {
         199          169983 :         for (unsigned i = 0; i < script->nfixed; i++)
         200           98038 :             escapedSlots[LocalSlot(script, i)] = true;
         201                 :     } else {
         202           97969 :         for (uint32_t i = 0; i < script->nClosedVars(); i++) {
         203            3824 :             unsigned local = script->getClosedVar(i);
         204            3824 :             JS_ASSERT(local < script->nfixed);
         205            3824 :             escapedSlots[LocalSlot(script, local)] = true;
         206                 :         }
         207                 :     }
         208                 :
         209                 :     /*
         210                 :      * If the script is in debug mode, JS_SetFrameReturnValue can be called at
         211                 :      * any safe point.
         212                 :      */
         213          166090 :     if (cx->compartment->debugMode())
         214           69096 :         usesReturnValue_ = true;
         215                 :
         216          166090 :     bool heavyweight = script->function() && script->function()->isHeavyweight();
         217                 :
         218          166090 :     isCompileable = true;
         219                 :
         220          166090 :     isInlineable = true;
         221          468792 :     if (script->nClosedArgs() || script->nClosedVars() || heavyweight ||
         222          302702 :         script->usesEval || script->mayNeedArgsObj() || cx->compartment->debugMode()) {
         223           76344 :         isInlineable = false;
         224                 :     }
         225                 :
         226          166090 :     modifiesArguments_ = false;
         227          166090 :     if (script->nClosedArgs() || heavyweight)
         228           11246 :         modifiesArguments_ = true;
         229                 :
         230          166090 :     canTrackVars = true;
         231                 :
         232                 :     /*
         233                 :      * If we are in the middle of one or more jumps, the offset of the highest
         234                 :      * target jumping over this bytecode.  Includes implicit jumps from
         235                 :      * try/catch/finally blocks.
         236                 :      */
         237          166090 :     unsigned forwardJump = 0;
         238                 :
         239                 :     /* If we are in the middle of a loop, the offset of the highest backedge. */
         240          166090 :     unsigned forwardLoop = 0;
         241                 :
         242                 :     /*
         243                 :      * If we are in the middle of a try block, the offset of the highest
         244                 :      * catch/finally/enditer.
         245                 :      */
         246          166090 :     unsigned forwardCatch = 0;
         247                 :
         248                 :     /* Fill in stack depth and definitions at initial bytecode. */
         249          166090 :     Bytecode *startcode = tla.new_<Bytecode>();
         250          166090 :     if (!startcode) {
         251               0 :         setOOM(cx);
         252               0 :         return;
         253                 :     }
         254                 :
         255          166090 :     startcode->stackDepth = 0;
         256          166090 :     codeArray[0] = startcode;
         257                 :
         258                 :     /* Number of JOF_TYPESET opcodes we have encountered. */
         259          166090 :     unsigned nTypeSets = 0;
         260          166090 :     types::TypeSet *typeArray = script->types->typeArray();
         261                 :
         262          166090 :     unsigned offset, nextOffset = 0;
         263        11035444 :     while (nextOffset < length) {
         264        10703264 :         offset = nextOffset;
         265                 :
         266        10703264 :         JS_ASSERT(forwardCatch <= forwardJump);
         267                 :
         268                 :         /* Check if the current forward jump/try-block has finished. */
         269        10703264 :         if (forwardJump && forwardJump == offset)
         270          158082 :             forwardJump = 0;
         271        10703264 :         if (forwardCatch && forwardCatch == offset)
         272           70019 :             forwardCatch = 0;
         273                 :
         274        10703264 :         Bytecode *code = maybeCode(offset);
         275        10703264 :         jsbytecode *pc = script->code + offset;
         276                 :
         277        10703264 :         JSOp op = (JSOp)*pc;
         278        10703264 :         JS_ASSERT(op < JSOP_LIMIT);
         279                 :
         280                 :         /* Immediate successor of this bytecode. */
         281        10703264 :         unsigned successorOffset = offset + GetBytecodeLength(pc);
         282                 :
         283                 :         /*
         284                 :          * Next bytecode to analyze.  This is either the successor, or is an
         285                 :          * earlier bytecode if this bytecode has a loop backedge.
         286                 :          */
         287        10703264 :         nextOffset = successorOffset;
         288                 :
         289        10703264 :         if (!code) {
         290                 :             /* Haven't found a path by which this bytecode is reachable. */
         291         1341202 :             continue;
         292                 :         }
         293                 :
         294                 :         /*
         295                 :          * Update info about bytecodes inside loops, which may have been
         296                 :          * analyzed before the backedge was seen.
         297                 :          */
         298         9362062 :         if (forwardLoop) {
         299         1335191 :             code->inLoop = true;
         300         1335191 :             if (forwardLoop <= offset)
         301           33813 :                 forwardLoop = 0;
         302                 :         }
         303                 :
         304         9362062 :         if (code->analyzed) {
         305                 :             /* No need to reanalyze, see Bytecode::mergeDefines. */
         306          191953 :             continue;
         307                 :         }
         308                 :
         309         9170109 :         code->analyzed = true;
         310                 :
         311         9170109 :         if (forwardCatch)
         312          435959 :             code->inTryBlock = true;
         313                 :
         314         9170109 :         if (script->hasBreakpointsAt(pc)) {
         315             532 :             code->safePoint = true;
         316             532 :             isInlineable = canTrackVars = false;
         317                 :         }
         318                 :
         319         9170109 :         unsigned stackDepth = code->stackDepth;
         320                 :
         321         9170109 :         if (!forwardJump)
         322         3615063 :             code->unconditional = true;
         323                 :
         324                 :         /*
         325                 :          * Treat decompose ops as no-ops which do not adjust the stack. We will
         326                 :          * pick up the stack depths as we go through the decomposed version.
         327                 :          */
         328         9170109 :         if (!(js_CodeSpec[op].format & JOF_DECOMPOSE)) {
         329         9157056 :             unsigned nuses = GetUseCount(script, offset);
         330         9157056 :             unsigned ndefs = GetDefCount(script, offset);
         331                 :
         332         9157056 :             JS_ASSERT(stackDepth >= nuses);
         333         9157056 :             stackDepth -= nuses;
         334         9157056 :             stackDepth += ndefs;
         335                 :         }
         336                 :
         337                 :         /*
         338                 :          * Assign an observed type set to each reachable JOF_TYPESET opcode.
         339                 :          * This may be less than the number of type sets in the script if some
         340                 :          * are unreachable, and may be greater in case the number of type sets
         341                 :          * overflows a uint16. In the latter case a single type set will be
         342                 :          * used for the observed types of all ops after the overflow.
         343                 :          */
         344         9170109 :         if ((js_CodeSpec[op].format & JOF_TYPESET) && cx->typeInferenceEnabled()) {
         345         1279955 :             if (nTypeSets < script->nTypeSets) {
         346         1279955 :                 code->observedTypes = &typeArray[nTypeSets++];
         347                 :             } else {
         348               0 :                 JS_ASSERT(nTypeSets == UINT16_MAX);
         349               0 :                 code->observedTypes = &typeArray[nTypeSets - 1];
         350                 :             }
         351                 :         }
         352                 :
         353         9170109 :         switch (op) {
         354                 :
         355                 :           case JSOP_RETURN:
         356                 :           case JSOP_STOP:
         357          167962 :             numReturnSites_++;
         358          167962 :             break;
         359                 :
         360                 :           case JSOP_SETRVAL:
         361                 :           case JSOP_POPV:
         362           37919 :             usesReturnValue_ = true;
         363           37919 :             isInlineable = false;
         364           37919 :             break;
         365                 :
         366                 :           case JSOP_QNAMEPART:
         367                 :           case JSOP_QNAMECONST:
         368             688 :             isCompileable = false;
         369                 :           case JSOP_NAME:
         370                 :           case JSOP_CALLNAME:
         371                 :           case JSOP_BINDNAME:
         372                 :           case JSOP_SETNAME:
         373                 :           case JSOP_DELNAME:
         374           67763 :             checkAliasedName(cx, pc);
         375           67763 :             usesScopeChain_ = true;
         376           67763 :             isInlineable = false;
         377           67763 :             break;
         378                 :
         379                 :           case JSOP_DEFFUN:
         380                 :           case JSOP_DEFVAR:
         381                 :           case JSOP_DEFCONST:
         382                 :           case JSOP_SETCONST:
         383          118866 :             checkAliasedName(cx, pc);
         384          118866 :             extendsScope_ = true;
         385          118866 :             isInlineable = canTrackVars = false;
         386          118866 :             break;
         387                 :
         388                 :           case JSOP_EVAL:
         389            3489 :             extendsScope_ = true;
         390            3489 :             isInlineable = canTrackVars = false;
         391            3489 :             break;
         392                 :
         393                 :           case JSOP_ENTERWITH:
         394             696 :             addsScopeObjects_ = true;
         395             696 :             isCompileable = isInlineable = canTrackVars = false;
         396             696 :             break;
         397                 :
         398                 :           case JSOP_ENTERLET0:
         399                 :           case JSOP_ENTERLET1:
         400                 :           case JSOP_ENTERBLOCK:
         401                 :           case JSOP_LEAVEBLOCK:
         402          158699 :             addsScopeObjects_ = true;
         403          158699 :             isInlineable = false;
         404          158699 :             break;
         405                 :
         406                 :           case JSOP_THIS:
         407           87549 :             usesThisValue_ = true;
         408           87549 :             break;
         409                 :
         410                 :           case JSOP_CALL:
         411                 :           case JSOP_NEW:
         412                 :             /* Only consider potentially inlineable calls here. */
         413          240133 :             hasFunctionCalls_ = true;
         414          240133 :             break;
         415                 :
         416                 :           case JSOP_TABLESWITCH: {
         417             436 :             isInlineable = false;
         418             436 :             unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
         419             436 :             jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
         420             436 :             int32_t low = GET_JUMP_OFFSET(pc2);
         421             436 :             pc2 += JUMP_OFFSET_LEN;
         422             436 :             int32_t high = GET_JUMP_OFFSET(pc2);
         423             436 :             pc2 += JUMP_OFFSET_LEN;
         424                 :
         425             436 :             if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump, &forwardLoop, stackDepth))
         426               0 :                 return;
         427             436 :             getCode(defaultOffset).switchTarget = true;
         428             436 :             getCode(defaultOffset).safePoint = true;
         429                 :
         430            1525 :             for (int32_t i = low; i <= high; i++) {
         431            1089 :                 unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
         432            1089 :                 if (targetOffset != offset) {
         433            1053 :                     if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, &forwardLoop, stackDepth))
         434               0 :                         return;
         435                 :                 }
         436            1089 :                 getCode(targetOffset).switchTarget = true;
         437            1089 :                 getCode(targetOffset).safePoint = true;
         438            1089 :                 pc2 += JUMP_OFFSET_LEN;
         439                 :             }
         440             436 :             break;
         441                 :           }
         442                 :
         443                 :           case JSOP_LOOKUPSWITCH: {
         444              90 :             isInlineable = false;
         445              90 :             unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
         446              90 :             jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
         447              90 :             unsigned npairs = GET_UINT16(pc2);
         448              90 :             pc2 += UINT16_LEN;
         449                 :
         450              90 :             if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump, &forwardLoop, stackDepth))
         451               0 :                 return;
         452              90 :             getCode(defaultOffset).switchTarget = true;
         453              90 :             getCode(defaultOffset).safePoint = true;
         454                 :
         455             612 :             while (npairs) {
         456             432 :                 pc2 += UINT32_INDEX_LEN;
         457             432 :                 unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
         458             432 :                 if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, &forwardLoop, stackDepth))
         459               0 :                     return;
         460             432 :                 getCode(targetOffset).switchTarget = true;
         461             432 :                 getCode(targetOffset).safePoint = true;
         462             432 :                 pc2 += JUMP_OFFSET_LEN;
         463             432 :                 npairs--;
         464                 :             }
         465              90 :             break;
         466                 :           }
         467                 :
         468                 :           case JSOP_TRY: {
         469                 :             /*
         470                 :              * Everything between a try and corresponding catch or finally is conditional.
         471                 :              * Note that there is no problem with code which is skipped by a thrown
         472                 :              * exception but is not caught by a later handler in the same function:
         473                 :              * no more code will execute, and it does not matter what is defined.
         474                 :              */
         475           71614 :             isInlineable = false;
         476           71614 :             JSTryNote *tn = script->trynotes()->vector;
         477           71614 :             JSTryNote *tnlimit = tn + script->trynotes()->length;
         478       286349070 :             for (; tn < tnlimit; tn++) {
         479       286277456 :                 unsigned startOffset = script->mainOffset + tn->start;
         480       286277456 :                 if (startOffset == offset + 1) {
         481           71659 :                     unsigned catchOffset = startOffset + tn->length;
         482                 :
         483                 :                     /* This will overestimate try block code, for multiple catch/finally. */
         484           71659 :                     if (catchOffset > forwardCatch)
         485           70064 :                         forwardCatch = catchOffset;
         486                 :
         487           71659 :                     if (tn->kind != JSTRY_ITER) {
         488           71659 :                         if (!addJump(cx, catchOffset, &nextOffset, &forwardJump, &forwardLoop, stackDepth))
         489               0 :                             return;
         490           71659 :                         getCode(catchOffset).exceptionEntry = true;
         491           71659 :                         getCode(catchOffset).safePoint = true;
         492                 :                     }
         493                 :                 }
         494                 :             }
         495           71614 :             break;
         496                 :           }
         497                 :
         498                 :           case JSOP_GETLOCAL: {
         499                 :             /*
         500                 :              * Watch for uses of variables not known to be defined, and mark
         501                 :              * them as having possible uses before definitions.  Ignore GETLOCAL
         502                 :              * followed by a POP, these are generated for, e.g. 'var x;'
         503                 :              */
         504          329976 :             jsbytecode *next = pc + JSOP_GETLOCAL_LENGTH;
         505          329976 :             if (JSOp(*next) != JSOP_POP || jumpTarget(next)) {
         506          168737 :                 uint32_t local = GET_SLOTNO(pc);
         507          168737 :                 if (local >= script->nfixed) {
         508           14788 :                     localsAliasStack_ = true;
         509           14788 :                     break;
         510                 :                 }
         511                 :             }
         512          315188 :             break;
         513                 :           }
         514                 :
         515                 :           case JSOP_CALLLOCAL:
         516                 :           case JSOP_INCLOCAL:
         517                 :           case JSOP_DECLOCAL:
         518                 :           case JSOP_LOCALINC:
         519                 :           case JSOP_LOCALDEC:
         520                 :           case JSOP_SETLOCAL: {
         521          401851 :             uint32_t local = GET_SLOTNO(pc);
         522          401851 :             if (local >= script->nfixed) {
         523           78352 :                 localsAliasStack_ = true;
         524           78352 :                 break;
         525                 :             }
         526          323499 :             break;
         527                 :           }
         528                 :
         529                 :           case JSOP_SETARG:
         530                 :           case JSOP_INCARG:
         531                 :           case JSOP_DECARG:
         532                 :           case JSOP_ARGINC:
         533                 :           case JSOP_ARGDEC:
         534            2252 :             modifiesArguments_ = true;
         535            2252 :             isInlineable = false;
         536            2252 :             break;
         537                 :
         538                 :           /* Additional opcodes which can be compiled but which can't be inlined. */
         539                 :           case JSOP_ARGUMENTS:
         540                 :           case JSOP_THROW:
         541                 :           case JSOP_EXCEPTION:
         542                 :           case JSOP_LAMBDA:
         543                 :           case JSOP_DEBUGGER:
         544                 :           case JSOP_FUNCALL:
         545                 :           case JSOP_FUNAPPLY:
         546          215864 :             isInlineable = false;
         547          215864 :             break;
         548                 :
         549                 :           /* Additional opcodes which can be both compiled both normally and inline. */
         550                 :           case JSOP_NOP:
         551                 :           case JSOP_UNDEFINED:
         552                 :           case JSOP_GOTO:
         553                 :           case JSOP_DEFAULT:
         554                 :           case JSOP_IFEQ:
         555                 :           case JSOP_IFNE:
         556                 :           case JSOP_ITERNEXT:
         557                 :           case JSOP_DUP:
         558                 :           case JSOP_DUP2:
         559                 :           case JSOP_SWAP:
         560                 :           case JSOP_PICK:
         561                 :           case JSOP_BITOR:
         562                 :           case JSOP_BITXOR:
         563                 :           case JSOP_BITAND:
         564                 :           case JSOP_LT:
         565                 :           case JSOP_LE:
         566                 :           case JSOP_GT:
         567                 :           case JSOP_GE:
         568                 :           case JSOP_EQ:
         569                 :           case JSOP_NE:
         570                 :           case JSOP_LSH:
         571                 :           case JSOP_RSH:
         572                 :           case JSOP_URSH:
         573                 :           case JSOP_ADD:
         574                 :           case JSOP_SUB:
         575                 :           case JSOP_MUL:
         576                 :           case JSOP_DIV:
         577                 :           case JSOP_MOD:
         578                 :           case JSOP_NOT:
         579                 :           case JSOP_BITNOT:
         580                 :           case JSOP_NEG:
         581                 :           case JSOP_POS:
         582                 :           case JSOP_DELPROP:
         583                 :           case JSOP_DELELEM:
         584                 :           case JSOP_TYPEOF:
         585                 :           case JSOP_TYPEOFEXPR:
         586                 :           case JSOP_VOID:
         587                 :           case JSOP_GETPROP:
         588                 :           case JSOP_CALLPROP:
         589                 :           case JSOP_LENGTH:
         590                 :           case JSOP_GETELEM:
         591                 :           case JSOP_CALLELEM:
         592                 :           case JSOP_TOID:
         593                 :           case JSOP_SETELEM:
         594                 :           case JSOP_IMPLICITTHIS:
         595                 :           case JSOP_DOUBLE:
         596                 :           case JSOP_STRING:
         597                 :           case JSOP_ZERO:
         598                 :           case JSOP_ONE:
         599                 :           case JSOP_NULL:
         600                 :           case JSOP_FALSE:
         601                 :           case JSOP_TRUE:
         602                 :           case JSOP_OR:
         603                 :           case JSOP_AND:
         604                 :           case JSOP_CASE:
         605                 :           case JSOP_STRICTEQ:
         606                 :           case JSOP_STRICTNE:
         607                 :           case JSOP_ITER:
         608                 :           case JSOP_MOREITER:
         609                 :           case JSOP_ENDITER:
         610                 :           case JSOP_POP:
         611                 :           case JSOP_GETARG:
         612                 :           case JSOP_CALLARG:
         613                 :           case JSOP_BINDGNAME:
         614                 :           case JSOP_UINT16:
         615                 :           case JSOP_NEWINIT:
         616                 :           case JSOP_NEWARRAY:
         617                 :           case JSOP_NEWOBJECT:
         618                 :           case JSOP_ENDINIT:
         619                 :           case JSOP_INITPROP:
         620                 :           case JSOP_INITELEM:
         621                 :           case JSOP_SETPROP:
         622                 :           case JSOP_IN:
         623                 :           case JSOP_INSTANCEOF:
         624                 :           case JSOP_LINENO:
         625                 :           case JSOP_ENUMELEM:
         626                 :           case JSOP_CONDSWITCH:
         627                 :           case JSOP_LABEL:
         628                 :           case JSOP_RETRVAL:
         629                 :           case JSOP_GETGNAME:
         630                 :           case JSOP_CALLGNAME:
         631                 :           case JSOP_SETGNAME:
         632                 :           case JSOP_REGEXP:
         633                 :           case JSOP_OBJECT:
         634                 :           case JSOP_UINT24:
         635                 :           case JSOP_GETXPROP:
         636                 :           case JSOP_INT8:
         637                 :           case JSOP_INT32:
         638                 :           case JSOP_HOLE:
         639                 :           case JSOP_LOOPHEAD:
         640                 :           case JSOP_LOOPENTRY:
         641         7238203 :             break;
         642                 :
         643                 :           default:
         644           26747 :             if (!(js_CodeSpec[op].format & JOF_DECOMPOSE))
         645           13694 :                 isCompileable = isInlineable = false;
         646           26747 :             break;
         647                 :         }
         648                 :
         649         9170109 :         uint32_t type = JOF_TYPE(js_CodeSpec[op].format);
         650                 :
         651                 :         /* Check basic jump opcodes, which may or may not have a fallthrough. */
         652         9170109 :         if (type == JOF_JUMP) {
         653                 :             /* Some opcodes behave differently on their branching path. */
         654          330693 :             unsigned newStackDepth = stackDepth;
         655                 :
         656          330693 :             switch (op) {
         657                 :               case JSOP_CASE:
         658                 :                 /* Case instructions do not push the lvalue back when branching. */
         659             324 :                 newStackDepth--;
         660             324 :                 break;
         661                 :
         662                 :               default:;
         663                 :             }
         664                 :
         665          330693 :             unsigned targetOffset = offset + GET_JUMP_OFFSET(pc);
         666          330693 :             if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, &forwardLoop, newStackDepth))
         667               0 :                 return;
         668                 :         }
         669                 :
         670                 :         /* Handle any fallthrough from this opcode. */
         671         9170109 :         if (!BytecodeNoFallThrough(op)) {
         672         8807162 :             JS_ASSERT(successorOffset < script->length);
         673                 :
         674         8807162 :             Bytecode *&nextcode = codeArray[successorOffset];
         675                 :
         676         8807162 :             if (!nextcode) {
         677         8674181 :                 nextcode = tla.new_<Bytecode>();
         678         8674181 :                 if (!nextcode) {
         679               0 :                     setOOM(cx);
         680               0 :                     return;
         681                 :                 }
         682         8674181 :                 nextcode->stackDepth = stackDepth;
         683                 :             }
         684         8807162 :             JS_ASSERT(nextcode->stackDepth == stackDepth);
         685                 :
         686         8807162 :             if (type == JOF_JUMP)
         687          140501 :                 nextcode->jumpFallthrough = true;
         688                 :
         689                 :             /* Treat the fallthrough of a branch instruction as a jump target. */
         690         8807162 :             if (type == JOF_JUMP)
         691          140501 :                 nextcode->jumpTarget = true;
         692                 :             else
         693         8666661 :                 nextcode->fallthrough = true;
         694                 :         }
         695                 :     }
         696                 :
         697          166090 :     JS_ASSERT(!failed());
         698          166090 :     JS_ASSERT(forwardJump == 0 && forwardLoop == 0 && forwardCatch == 0);
         699                 :
         700          166090 :     ranBytecode_ = true;
         701                 :
         702                 :     /*
         703                 :      * Always ensure that a script's arguments usage has been analyzed before
         704                 :      * entering the script. This allows the functionPrologue to ensure that
         705                 :      * arguments are always created eagerly which simplifies interp logic.
         706                 :      */
         707          166090 :     if (!script->analyzedArgsUsage()) {
         708          150390 :         if (!script->mayNeedArgsObj())
         709          148194 :             script->setNeedsArgsObj(false);
         710                 :         else
         711            2196 :             analyzeSSA(cx);
         712          150390 :         JS_ASSERT_IF(!failed(), script->analyzedArgsUsage());
         713                 :     }
         714                 : }
         715                 :
         716                 : /////////////////////////////////////////////////////////////////////
         717                 : // Lifetime Analysis
         718                 : /////////////////////////////////////////////////////////////////////
         719                 :
         720                 : void
         721           40259 : ScriptAnalysis::analyzeLifetimes(JSContext *cx)
         722                 : {
         723           40259 :     JS_ASSERT(cx->compartment->activeAnalysis && !ranLifetimes() && !failed());
         724                 :
         725           40259 :     if (!ranBytecode()) {
         726               0 :         analyzeBytecode(cx);
         727               0 :         if (failed())
         728               0 :             return;
         729                 :     }
         730                 :
         731           40259 :     LifoAlloc &tla = cx->typeLifoAlloc();
         732                 :
         733           40259 :     lifetimes = tla.newArray<LifetimeVariable>(numSlots);
         734           40259 :     if (!lifetimes) {
         735               0 :         setOOM(cx);
         736               0 :         return;
         737                 :     }
         738           40259 :     PodZero(lifetimes, numSlots);
         739                 :
         740                 :     /*
         741                 :      * Variables which are currently dead. On forward branches to locations
         742                 :      * where these are live, they need to be marked as live.
         743                 :      */
         744                 :     LifetimeVariable **saved = (LifetimeVariable **)
         745           40259 :         cx->calloc_(numSlots * sizeof(LifetimeVariable*));
         746           40259 :     if (!saved) {
         747               0 :         setOOM(cx);
         748               0 :         return;
         749                 :     }
         750           40259 :     unsigned savedCount = 0;
         751                 :
         752           40259 :     LoopAnalysis *loop = NULL;
         753                 :
         754           40259 :     uint32_t offset = script->length - 1;
         755         6024314 :     while (offset < script->length) {
         756         5943796 :         Bytecode *code = maybeCode(offset);
         757         5943796 :         if (!code) {
         758         3796612 :             offset--;
         759         3796612 :             continue;
         760                 :         }
         761                 :
         762         2147184 :         if (loop && code->safePoint)
         763            3950 :             loop->hasSafePoints = true;
         764                 :
         765         2147184 :         jsbytecode *pc = script->code + offset;
         766                 :
         767         2147184 :         JSOp op = (JSOp) *pc;
         768                 :
         769         2147184 :         if (op == JSOP_LOOPHEAD && code->loop) {
         770                 :             /*
         771                 :              * This is the head of a loop, we need to go and make sure that any
         772                 :              * variables live at the head are live at the backedge and points prior.
         773                 :              * For each such variable, look for the last lifetime segment in the body
         774                 :              * and extend it to the end of the loop.
         775                 :              */
         776           11018 :             JS_ASSERT(loop == code->loop);
         777           11018 :             unsigned backedge = code->loop->backedge;
         778           66342 :             for (unsigned i = 0; i < numSlots; i++) {
         779           55324 :                 if (lifetimes[i].lifetime)
         780           10267 :                     extendVariable(cx, lifetimes[i], offset, backedge);
         781                 :             }
         782                 :
         783           11018 :             loop = loop->parent;
         784           11018 :             JS_ASSERT_IF(loop, loop->head < offset);
         785                 :         }
         786                 :
         787                 :         /* Find the last jump target in the loop, other than the initial entry point. */
         788         2147184 :         if (loop && code->jumpTarget && offset != loop->entry && offset > loop->lastBlock)
         789            3428 :             loop->lastBlock = offset;
         790                 :
         791         2147184 :         if (code->exceptionEntry) {
         792           30478 :             DebugOnly<bool> found = false;
         793           15239 :             JSTryNote *tn = script->trynotes()->vector;
         794           15239 :             JSTryNote *tnlimit = tn + script->trynotes()->length;
         795        31815178 :             for (; tn < tnlimit; tn++) {
         796        31815178 :                 unsigned startOffset = script->mainOffset + tn->start;
         797        31815178 :                 if (startOffset + tn->length == offset) {
         798                 :                     /*
         799                 :                      * Extend all live variables at exception entry to the start of
         800                 :                      * the try block.
         801                 :                      */
         802           47012 :                     for (unsigned i = 0; i < numSlots; i++) {
         803           31773 :                         if (lifetimes[i].lifetime)
         804             195 :                             ensureVariable(lifetimes[i], startOffset - 1);
         805                 :                     }
         806                 :
         807           15239 :                     found = true;
         808           15239 :                     break;
         809                 :                 }
         810                 :             }
         811           15239 :             JS_ASSERT(found);
         812                 :         }
         813                 :
         814         2147184 :         switch (op) {
         815                 :           case JSOP_GETARG:
         816                 :           case JSOP_CALLARG:
         817                 :           case JSOP_GETLOCAL:
         818                 :           case JSOP_CALLLOCAL:
         819                 :           case JSOP_THIS: {
         820          142567 :             uint32_t slot = GetBytecodeSlot(script, pc);
         821          142567 :             if (!slotEscapes(slot))
         822           62027 :                 addVariable(cx, lifetimes[slot], offset, saved, savedCount);
         823          142567 :             break;
         824                 :           }
         825                 :
         826                 :           case JSOP_SETARG:
         827                 :           case JSOP_SETLOCAL: {
         828           98746 :             uint32_t slot = GetBytecodeSlot(script, pc);
         829           98746 :             if (!slotEscapes(slot))
         830           40170 :                 killVariable(cx, lifetimes[slot], offset, saved, savedCount);
         831           98746 :             break;
         832                 :           }
         833                 :
         834                 :           case JSOP_INCARG:
         835                 :           case JSOP_DECARG:
         836                 :           case JSOP_ARGINC:
         837                 :           case JSOP_ARGDEC:
         838                 :           case JSOP_INCLOCAL:
         839                 :           case JSOP_DECLOCAL:
         840                 :           case JSOP_LOCALINC:
         841                 :           case JSOP_LOCALDEC: {
         842            6614 :             uint32_t slot = GetBytecodeSlot(script, pc);
         843            6614 :             if (!slotEscapes(slot)) {
         844            2714 :                 killVariable(cx, lifetimes[slot], offset, saved, savedCount);
         845            2714 :                 addVariable(cx, lifetimes[slot], offset, saved, savedCount);
         846                 :             }
         847            6614 :             break;
         848                 :           }
         849                 :
         850                 :           case JSOP_LOOKUPSWITCH:
         851                 :           case JSOP_TABLESWITCH:
         852                 :             /* Restore all saved variables. :FIXME: maybe do this precisely. */
         853             151 :             for (unsigned i = 0; i < savedCount; i++) {
         854              21 :                 LifetimeVariable &var = *saved[i];
         855              21 :                 var.lifetime = tla.new_<Lifetime>(offset, var.savedEnd, var.saved);
         856              21 :                 if (!var.lifetime) {
         857               0 :                     cx->free_(saved);
         858               0 :                     setOOM(cx);
         859               0 :                     return;
         860                 :                 }
         861              21 :                 var.saved = NULL;
         862              21 :                 saved[i--] = saved[--savedCount];
         863                 :             }
         864             130 :             savedCount = 0;
         865             130 :             break;
         866                 :
         867                 :           case JSOP_TRY:
         868           46980 :             for (unsigned i = 0; i < numSlots; i++) {
         869           31751 :                 LifetimeVariable &var = lifetimes[i];
         870           31751 :                 if (var.ensured) {
         871             194 :                     JS_ASSERT(var.lifetime);
         872             194 :                     if (var.lifetime->start == offset)
         873             193 :                         var.ensured = false;
         874                 :                 }
         875                 :             }
         876           15229 :             break;
         877                 :
         878                 :           case JSOP_NEW:
         879                 :           case JSOP_CALL:
         880                 :           case JSOP_EVAL:
         881                 :           case JSOP_FUNAPPLY:
         882                 :           case JSOP_FUNCALL:
         883           58883 :             if (loop)
         884           14995 :                 loop->hasCallsLoops = true;
         885           58883 :             break;
         886                 :
         887                 :           default:;
         888                 :         }
         889                 :
         890         2147184 :         uint32_t type = JOF_TYPE(js_CodeSpec[op].format);
         891         2147184 :         if (type == JOF_JUMP) {
         892                 :             /*
         893                 :              * Forward jumps need to pull in all variables which are live at
         894                 :              * their target offset --- the variables live before the jump are
         895                 :              * the union of those live at the fallthrough and at the target.
         896                 :              */
         897           78962 :             uint32_t targetOffset = FollowBranch(cx, script, offset);
         898                 :
         899                 :             /*
         900                 :              * Watch for 'continue' statements in the loop body, which are
         901                 :              * jumps to the entry offset separate from the initial jump.
         902                 :              */
         903           78962 :             if (loop && loop->entry == targetOffset && loop->entry > loop->lastBlock)
         904             218 :                 loop->lastBlock = loop->entry;
         905                 :
         906           78962 :             if (targetOffset < offset) {
         907                 :                 /* This is a loop back edge, no lifetime to pull in yet. */
         908                 :
         909                 : #ifdef DEBUG
         910           11018 :                 JSOp nop = JSOp(script->code[targetOffset]);
         911           11018 :                 JS_ASSERT(nop == JSOP_LOOPHEAD);
         912                 : #endif
         913                 :
         914                 :                 /*
         915                 :                  * If we already have a loop, it is an outer loop and we
         916                 :                  * need to prune the last block in the loop --- we do not
         917                 :                  * track 'continue' statements for outer loops.
         918                 :                  */
         919           11018 :                 if (loop && loop->entry > loop->lastBlock)
         920            1440 :                     loop->lastBlock = loop->entry;
         921                 :
         922           11018 :                 LoopAnalysis *nloop = tla.new_<LoopAnalysis>();
         923           11018 :                 if (!nloop) {
         924               0 :                     cx->free_(saved);
         925               0 :                     setOOM(cx);
         926               0 :                     return;
         927                 :                 }
         928           11018 :                 PodZero(nloop);
         929                 :
         930           11018 :                 if (loop)
         931            1630 :                     loop->hasCallsLoops = true;
         932                 :
         933           11018 :                 nloop->parent = loop;
         934           11018 :                 loop = nloop;
         935                 :
         936           11018 :                 getCode(targetOffset).loop = loop;
         937           11018 :                 loop->head = targetOffset;
         938           11018 :                 loop->backedge = offset;
         939           11018 :                 loop->lastBlock = loop->head;
         940                 :
         941                 :                 /*
         942                 :                  * Find the entry jump, which will be a GOTO for 'for' or
         943                 :                  * 'while' loops or a fallthrough for 'do while' loops.
         944                 :                  */
         945           11018 :                 uint32_t entry = targetOffset;
         946           11018 :                 if (entry) {
         947           54798 :                     do {
         948           54798 :                         entry--;
         949           54798 :                     } while (!maybeCode(entry));
         950                 :
         951           11018 :                     jsbytecode *entrypc = script->code + entry;
         952                 :
         953           11018 :                     if (JSOp(*entrypc) == JSOP_GOTO || JSOp(*entrypc) == JSOP_FILTER)
         954           10945 :                         loop->entry = entry + GET_JUMP_OFFSET(entrypc);
         955                 :                     else
         956              73 :                         loop->entry = targetOffset;
         957                 :                 } else {
         958                 :                     /* Do-while loop at the start of the script. */
         959               0 :                     loop->entry = targetOffset;
         960                 :                 }
         961           21963 :                 JS_ASSERT(script->code[loop->entry] == JSOP_LOOPHEAD ||
         962           21963 :                           script->code[loop->entry] == JSOP_LOOPENTRY);
         963                 :             } else {
         964           74207 :                 for (unsigned i = 0; i < savedCount; i++) {
         965            6263 :                     LifetimeVariable &var = *saved[i];
         966            6263 :                     JS_ASSERT(!var.lifetime && var.saved);
         967            6263 :                     if (var.live(targetOffset)) {
         968                 :                         /*
         969                 :                          * Jumping to a place where this variable is live. Make a new
         970                 :                          * lifetime segment for the variable.
         971                 :                          */
         972             530 :                         var.lifetime = tla.new_<Lifetime>(offset, var.savedEnd, var.saved);
         973             530 :                         if (!var.lifetime) {
         974               0 :                             cx->free_(saved);
         975               0 :                             setOOM(cx);
         976               0 :                             return;
         977                 :                         }
         978             530 :                         var.saved = NULL;
         979             530 :                         saved[i--] = saved[--savedCount];
         980            5733 :                     } else if (loop && !var.savedEnd) {
         981                 :                         /*
         982                 :                          * This jump precedes the basic block which killed the variable,
         983                 :                          * remember it and use it for the end of the next lifetime
         984                 :                          * segment should the variable become live again. This is needed
         985                 :                          * for loops, as if we wrap liveness around the loop the isLive
         986                 :                          * test below may have given the wrong answer.
         987                 :                          */
         988             669 :                         var.savedEnd = offset;
         989                 :                     }
         990                 :                 }
         991                 :             }
         992                 :         }
         993                 :
         994         2147184 :         offset--;
         995                 :     }
         996                 :
         997           40259 :     cx->free_(saved);
         998                 :
         999           40259 :     ranLifetimes_ = true;
        1000                 : }
        1001                 :
        1002                 : #ifdef DEBUG
        1003                 : void
        1004               0 : LifetimeVariable::print() const
        1005                 : {
        1006               0 :     Lifetime *segment = lifetime ? lifetime : saved;
        1007               0 :     while (segment) {
        1008               0 :         printf(" (%u,%u%s)", segment->start, segment->end, segment->loopTail ? ",tail" : "");
        1009               0 :         segment = segment->next;
        1010                 :     }
        1011               0 :     printf("\n");
        1012               0 : }
        1013                 : #endif /* DEBUG */
        1014                 :
        1015                 : inline void
        1016           64741 : ScriptAnalysis::addVariable(JSContext *cx, LifetimeVariable &var, unsigned offset,
        1017                 :                             LifetimeVariable **&saved, unsigned &savedCount)
        1018                 : {
        1019           64741 :     if (var.lifetime) {
        1020           42070 :         if (var.ensured)
        1021              62 :             return;
        1022                 :
        1023           42008 :         JS_ASSERT(offset < var.lifetime->start);
        1024           42008 :         var.lifetime->start = offset;
        1025                 :     } else {
        1026           22671 :         if (var.saved) {
        1027                 :             /* Remove from the list of saved entries. */
        1028            8552 :             for (unsigned i = 0; i < savedCount; i++) {
        1029            8552 :                 if (saved[i] == &var) {
        1030            4698 :                     JS_ASSERT(savedCount);
        1031            4698 :                     saved[i--] = saved[--savedCount];
        1032            4698 :                     break;
        1033                 :                 }
        1034                 :             }
        1035                 :         }
        1036           22671 :         var.lifetime = cx->typeLifoAlloc().new_<Lifetime>(offset, var.savedEnd, var.saved);
        1037           22671 :         if (!var.lifetime) {
        1038               0 :             setOOM(cx);
        1039               0 :             return;
        1040                 :         }
        1041           22671 :         var.saved = NULL;
        1042                 :     }
        1043                 : }
        1044                 :
        1045                 : inline void
        1046           42884 : ScriptAnalysis::killVariable(JSContext *cx, LifetimeVariable &var, unsigned offset,
        1047                 :                              LifetimeVariable **&saved, unsigned &savedCount)
        1048                 : {
        1049           42884 :     if (!var.lifetime) {
        1050                 :         /* Make a point lifetime indicating the write. */
        1051           32227 :         if (!var.saved)
        1052             651 :             saved[savedCount++] = &var;
        1053           32227 :         var.saved = cx->typeLifoAlloc().new_<Lifetime>(offset, var.savedEnd, var.saved);
        1054           32227 :         if (!var.saved) {
        1055               0 :             setOOM(cx);
        1056               0 :             return;
        1057                 :         }
        1058           32227 :         var.saved->write = true;
        1059           32227 :         var.savedEnd = 0;
        1060           32227 :         return;
        1061                 :     }
        1062                 :
        1063           10657 :     JS_ASSERT_IF(!var.ensured, offset < var.lifetime->start);
        1064           10657 :     unsigned start = var.lifetime->start;
        1065                 :
        1066                 :     /*
        1067                 :      * The variable is considered to be live at the bytecode which kills it
        1068                 :      * (just not at earlier bytecodes). This behavior is needed by downstream
        1069                 :      * register allocation (see FrameState::bestEvictReg).
        1070                 :      */
        1071           10657 :     var.lifetime->start = offset;
        1072           10657 :     var.lifetime->write = true;
        1073                 :
        1074           10657 :     if (var.ensured) {
        1075                 :         /*
        1076                 :          * The variable is live even before the write, due to an enclosing try
        1077                 :          * block. We need to split the lifetime to indicate there was a write.
        1078                 :          * We set the new interval's savedEnd to 0, since it will always be
        1079                 :          * adjacent to the old interval, so it never needs to be extended.
        1080                 :          */
        1081              51 :         var.lifetime = cx->typeLifoAlloc().new_<Lifetime>(start, 0, var.lifetime);
        1082              51 :         if (!var.lifetime) {
        1083               0 :             setOOM(cx);
        1084               0 :             return;
        1085                 :         }
        1086              51 :         var.lifetime->end = offset;
        1087                 :     } else {
        1088           10606 :         var.saved = var.lifetime;
        1089           10606 :         var.savedEnd = 0;
        1090           10606 :         var.lifetime = NULL;
        1091                 :
        1092           10606 :         saved[savedCount++] = &var;
        1093                 :     }
        1094                 : }
        1095                 :
        1096                 : inline void
        1097           10267 : ScriptAnalysis::extendVariable(JSContext *cx, LifetimeVariable &var,
        1098                 :                                unsigned start, unsigned end)
        1099                 : {
        1100           10267 :     JS_ASSERT(var.lifetime);
        1101           10267 :     if (var.ensured) {
        1102                 :         /*
        1103                 :          * If we are still ensured to be live, the try block must scope over
        1104                 :          * the loop, in which case the variable is already guaranteed to be
        1105                 :          * live for the entire loop.
        1106                 :          */
        1107              38 :         JS_ASSERT(var.lifetime->start < start);
        1108              38 :         return;
        1109                 :     }
        1110                 :
        1111           10229 :     var.lifetime->start = start;
        1112                 :
        1113                 :     /*
        1114                 :      * Consider this code:
        1115                 :      *
        1116                 :      *   while (...) { (#1)
        1117                 :      *       use x;    (#2)
        1118                 :      *       ...
        1119                 :      *       x = ...;  (#3)
        1120                 :      *       ...
        1121                 :      *   }             (#4)
        1122                 :      *
        1123                 :      * Just before analyzing the while statement, there would be a live range
        1124                 :      * from #1..#2 and a "point range" at #3. The job of extendVariable is to
        1125                 :      * create a new live range from #3..#4.
        1126                 :      *
        1127                 :      * However, more extensions may be required if the definition of x is
        1128                 :      * conditional. Consider the following.
        1129                 :      *
        1130                 :      *   while (...) {     (#1)
        1131                 :      *       use x;        (#2)
        1132                 :      *       ...
        1133                 :      *       if (...)      (#5)
        1134                 :      *           x = ...;  (#3)
        1135                 :      *       ...
        1136                 :      *   }                 (#4)
        1137                 :      *
        1138                 :      * Assume that x is not used after the loop. Then, before extendVariable is
        1139                 :      * run, the live ranges would be the same as before (#1..#2 and #3..#3). We
        1140                 :      * still need to create a range from #3..#4. But, since the assignment at #3
        1141                 :      * may never run, we also need to create a range from #2..#3. This is done
        1142                 :      * as follows.
        1143                 :      *
        1144                 :      * Each time we create a Lifetime, we store the start of the most recently
        1145                 :      * seen sequence of conditional code in the Lifetime's savedEnd field. So,
        1146                 :      * when creating the Lifetime at #2, we set the Lifetime's savedEnd to
        1147                 :      * #5. (The start of the most recent conditional is cached in each
        1148                 :      * variable's savedEnd field.) Consequently, extendVariable is able to
        1149                 :      * create a new interval from #2..#5 using the savedEnd field of the
        1150                 :      * existing #1..#2 interval.
        1151                 :      */
        1152                 :
        1153           10229 :     Lifetime *segment = var.lifetime;
        1154           31126 :     while (segment && segment->start < end) {
        1155           14171 :         uint32_t savedEnd = segment->savedEnd;
        1156           14171 :         if (!segment->next || segment->next->start >= end) {
        1157                 :             /*
        1158                 :              * savedEnd is only set for variables killed in the middle of the
        1159                 :              * loop. Make a tail segment connecting the last use with the
        1160                 :              * back edge.
        1161                 :              */
        1162           10229 :             if (segment->end >= end) {
        1163                 :                 /* Variable known to be live after the loop finishes. */
        1164            3503 :                 break;
        1165                 :             }
        1166            6726 :             savedEnd = end;
        1167                 :         }
        1168           10668 :         JS_ASSERT(savedEnd <= end);
        1169           10668 :         if (savedEnd > segment->end) {
        1170            6771 :             Lifetime *tail = cx->typeLifoAlloc().new_<Lifetime>(savedEnd, 0, segment->next);
        1171            6771 :             if (!tail) {
        1172               0 :                 setOOM(cx);
        1173               0 :                 return;
        1174                 :             }
        1175            6771 :             tail->start = segment->end;
        1176            6771 :             tail->loopTail = true;
        1177                 :
        1178                 :             /*
        1179                 :              * Clear the segment's saved end, but preserve in the tail if this
        1180                 :              * is the last segment in the loop and the variable is killed in an
        1181                 :              * outer loop before the backedge.
        1182                 :              */
        1183            6771 :             if (segment->savedEnd > end) {
        1184              25 :                 JS_ASSERT(savedEnd == end);
        1185              25 :                 tail->savedEnd = segment->savedEnd;
        1186                 :             }
        1187            6771 :             segment->savedEnd = 0;
        1188                 :
        1189            6771 :             segment->next = tail;
        1190            6771 :             segment = tail->next;
        1191                 :         } else {
        1192            3897 :             JS_ASSERT(segment->savedEnd == 0);
        1193            3897 :             segment = segment->next;
        1194                 :         }
        1195                 :     }
        1196                 : }
        1197                 :
        1198                 : inline void
        1199             195 : ScriptAnalysis::ensureVariable(LifetimeVariable &var, unsigned until)
        1200                 : {
        1201             195 :     JS_ASSERT(var.lifetime);
        1202                 :
        1203                 :     /*
        1204                 :      * If we are already ensured, the current range we are trying to ensure
        1205                 :      * should already be included.
        1206                 :      */
        1207             195 :     if (var.ensured) {
        1208               2 :         JS_ASSERT(var.lifetime->start <= until);
        1209               2 :         return;
        1210                 :     }
        1211                 :
        1212             193 :     JS_ASSERT(until < var.lifetime->start);
        1213             193 :     var.lifetime->start = until;
        1214             193 :     var.ensured = true;
        1215                 : }
        1216                 :
        1217                 : void
        1218           96663 : ScriptAnalysis::clearAllocations()
        1219                 : {
        1220                 :     /*
        1221                 :      * Clear out storage used for register allocations in a compilation once
        1222                 :      * that compilation has finished. Register allocations are only used for
        1223                 :      * a single compilation.
        1224                 :      */
        1225        23342968 :     for (unsigned i = 0; i < script->length; i++) {
        1226        23246305 :         Bytecode *code = maybeCode(i);
        1227        23246305 :         if (code)
        1228         8383659 :             code->allocation = NULL;
        1229                 :     }
        1230           96663 : }
        1231                 :
        1232                 : /////////////////////////////////////////////////////////////////////
        1233                 : // SSA Analysis
        1234                 : /////////////////////////////////////////////////////////////////////
        1235                 :
        1236                 : void
        1237           40259 : ScriptAnalysis::analyzeSSA(JSContext *cx)
        1238                 : {
        1239           40259 :     JS_ASSERT(cx->compartment->activeAnalysis && !ranSSA() && !failed());
        1240                 :
        1241           40259 :     if (!ranLifetimes()) {
        1242           40259 :         analyzeLifetimes(cx);
        1243           40259 :         if (failed())
        1244               0 :             return;
        1245                 :     }
        1246                 :
        1247           40259 :     LifoAlloc &tla = cx->typeLifoAlloc();
        1248           40259 :     unsigned maxDepth = script->nslots - script->nfixed;
        1249                 :
        1250                 :     /*
        1251                 :      * Current value of each variable and stack value. Empty for missing or
        1252                 :      * untracked entries, i.e. escaping locals and arguments.
        1253                 :      */
        1254                 :     SSAValueInfo *values = (SSAValueInfo *)
        1255           40259 :         cx->calloc_((numSlots + maxDepth) * sizeof(SSAValueInfo));
        1256           40259 :     if (!values) {
        1257               0 :         setOOM(cx);
        1258               0 :         return;
        1259                 :     }
        1260                 :     struct FreeSSAValues {
        1261                 :         JSContext *cx;
        1262                 :         SSAValueInfo *values;
        1263           40259 :         FreeSSAValues(JSContext *cx, SSAValueInfo *values) : cx(cx), values(values) {}
        1264           40259 :         ~FreeSSAValues() { cx->free_(values); }
        1265           80518 :     } free(cx, values);
        1266                 :
        1267           40259 :     SSAValueInfo *stack = values + numSlots;
        1268           40259 :     uint32_t stackDepth = 0;
        1269                 :
        1270          120175 :     for (uint32_t slot = ArgSlot(0); slot < numSlots; slot++) {
        1271           79916 :         if (trackSlot(slot))
        1272           12959 :             values[slot].v.initInitial(slot);
        1273                 :     }
        1274                 :
        1275                 :     /*
        1276                 :      * All target offsets for forward jumps we have seen (including ones whose
        1277                 :      * target we have advanced past). We lazily add pending entries at these
        1278                 :      * targets for the original value of variables modified before the branch
        1279                 :      * rejoins.
        1280                 :      */
        1281           80518 :     Vector<uint32_t> branchTargets(cx);
        1282                 :
        1283                 :     /*
        1284                 :      * Subset of branchTargets which are exception handlers at future offsets.
        1285                 :      * Any new value of a variable modified before the target is reached is a
        1286                 :      * potential value at that target, along with the lazy original value.
        1287                 :      */
        1288           80518 :     Vector<uint32_t> exceptionTargets(cx);
        1289                 :
        1290           40259 :     uint32_t offset = 0;
        1291         2251207 :     while (offset < script->length) {
        1292         2170689 :         jsbytecode *pc = script->code + offset;
        1293         2170689 :         JSOp op = (JSOp)*pc;
        1294                 :
        1295         2170689 :         uint32_t successorOffset = offset + GetBytecodeLength(pc);
        1296                 :
        1297         2170689 :         Bytecode *code = maybeCode(pc);
        1298         2170689 :         if (!code) {
        1299           23505 :             offset = successorOffset;
        1300           23505 :             continue;
        1301                 :         }
        1302                 :
        1303         2147184 :         if (code->exceptionEntry) {
        1304                 :             /* Remove from exception targets list, which reflects only future targets. */
        1305           15247 :             for (size_t i = 0; i < exceptionTargets.length(); i++) {
        1306           15247 :                 if (exceptionTargets[i] == offset) {
        1307           15239 :                     exceptionTargets[i] = exceptionTargets.back();
        1308           15239 :                     exceptionTargets.popBack();
        1309           15239 :                     break;
        1310                 :                 }
        1311                 :             }
        1312                 :         }
        1313                 :
        1314         2147184 :         if (code->stackDepth > stackDepth)
        1315             274 :             PodZero(stack + stackDepth, code->stackDepth - stackDepth);
        1316         2147184 :         stackDepth = code->stackDepth;
        1317                 :
        1318         2147184 :         if (op == JSOP_LOOPHEAD && code->loop) {
        1319                 :             /*
        1320                 :              * Make sure there is a pending value array for phi nodes at the
        1321                 :              * loop head. We won't be able to clear these until we reach the
        1322                 :              * loop's back edge.
        1323                 :              *
        1324                 :              * We need phi nodes for all variables which might be modified
        1325                 :              * during the loop. This ensures that in the loop body we have
        1326                 :              * already updated state to reflect possible changes that happen
        1327                 :              * before the back edge, and don't need to go back and fix things
        1328                 :              * up when we *do* get to the back edge. This could be made lazier.
        1329                 :              *
        1330                 :              * We don't make phi nodes for values on the stack at the head of
        1331                 :              * the loop. These may be popped during the loop (i.e. for ITER
        1332                 :              * loops), but in such cases the original value is pushed back.
        1333                 :              */
        1334           11018 :             Vector<SlotValue> *&pending = code->pendingValues;
        1335           11018 :             if (!pending) {
        1336           11018 :                 pending = cx->new_< Vector<SlotValue> >(cx);
        1337           11018 :                 if (!pending) {
        1338               0 :                     setOOM(cx);
        1339                 :                     return;
        1340                 :                 }
        1341                 :             }
        1342                 :
        1343                 :             /*
        1344                 :              * Make phi nodes and update state for slots which are already in
        1345                 :              * pending from previous branches to the loop head, and which are
        1346                 :              * modified in the body of the loop.
        1347                 :              */
        1348           11018 :             for (unsigned i = 0; i < pending->length(); i++) {
        1349               0 :                 SlotValue &v = (*pending)[i];
        1350               0 :                 if (v.slot < numSlots && liveness(v.slot).firstWrite(code->loop) != UINT32_MAX) {
        1351               0 :                     if (v.value.kind() != SSAValue::PHI || v.value.phiOffset() != offset) {
        1352               0 :                         JS_ASSERT(v.value.phiOffset() < offset);
        1353               0 :                         SSAValue ov = v.value;
        1354               0 :                         if (!makePhi(cx, v.slot, offset, &ov))
        1355                 :                             return;
        1356               0 :                         insertPhi(cx, ov, v.value);
        1357               0 :                         v.value = ov;
        1358                 :                     }
        1359                 :                 }
        1360               0 :                 if (code->fallthrough || code->jumpFallthrough)
        1361               0 :                     mergeValue(cx, offset, values[v.slot].v, &v);
        1362               0 :                 mergeBranchTarget(cx, values[v.slot], v.slot, branchTargets, offset - 1);
        1363               0 :                 values[v.slot].v = v.value;
        1364                 :             }
        1365                 :
        1366                 :             /*
        1367                 :              * Make phi nodes for all other slots which might be modified
        1368                 :              * during the loop. This ensures that in the loop body we have
        1369                 :              * already updated state to reflect possible changes that happen
        1370                 :              * before the back edge, and don't need to go back and fix things
        1371                 :              * up when we *do* get to the back edge. This could be made lazier.
        1372                 :              */
        1373           64497 :             for (uint32_t slot = ArgSlot(0); slot < numSlots + stackDepth; slot++) {
        1374           53479 :                 if (slot >= numSlots || !trackSlot(slot))
        1375           38399 :                     continue;
        1376           15080 :                 if (liveness(slot).firstWrite(code->loop) == UINT32_MAX)
        1377            9372 :                     continue;
        1378            5708 :                 if (values[slot].v.kind() == SSAValue::PHI && values[slot].v.phiOffset() == offset) {
        1379                 :                     /* There is already a pending entry for this slot. */
        1380               0 :                     continue;
        1381                 :                 }
        1382                 :                 SSAValue ov;
        1383            5708 :                 if (!makePhi(cx, slot, offset, &ov))
        1384                 :                     return;
        1385            5708 :                 if (code->fallthrough || code->jumpFallthrough)
        1386              16 :                     insertPhi(cx, ov, values[slot].v);
        1387            5708 :                 mergeBranchTarget(cx, values[slot], slot, branchTargets, offset - 1);
        1388            5708 :                 values[slot].v = ov;
        1389            5708 :                 if (!pending->append(SlotValue(slot, ov))) {
        1390               0 :                     setOOM(cx);
        1391                 :                     return;
        1392                 :                 }
        1393           11018 :             }
        1394         2136166 :         } else if (code->pendingValues) {
        1395                 :             /*
        1396                 :              * New values at this point from a previous jump to this bytecode.
        1397                 :              * If there is fallthrough from the previous instruction, merge
        1398                 :              * with the current state and create phi nodes where necessary,
        1399                 :              * otherwise replace current values with the new values.
        1400                 :              *
        1401                 :              * Catch blocks are artifically treated as having fallthrough, so
        1402                 :              * that values written inside the block but not subsequently
        1403                 :              * overwritten are picked up.
        1404                 :              */
        1405           67663 :             bool exception = getCode(offset).exceptionEntry;
        1406           67663 :             Vector<SlotValue> *pending = code->pendingValues;
        1407          105676 :             for (unsigned i = 0; i < pending->length(); i++) {
        1408           38013 :                 SlotValue &v = (*pending)[i];
        1409           38149 :                 if (code->fallthrough || code->jumpFallthrough ||
        1410             136 :                     (exception && values[v.slot].v.kind() != SSAValue::EMPTY)) {
        1411           33147 :                     mergeValue(cx, offset, values[v.slot].v, &v);
        1412                 :                 }
        1413           38013 :                 mergeBranchTarget(cx, values[v.slot], v.slot, branchTargets, offset);
        1414           38013 :                 values[v.slot].v = v.value;
        1415                 :             }
        1416           67663 :             freezeNewValues(cx, offset);
        1417                 :         }
        1418                 :
        1419         2147184 :         if (js_CodeSpec[op].format & JOF_DECOMPOSE) {
        1420            3710 :             offset = successorOffset;
        1421            3710 :             continue;
        1422                 :         }
        1423                 :
        1424         2143474 :         unsigned nuses = GetUseCount(script, offset);
        1425         2143474 :         unsigned ndefs = GetDefCount(script, offset);
        1426         2143474 :         JS_ASSERT(stackDepth >= nuses);
        1427                 :
        1428         2143474 :         unsigned xuses = ExtendedUse(pc) ? nuses + 1 : nuses;
        1429                 :
        1430         2143474 :         if (xuses) {
        1431         1180130 :             code->poppedValues = tla.newArray<SSAValue>(xuses);
        1432         1180130 :             if (!code->poppedValues) {
        1433               0 :                 setOOM(cx);
        1434                 :                 return;
        1435                 :             }
        1436         2962093 :             for (unsigned i = 0; i < nuses; i++) {
        1437         1781963 :                 SSAValue &v = stack[stackDepth - 1 - i].v;
        1438         1781963 :                 code->poppedValues[i] = v;
        1439         1781963 :                 v.clear();
        1440                 :             }
        1441         1180130 :             if (xuses > nuses) {
        1442                 :                 /*
        1443                 :                  * For SETLOCAL, INCLOCAL, etc. opcodes, add an extra popped
        1444                 :                  * value holding the value of the local before the op.
        1445                 :                  */
        1446          226225 :                 uint32_t slot = GetBytecodeSlot(script, pc);
        1447          226225 :                 if (trackSlot(slot))
        1448           83169 :                     code->poppedValues[nuses] = values[slot].v;
        1449                 :                 else
        1450          143056 :                     code->poppedValues[nuses].clear();
        1451                 :             }
        1452                 :
        1453         1180130 :             if (xuses) {
        1454         1180130 :                 SSAUseChain *useChains = tla.newArray<SSAUseChain>(xuses);
        1455         1180130 :                 if (!useChains) {
        1456               0 :                     setOOM(cx);
        1457                 :                     return;
        1458                 :                 }
        1459         1180130 :                 PodZero(useChains, xuses);
        1460         3188318 :                 for (unsigned i = 0; i < xuses; i++) {
        1461         2008188 :                     const SSAValue &v = code->poppedValues[i];
        1462         2008188 :                     if (trackUseChain(v)) {
        1463         1833474 :                         SSAUseChain *&uses = useChain(v);
        1464         1833474 :                         useChains[i].popped = true;
        1465         1833474 :                         useChains[i].offset = offset;
        1466         1833474 :                         useChains[i].u.which = i;
        1467         1833474 :                         useChains[i].next = uses;
        1468         1833474 :                         uses = &useChains[i];
        1469                 :                     }
        1470                 :                 }
        1471                 :             }
        1472                 :         }
        1473                 :
        1474         2143474 :         stackDepth -= nuses;
        1475                 :
        1476         3926744 :         for (unsigned i = 0; i < ndefs; i++)
        1477         1783270 :             stack[stackDepth + i].v.initPushed(offset, i);
        1478                 :
        1479         2143474 :         unsigned xdefs = ExtendedDef(pc) ? ndefs + 1 : ndefs;
        1480         2143474 :         if (xdefs) {
        1481         1683250 :             code->pushedUses = tla.newArray<SSAUseChain *>(xdefs);
        1482         1683250 :             if (!code->pushedUses) {
        1483               0 :                 setOOM(cx);
        1484                 :                 return;
        1485                 :             }
        1486         1683250 :             PodZero(code->pushedUses, xdefs);
        1487                 :         }
        1488                 :
        1489         2143474 :         stackDepth += ndefs;
        1490                 :
        1491         2143474 :         if (BytecodeUpdatesSlot(op)) {
        1492          105360 :             uint32_t slot = GetBytecodeSlot(script, pc);
        1493          105360 :             if (trackSlot(slot)) {
        1494           42871 :                 mergeBranchTarget(cx, values[slot], slot, branchTargets, offset);
        1495           42871 :                 mergeExceptionTarget(cx, values[slot].v, slot, exceptionTargets);
        1496           42871 :                 values[slot].v.initWritten(slot, offset);
        1497                 :             }
        1498                 :         }
        1499                 :
        1500         2143474 :         switch (op) {
        1501                 :           case JSOP_GETARG:
        1502                 :           case JSOP_GETLOCAL: {
        1503          119128 :             uint32_t slot = GetBytecodeSlot(script, pc);
        1504          119128 :             if (trackSlot(slot)) {
        1505                 :                 /*
        1506                 :                  * Propagate the current value of the local to the pushed value,
        1507                 :                  * and remember it with an extended use on the opcode.
        1508                 :                  */
        1509           39766 :                 stack[stackDepth - 1].v = code->poppedValues[0] = values[slot].v;
        1510                 :             }
        1511          119128 :             break;
        1512                 :           }
        1513                 :
        1514                 :           /* Short circuit ops which push back one of their operands. */
        1515                 :
        1516                 :           case JSOP_MOREITER:
        1517            1627 :             stack[stackDepth - 2].v = code->poppedValues[0];
        1518            1627 :             break;
        1519                 :
        1520                 :           case JSOP_INITPROP:
        1521            5405 :             stack[stackDepth - 1].v = code->poppedValues[1];
        1522            5405 :             break;
        1523                 :
        1524                 :           case JSOP_INITELEM:
        1525           11569 :             stack[stackDepth - 1].v = code->poppedValues[2];
        1526           11569 :             break;
        1527                 :
        1528                 :           case JSOP_DUP:
        1529           21506 :             stack[stackDepth - 1].v = stack[stackDepth - 2].v = code->poppedValues[0];
        1530           21506 :             break;
        1531                 :
        1532                 :           case JSOP_DUP2:
        1533             312 :             stack[stackDepth - 1].v = stack[stackDepth - 3].v = code->poppedValues[0];
        1534             312 :             stack[stackDepth - 2].v = stack[stackDepth - 4].v = code->poppedValues[1];
        1535             312 :             break;
        1536                 :
        1537                 :           case JSOP_SWAP:
        1538                 :             /* Swap is like pick 1. */
        1539                 :           case JSOP_PICK: {
        1540           22453 :             unsigned pickedDepth = (op == JSOP_SWAP ? 1 : pc[1]);
        1541           22453 :             stack[stackDepth - 1].v = code->poppedValues[pickedDepth];
        1542           48098 :             for (unsigned i = 0; i < pickedDepth; i++)
        1543           25645 :                 stack[stackDepth - 2 - i].v = code->poppedValues[i];
        1544           22453 :             break;
        1545                 :           }
        1546                 :
        1547                 :           /*
        1548                 :            * Switch and try blocks preserve the stack between the original op
        1549                 :            * and all case statements or exception/finally handlers.
        1550                 :            */
        1551                 :
        1552                 :           case JSOP_TABLESWITCH: {
        1553             104 :             unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
        1554             104 :             jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
        1555             104 :             int32_t low = GET_JUMP_OFFSET(pc2);
        1556             104 :             pc2 += JUMP_OFFSET_LEN;
        1557             104 :             int32_t high = GET_JUMP_OFFSET(pc2);
        1558             104 :             pc2 += JUMP_OFFSET_LEN;
        1559                 :
        1560             402 :             for (int32_t i = low; i <= high; i++) {
        1561             298 :                 unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
        1562             298 :                 if (targetOffset != offset)
        1563             290 :                     checkBranchTarget(cx, targetOffset, branchTargets, values, stackDepth);
        1564             298 :                 pc2 += JUMP_OFFSET_LEN;
        1565                 :             }
        1566                 :
        1567             104 :             checkBranchTarget(cx, defaultOffset, branchTargets, values, stackDepth);
        1568             104 :             break;
        1569                 :           }
        1570                 :
        1571                 :           case JSOP_LOOKUPSWITCH: {
        1572              26 :             unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
        1573              26 :             jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
        1574              26 :             unsigned npairs = GET_UINT16(pc2);
        1575              26 :             pc2 += UINT16_LEN;
        1576                 :
        1577             164 :             while (npairs) {
        1578             112 :                 pc2 += UINT32_INDEX_LEN;
        1579             112 :                 unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
        1580             112 :                 checkBranchTarget(cx, targetOffset, branchTargets, values, stackDepth);
        1581             112 :                 pc2 += JUMP_OFFSET_LEN;
        1582             112 :                 npairs--;
        1583                 :             }
        1584                 :
        1585              26 :             checkBranchTarget(cx, defaultOffset, branchTargets, values, stackDepth);
        1586              26 :             break;
        1587                 :           }
        1588                 :
        1589                 :           case JSOP_TRY: {
        1590           15229 :             JSTryNote *tn = script->trynotes()->vector;
        1591           15229 :             JSTryNote *tnlimit = tn + script->trynotes()->length;
        1592        63630299 :             for (; tn < tnlimit; tn++) {
        1593        63615070 :                 unsigned startOffset = script->mainOffset + tn->start;
        1594        63615070 :                 if (startOffset == offset + 1) {
        1595           15239 :                     unsigned catchOffset = startOffset + tn->length;
        1596                 :
        1597           15239 :                     if (tn->kind != JSTRY_ITER) {
        1598           15239 :                         checkBranchTarget(cx, catchOffset, branchTargets, values, stackDepth);
        1599           15239 :                         checkExceptionTarget(cx, catchOffset, exceptionTargets);
        1600                 :                     }
        1601                 :                 }
        1602                 :             }
        1603           15229 :             break;
        1604                 :           }
        1605                 :
        1606                 :           case JSOP_THROW:
        1607                 :           case JSOP_RETURN:
        1608                 :           case JSOP_STOP:
        1609                 :           case JSOP_RETRVAL:
        1610           42018 :             mergeAllExceptionTargets(cx, values, exceptionTargets);
        1611           42018 :             break;
        1612                 :
        1613                 :           default:;
        1614                 :         }
        1615                 :
        1616         2143474 :         uint32_t type = JOF_TYPE(js_CodeSpec[op].format);
        1617         2143474 :         if (type == JOF_JUMP) {
        1618           78962 :             unsigned targetOffset = FollowBranch(cx, script, offset);
        1619           78962 :             checkBranchTarget(cx, targetOffset, branchTargets, values, stackDepth);
        1620                 :
        1621                 :             /*
        1622                 :              * If this is a back edge, we're done with the loop and can freeze
        1623                 :              * the phi values at the head now.
        1624                 :              */
        1625           78962 :             if (targetOffset < offset)
        1626           11018 :                 freezeNewValues(cx, targetOffset);
        1627                 :         }
        1628                 :
        1629         2143474 :         offset = successorOffset;
        1630                 :     }
        1631                 :
        1632           40259 :     ranSSA_ = true;
        1633                 :
        1634                 :     /*
        1635                 :      * Now that we have full SSA information for the script, analyze whether
        1636                 :      * the arguments object is actually needed. The first pass performed by the
        1637                 :      * frontend just looked for the 'arguments' keyword. Here, we can see how
        1638                 :      * 'arguments' is used and optimize several cases where we can read values
        1639                 :      * from the stack frame directly.
        1640                 :      */
        1641           40259 :     if (script->analyzedArgsUsage())
        1642                 :         return;
        1643                 :
        1644                 :     /* Ensured by analyzeBytecode. */
        1645            2196 :     JS_ASSERT(script->function());
        1646            2196 :     JS_ASSERT(script->mayNeedArgsObj());
        1647            2196 :     JS_ASSERT(!script->usesEval);
        1648                 :
        1649                 :     /*
        1650                 :      * Since let variables are not tracked, we cannot soundly perform this
        1651                 :      * analysis in their presence.
        1652                 :      */
        1653            2196 :     if (localsAliasStack()) {
        1654              45 :         script->setNeedsArgsObj(true);
        1655                 :         return;
        1656                 :     }
        1657                 :
        1658                 :     /*
        1659                 :      * In the case of 'f.apply(x, arguments)', we want to avoid creating
        1660                 :      * 'arguments' eagerly: 'f.apply' can read directly out of the frame.
        1661                 :      * However, if 'f.apply' turns out to not be Function.prototype.apply, we
        1662                 :      * need to set flip script->needsArgsObj and fix up all stack frames. To
        1663                 :      * avoid a full stack scan (to find outstanding JS_OPTIMIZED_APPLY magic
        1664                 :      * values), we only apply this optimization when there are no other uses of
        1665                 :      * 'arguments' in the function. See Script::applySpeculationFailed.
        1666                 :      * Also, to simplify logic involving closed-over variables and call
        1667                 :      * objects, we skip the optimization for heavyweight functions.
        1668                 :      */
        1669            2151 :     bool canOptimizeApply = !script->function()->isHeavyweight();
        1670            2151 :     bool haveOptimizedApply = false;
        1671                 :
        1672                 :     jsbytecode *pc;
        1673           22485 :     for (offset = 0; offset < script->length; offset += GetBytecodeLength(pc)) {
        1674           21327 :         pc = script->code + offset;
        1675                 :
        1676                 :         /* Ensured by NewScriptFromEmitter. */
        1677           21327 :         JS_ASSERT_IF(script->strictModeCode, *pc != JSOP_SETARG);
        1678                 :
        1679                 :         /* The front-end took care of dynamic ways to name 'arguments'. */
        1680           21327 :         if (JSOp(*pc) != JSOP_ARGUMENTS)
        1681           18555 :             continue;
        1682                 :
        1683                 :         /* A null Bytecode* means unreachable. */
        1684            2772 :         if (!maybeCode(offset))
        1685               0 :             continue;
        1686                 :
        1687            2772 :         if (SpeculateApplyOptimization(pc) && canOptimizeApply) {
        1688             144 :             haveOptimizedApply = true;
        1689             144 :             continue;
        1690                 :         }
        1691                 :
        1692            5256 :         Vector<SSAValue> seen(cx);
        1693            5256 :         if (haveOptimizedApply ||
        1694            2628 :             !followEscapingArguments(cx, SSAValue::PushedValue(offset, 0), &seen))
        1695                 :         {
        1696             993 :             script->setNeedsArgsObj(true);
        1697                 :             return;
        1698                 :         }
        1699                 :
        1700            4263 :         canOptimizeApply = false;
        1701                 :     }
        1702                 :
        1703           41417 :     script->setNeedsArgsObj(false);
        1704                 : }
        1705                 :
        1706                 : /* Get a phi node's capacity for a given length. */
        1707                 : static inline unsigned
        1708           40210 : PhiNodeCapacity(unsigned length)
        1709                 : {
        1710           40210 :     if (length <= 4)
        1711           40191 :         return 4;
        1712                 :
        1713                 :     unsigned log2;
        1714              19 :     JS_FLOOR_LOG2(log2, length - 1);
        1715              19 :     return 1 << (log2 + 1);
        1716                 : }
        1717                 :
        1718                 : bool
        1719           15179 : ScriptAnalysis::makePhi(JSContext *cx, uint32_t slot, uint32_t offset, SSAValue *pv)
        1720                 : {
        1721           15179 :     SSAPhiNode *node = cx->typeLifoAlloc().new_<SSAPhiNode>();
        1722           15179 :     SSAValue *options = cx->typeLifoAlloc().newArray<SSAValue>(PhiNodeCapacity(0));
        1723           15179 :     if (!node || !options) {
        1724               0 :         setOOM(cx);
        1725               0 :         return false;
        1726                 :     }
        1727           15179 :     node->slot = slot;
        1728           15179 :     node->options = options;
        1729           15179 :     pv->initPhi(offset, node);
        1730           15179 :     return true;
        1731                 : }
        1732                 :
        1733                 : void
        1734           25185 : ScriptAnalysis::insertPhi(JSContext *cx, SSAValue &phi, const SSAValue &v)
        1735                 : {
        1736           25185 :     JS_ASSERT(phi.kind() == SSAValue::PHI);
        1737           25185 :     SSAPhiNode *node = phi.phiNode();
        1738                 :
        1739                 :     /*
        1740                 :      * Filter dupes inserted into small nodes to keep things clean and avoid
        1741                 :      * extra type constraints, but don't bother on large phi nodes to avoid
        1742                 :      * quadratic behavior.
        1743                 :      */
        1744           25185 :     if (node->length <= 8) {
        1745           35474 :         for (unsigned i = 0; i < node->length; i++) {
        1746           10460 :             if (v == node->options[i])
        1747             171 :                 return;
        1748                 :         }
        1749                 :     }
        1750                 :
        1751           25014 :     if (trackUseChain(v)) {
        1752           23019 :         SSAUseChain *&uses = useChain(v);
        1753                 :
        1754           23019 :         SSAUseChain *use = cx->typeLifoAlloc().new_<SSAUseChain>();
        1755           23019 :         if (!use) {
        1756               0 :             setOOM(cx);
        1757               0 :             return;
        1758                 :         }
        1759                 :
        1760           23019 :         use->popped = false;
        1761           23019 :         use->offset = phi.phiOffset();
        1762           23019 :         use->u.phi = node;
        1763           23019 :         use->next = uses;
        1764           23019 :         uses = use;
        1765                 :     }
        1766                 :
        1767           25014 :     if (node->length < PhiNodeCapacity(node->length)) {
        1768           24997 :         node->options[node->length++] = v;
        1769           24997 :         return;
        1770                 :     }
        1771                 :
        1772                 :     SSAValue *newOptions =
        1773              17 :         cx->typeLifoAlloc().newArray<SSAValue>(PhiNodeCapacity(node->length + 1));
        1774              17 :     if (!newOptions) {
        1775               0 :         setOOM(cx);
        1776               0 :         return;
        1777                 :     }
        1778                 :
        1779              17 :     PodCopy(newOptions, node->options, node->length);
        1780              17 :     node->options = newOptions;
        1781              17 :     node->options[node->length++] = v;
        1782                 : }
        1783                 :
        1784                 : inline void
        1785           40024 : ScriptAnalysis::mergeValue(JSContext *cx, uint32_t offset, const SSAValue &v, SlotValue *pv)
        1786                 : {
        1787                 :     /* Make sure that v is accounted for in the pending value or phi value at pv. */
        1788           40024 :     JS_ASSERT(v.kind() != SSAValue::EMPTY && pv->value.kind() != SSAValue::EMPTY);
        1789                 :
        1790           40024 :     if (v == pv->value)
        1791           24326 :         return;
        1792                 :
        1793           15698 :     if (pv->value.kind() != SSAValue::PHI || pv->value.phiOffset() < offset) {
        1794            9471 :         SSAValue ov = pv->value;
        1795            9471 :         if (makePhi(cx, pv->slot, offset, &pv->value)) {
        1796            9471 :             insertPhi(cx, pv->value, v);
        1797            9471 :             insertPhi(cx, pv->value, ov);
        1798                 :         }
        1799            9471 :         return;
        1800                 :     }
        1801                 :
        1802            6227 :     JS_ASSERT(pv->value.phiOffset() == offset);
        1803            6227 :     insertPhi(cx, pv->value, v);
        1804                 : }
        1805                 :
        1806                 : void
        1807           58626 : ScriptAnalysis::checkPendingValue(JSContext *cx, const SSAValue &v, uint32_t slot,
        1808                 :                                   Vector<SlotValue> *pending)
        1809                 : {
        1810           58626 :     JS_ASSERT(v.kind() != SSAValue::EMPTY);
        1811                 :
        1812        67206591 :     for (unsigned i = 0; i < pending->length(); i++) {
        1813        67148387 :         if ((*pending)[i].slot == slot)
        1814             422 :             return;
        1815                 :     }
        1816                 :
        1817           58204 :     if (!pending->append(SlotValue(slot, v)))
        1818               0 :         setOOM(cx);
        1819                 : }
        1820                 :
        1821                 : void
        1822           94733 : ScriptAnalysis::checkBranchTarget(JSContext *cx, uint32_t targetOffset,
        1823                 :                                   Vector<uint32_t> &branchTargets,
        1824                 :                                   SSAValueInfo *values, uint32_t stackDepth)
        1825                 : {
        1826           94733 :     unsigned targetDepth = getCode(targetOffset).stackDepth;
        1827           94733 :     JS_ASSERT(targetDepth <= stackDepth);
        1828                 :
        1829                 :     /*
        1830                 :      * If there is already an active branch to target, make sure its pending
        1831                 :      * values reflect any changes made since the first branch. Otherwise, add a
        1832                 :      * new pending branch and determine its pending values lazily.
        1833                 :      */
        1834           94733 :     Vector<SlotValue> *&pending = getCode(targetOffset).pendingValues;
        1835           94733 :     if (pending) {
        1836           33816 :         for (unsigned i = 0; i < pending->length(); i++) {
        1837            6746 :             SlotValue &v = (*pending)[i];
        1838            6746 :             mergeValue(cx, targetOffset, values[v.slot].v, &v);
        1839                 :         }
        1840                 :     } else {
        1841           67663 :         pending = cx->new_< Vector<SlotValue> >(cx);
        1842           67663 :         if (!pending || !branchTargets.append(targetOffset)) {
        1843               0 :             setOOM(cx);
        1844               0 :             return;
        1845                 :         }
        1846                 :     }
        1847                 :
        1848                 :     /*
        1849                 :      * Make sure there is a pending entry for each value on the stack.
        1850                 :      * The number of stack entries at join points is usually zero, and
        1851                 :      * we don't want to look at the active branches while popping and
        1852                 :      * pushing values in each opcode.
        1853                 :      */
        1854          146263 :     for (unsigned i = 0; i < targetDepth; i++) {
        1855           51530 :         uint32_t slot = StackSlot(script, i);
        1856           51530 :         checkPendingValue(cx, values[slot].v, slot, pending);
        1857                 :     }
        1858                 : }
        1859                 :
        1860                 : void
        1861           15239 : ScriptAnalysis::checkExceptionTarget(JSContext *cx, uint32_t catchOffset,
        1862                 :                                      Vector<uint32_t> &exceptionTargets)
        1863                 : {
        1864           15239 :     JS_ASSERT(getCode(catchOffset).exceptionEntry);
        1865                 :
        1866                 :     /*
        1867                 :      * The catch offset will already be in the branch targets, just check
        1868                 :      * whether this is already a known exception target.
        1869                 :      */
        1870           15257 :     for (unsigned i = 0; i < exceptionTargets.length(); i++) {
        1871              18 :         if (exceptionTargets[i] == catchOffset)
        1872               0 :             return;
        1873                 :     }
        1874           15239 :     if (!exceptionTargets.append(catchOffset))
        1875               0 :         setOOM(cx);
        1876                 : }
        1877                 :
        1878                 : void
        1879           86592 : ScriptAnalysis::mergeBranchTarget(JSContext *cx, SSAValueInfo &value, uint32_t slot,
        1880                 :                                   const Vector<uint32_t> &branchTargets, uint32_t currentOffset)
        1881                 : {
        1882           86592 :     if (slot >= numSlots) {
        1883                 :         /*
        1884                 :          * There is no need to lazily check that there are pending values at
        1885                 :          * branch targets for slots on the stack, these are added to pending
        1886                 :          * eagerly.
        1887                 :          */
        1888           30917 :         return;
        1889                 :     }
        1890                 :
        1891           55675 :     JS_ASSERT(trackSlot(slot));
        1892                 :
        1893                 :     /*
        1894                 :      * Before changing the value of a variable, make sure the old value is
        1895                 :      * marked at the target of any branches jumping over the current opcode.
        1896                 :      * Only look at new branch targets which have appeared since the last time
        1897                 :      * the variable was written.
        1898                 :      */
        1899           70183 :     for (int i = branchTargets.length() - 1; i >= value.branchSize; i--) {
        1900           14508 :         if (branchTargets[i] <= currentOffset)
        1901            7412 :             continue;
        1902                 :
        1903            7096 :         const Bytecode &code = getCode(branchTargets[i]);
        1904                 :
        1905            7096 :         Vector<SlotValue> *pending = code.pendingValues;
        1906            7096 :         checkPendingValue(cx, value.v, slot, pending);
        1907                 :     }
        1908                 :
        1909           55675 :     value.branchSize = branchTargets.length();
        1910                 : }
        1911                 :
        1912                 : void
        1913           42881 : ScriptAnalysis::mergeExceptionTarget(JSContext *cx, const SSAValue &value, uint32_t slot,
        1914                 :                                      const Vector<uint32_t> &exceptionTargets)
        1915                 : {
        1916           42881 :     JS_ASSERT(trackSlot(slot));
        1917                 :
        1918                 :     /*
        1919                 :      * Update the value at exception targets with the value of a variable
        1920                 :      * before it is overwritten. Unlike mergeBranchTarget, this is done whether
        1921                 :      * or not the overwritten value is the value of the variable at the
        1922                 :      * original branch. Values for a variable which are written after the
        1923                 :      * try block starts and overwritten before it is finished can still be
        1924                 :      * seen at exception handlers via exception paths.
        1925                 :      */
        1926           43012 :     for (unsigned i = 0; i < exceptionTargets.length(); i++) {
        1927             131 :         unsigned offset = exceptionTargets[i];
        1928             131 :         Vector<SlotValue> *pending = getCode(offset).pendingValues;
        1929                 :
        1930             131 :         bool duplicate = false;
        1931             176 :         for (unsigned i = 0; i < pending->length(); i++) {
        1932             176 :             if ((*pending)[i].slot == slot) {
        1933             131 :                 duplicate = true;
        1934             131 :                 SlotValue &v = (*pending)[i];
        1935             131 :                 mergeValue(cx, offset, value, &v);
        1936             131 :                 break;
        1937                 :             }
        1938                 :         }
        1939                 :
        1940             131 :         if (!duplicate && !pending->append(SlotValue(slot, value)))
        1941               0 :             setOOM(cx);
        1942                 :     }
        1943           42881 : }
        1944                 :
        1945                 : void
        1946           42018 : ScriptAnalysis::mergeAllExceptionTargets(JSContext *cx, SSAValueInfo *values,
        1947                 :                                          const Vector<uint32_t> &exceptionTargets)
        1948                 : {
        1949           42140 :     for (unsigned i = 0; i < exceptionTargets.length(); i++) {
        1950             122 :         Vector<SlotValue> *pending = getCode(exceptionTargets[i]).pendingValues;
        1951             144 :         for (unsigned i = 0; i < pending->length(); i++) {
        1952              22 :             const SlotValue &v = (*pending)[i];
        1953              22 :             if (trackSlot(v.slot))
        1954              10 :                 mergeExceptionTarget(cx, values[v.slot].v, v.slot, exceptionTargets);
        1955                 :         }
        1956                 :     }
        1957           42018 : }
        1958                 :
        1959                 : void
        1960           78681 : ScriptAnalysis::freezeNewValues(JSContext *cx, uint32_t offset)
        1961                 : {
        1962           78681 :     Bytecode &code = getCode(offset);
        1963                 :
        1964           78681 :     Vector<SlotValue> *pending = code.pendingValues;
        1965           78681 :     code.pendingValues = NULL;
        1966                 :
        1967           78681 :     unsigned count = pending->length();
        1968           78681 :     if (count == 0) {
        1969           62258 :         cx->delete_(pending);
        1970           62258 :         return;
        1971                 :     }
        1972                 :
        1973           16423 :     code.newValues = cx->typeLifoAlloc().newArray<SlotValue>(count + 1);
        1974           16423 :     if (!code.newValues) {
        1975               0 :         setOOM(cx);
        1976               0 :         return;
        1977                 :     }
        1978                 :
        1979           80335 :     for (unsigned i = 0; i < count; i++)
        1980           63912 :         code.newValues[i] = (*pending)[i];
        1981           16423 :     code.newValues[count].slot = 0;
        1982           16423 :     code.newValues[count].value.clear();
        1983                 :
        1984           16423 :     cx->delete_(pending);
        1985                 : }
        1986                 :
        1987                 : bool
        1988            3273 : ScriptAnalysis::followEscapingArguments(JSContext *cx, const SSAValue &v, Vector<SSAValue> *seen)
        1989                 : {
        1990                 :     /*
        1991                 :      * trackUseChain is false for initial values of variables, which
        1992                 :      * cannot hold the script's arguments object.
        1993                 :      */
        1994            3273 :     if (!trackUseChain(v))
        1995               0 :         return true;
        1996                 :
        1997            4950 :     for (unsigned i = 0; i < seen->length(); i++) {
        1998            1761 :         if (v == (*seen)[i])
        1999              84 :             return true;
        2000                 :     }
        2001            3189 :     if (!seen->append(v)) {
        2002               0 :         cx->compartment->types.setPendingNukeTypes(cx);
        2003               0 :         return false;
        2004                 :     }
        2005                 :
        2006            3189 :     SSAUseChain *use = useChain(v);
        2007            8643 :     while (use) {
        2008            3333 :         if (!followEscapingArguments(cx, use, seen))
        2009            1068 :             return false;
        2010            2265 :         use = use->next;
        2011                 :     }
        2012                 :
        2013            2121 :     return true;
        2014                 : }
        2015                 :
        2016                 : bool
        2017            3333 : ScriptAnalysis::followEscapingArguments(JSContext *cx, SSAUseChain *use, Vector<SSAValue> *seen)
        2018                 : {
        2019            3333 :     if (!use->popped)
        2020             111 :         return followEscapingArguments(cx, SSAValue::PhiValue(use->offset, use->u.phi), seen);
        2021                 :
        2022            3222 :     jsbytecode *pc = script->code + use->offset;
        2023            3222 :     uint32_t which = use->u.which;
        2024                 :
        2025            3222 :     JSOp op = JSOp(*pc);
        2026                 :
        2027            3222 :     if (op == JSOP_POP || op == JSOP_POPN)
        2028             132 :         return true;
        2029                 :
        2030                 :     /* arguments[i] can read fp->canonicalActualArg(i) directly. */
        2031            3090 :     if (op == JSOP_GETELEM && which == 1)
        2032            1338 :         return true;
        2033                 :
        2034                 :     /* arguments.length length can read fp->numActualArgs() directly. */
        2035            1752 :     if (op == JSOP_LENGTH)
        2036             399 :         return true;
        2037                 :
        2038                 :     /* Allow assignments to non-closed locals (but not arguments). */
        2039                 :
        2040            1353 :     if (op == JSOP_SETLOCAL) {
        2041             342 :         uint32_t slot = GetBytecodeSlot(script, pc);
        2042             342 :         if (!trackSlot(slot) || script->strictModeCode)
        2043             162 :             return false;
        2044             180 :         if (!followEscapingArguments(cx, SSAValue::PushedValue(use->offset, 0), seen))
        2045               6 :             return false;
        2046             174 :         return followEscapingArguments(cx, SSAValue::WrittenVar(slot, use->offset), seen);
        2047                 :     }
        2048                 :
        2049            1011 :     if (op == JSOP_GETLOCAL)
        2050             180 :         return followEscapingArguments(cx, SSAValue::PushedValue(use->offset, 0), seen);
        2051                 :
        2052             831 :     return false;
        2053                 : }
        2054                 :
        2055                 : CrossSSAValue
        2056           25575 : CrossScriptSSA::foldValue(const CrossSSAValue &cv)
        2057                 : {
        2058           25575 :     const Frame &frame = getFrame(cv.frame);
        2059           25575 :     const SSAValue &v = cv.v;
        2060                 :
        2061           25575 :     JSScript *parentScript = NULL;
        2062           25575 :     ScriptAnalysis *parentAnalysis = NULL;
        2063           25575 :     if (frame.parent != INVALID_FRAME) {
        2064             730 :         parentScript = getFrame(frame.parent).script;
        2065             730 :         parentAnalysis = parentScript->analysis();
        2066                 :     }
        2067                 :
        2068           25575 :     if (v.kind() == SSAValue::VAR && v.varInitial() && parentScript) {
        2069              98 :         uint32_t slot = v.varSlot();
        2070              98 :         if (slot >= ArgSlot(0) && slot < LocalSlot(frame.script, 0)) {
        2071              98 :             uint32_t argc = GET_ARGC(frame.parentpc);
        2072              98 :             SSAValue argv = parentAnalysis->poppedValue(frame.parentpc, argc - 1 - (slot - ArgSlot(0)));
        2073              98 :             return foldValue(CrossSSAValue(frame.parent, argv));
        2074                 :         }
        2075                 :     }
        2076                 :
        2077           25477 :     if (v.kind() == SSAValue::PUSHED) {
        2078           12826 :         jsbytecode *pc = frame.script->code + v.pushedOffset();
        2079                 :
        2080           12826 :         switch (JSOp(*pc)) {
        2081                 :           case JSOP_THIS:
        2082             609 :             if (parentScript) {
        2083             349 :                 uint32_t argc = GET_ARGC(frame.parentpc);
        2084             349 :                 SSAValue thisv = parentAnalysis->poppedValue(frame.parentpc, argc);
        2085             349 :                 return foldValue(CrossSSAValue(frame.parent, thisv));
        2086                 :             }
        2087             260 :             break;
        2088                 :
        2089                 :           case JSOP_CALL: {
        2090                 :             /*
        2091                 :              * If there is a single inline callee with a single return site,
        2092                 :              * propagate back to that.
        2093                 :              */
        2094              73 :             JSScript *callee = NULL;
        2095              73 :             uint32_t calleeFrame = INVALID_FRAME;
        2096             607 :             for (unsigned i = 0; i < numFrames(); i++) {
        2097             534 :                 if (iterFrame(i).parent == cv.frame && iterFrame(i).parentpc == pc) {
        2098              65 :                     if (callee)
        2099               0 :                         return cv;  /* Multiple callees */
        2100              65 :                     callee = iterFrame(i).script;
        2101              65 :                     calleeFrame = iterFrame(i).index;
        2102                 :                 }
        2103                 :             }
        2104              73 :             if (callee && callee->analysis()->numReturnSites() == 1) {
        2105              63 :                 ScriptAnalysis *analysis = callee->analysis();
        2106              63 :                 uint32_t offset = 0;
        2107             354 :                 while (offset < callee->length) {
        2108             291 :                     jsbytecode *pc = callee->code + offset;
        2109             291 :                     if (analysis->maybeCode(pc) && JSOp(*pc) == JSOP_RETURN)
        2110              63 :                         return foldValue(CrossSSAValue(calleeFrame, analysis->poppedValue(pc, 0)));
        2111             228 :                     offset += GetBytecodeLength(pc);
        2112                 :                 }
        2113                 :             }
        2114              10 :             break;
        2115                 :           }
        2116                 :
        2117                 :           case JSOP_TOID: {
        2118                 :             /*
        2119                 :              * TOID acts as identity for integers, so to get better precision
        2120                 :              * we should propagate its popped values forward if it acted as
        2121                 :              * identity.
        2122                 :              */
        2123             344 :             ScriptAnalysis *analysis = frame.script->analysis();
        2124             344 :             SSAValue toidv = analysis->poppedValue(pc, 0);
        2125             344 :             if (analysis->getValueTypes(toidv)->getKnownTypeTag(cx) == JSVAL_TYPE_INT32)
        2126             338 :                 return foldValue(CrossSSAValue(cv.frame, toidv));
        2127               6 :             break;
        2128                 :           }
        2129                 :
        2130                 :           default:;
        2131                 :         }
        2132                 :     }
        2133                 :
        2134           24727 :     return cv;
        2135                 : }
        2136                 :
        2137                 : #ifdef DEBUG
        2138                 :
        2139                 : void
        2140               0 : ScriptAnalysis::printSSA(JSContext *cx)
        2141                 : {
        2142               0 :     AutoEnterAnalysis enter(cx);
        2143                 :
        2144               0 :     printf("\n");
        2145                 :
        2146               0 :     for (unsigned offset = 0; offset < script->length; offset++) {
        2147               0 :         Bytecode *code = maybeCode(offset);
        2148               0 :         if (!code)
        2149               0 :             continue;
        2150                 :
        2151               0 :         jsbytecode *pc = script->code + offset;
        2152                 :
        2153               0 :         PrintBytecode(cx, script, pc);
        2154                 :
        2155               0 :         SlotValue *newv = code->newValues;
        2156               0 :         if (newv) {
        2157               0 :             while (newv->slot) {
        2158               0 :                 if (newv->value.kind() != SSAValue::PHI || newv->value.phiOffset() != offset) {
        2159               0 :                     newv++;
        2160               0 :                     continue;
        2161                 :                 }
        2162               0 :                 printf("  phi ");
        2163               0 :                 newv->value.print();
        2164               0 :                 printf(" [");
        2165               0 :                 for (unsigned i = 0; i < newv->value.phiLength(); i++) {
        2166               0 :                     if (i)
        2167               0 :                         printf(",");
        2168               0 :                     newv->value.phiValue(i).print();
        2169                 :                 }
        2170               0 :                 printf("]\n");
        2171               0 :                 newv++;
        2172                 :             }
        2173                 :         }
        2174                 :
        2175               0 :         unsigned nuses = GetUseCount(script, offset);
        2176               0 :         unsigned xuses = ExtendedUse(pc) ? nuses + 1 : nuses;
        2177                 :
        2178               0 :         for (unsigned i = 0; i < xuses; i++) {
        2179               0 :             printf("  popped%d: ", i);
        2180               0 :             code->poppedValues[i].print();
        2181               0 :             printf("\n");
        2182                 :         }
        2183                 :     }
        2184                 :
        2185               0 :     printf("\n");
        2186               0 : }
        2187                 :
        2188                 : void
        2189               0 : SSAValue::print() const
        2190                 : {
        2191               0 :     switch (kind()) {
        2192                 :
        2193                 :       case EMPTY:
        2194               0 :         printf("empty");
        2195               0 :         break;
        2196                 :
        2197                 :       case PUSHED:
        2198               0 :         printf("pushed:%05u#%u", pushedOffset(), pushedIndex());
        2199               0 :         break;
        2200                 :
        2201                 :       case VAR:
        2202               0 :         if (varInitial())
        2203               0 :             printf("initial:%u", varSlot());
        2204                 :         else
        2205               0 :             printf("write:%05u", varOffset());
        2206               0 :         break;
        2207                 :
        2208                 :       case PHI:
        2209               0 :         printf("phi:%05u#%u", phiOffset(), phiSlot());
        2210               0 :         break;
        2211                 :
        2212                 :       default:
        2213               0 :         JS_NOT_REACHED("Bad kind");
        2214                 :     }
        2215               0 : }
        2216                 :
        2217                 : void
        2218          101263 : ScriptAnalysis::assertMatchingDebugMode()
        2219                 : {
        2220          101263 :     JS_ASSERT(!!script->compartment()->debugMode() == !!originalDebugMode_);
        2221          101263 : }
        2222                 :
        2223                 : #endif  /* DEBUG */
        2224                 :
        2225                 : } /* namespace analyze */
        2226                 : } /* namespace js */

Generated by: LCOV version 1.7