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