LCOV - code coverage report
Current view: directory - js/src - jsanalyze.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 916 779 85.0 %
Date: 2012-06-02 Functions: 29 25 86.2 %

       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         2603846 : ScriptAnalysis::addJump(JSContext *cx, unsigned offset,
      74                 :                         unsigned *currentOffset, unsigned *forwardJump,
      75                 :                         unsigned stackDepth)
      76                 : {
      77         2603846 :     JS_ASSERT(offset < script->length);
      78                 : 
      79         2603846 :     Bytecode *&code = codeArray[offset];
      80         2603846 :     if (!code) {
      81         2211030 :         code = cx->typeLifoAlloc().new_<Bytecode>();
      82         2211030 :         if (!code) {
      83               0 :             setOOM(cx);
      84               0 :             return false;
      85                 :         }
      86         2211030 :         code->stackDepth = stackDepth;
      87                 :     }
      88         2603846 :     JS_ASSERT(code->stackDepth == stackDepth);
      89                 : 
      90         2603846 :     code->jumpTarget = true;
      91                 : 
      92         2603846 :     if (offset < *currentOffset) {
      93                 :         /* Scripts containing loops are never inlined. */
      94          178111 :         isInlineable = false;
      95                 : 
      96                 :         /* Don't follow back edges to bytecode which has already been analyzed. */
      97          178111 :         if (!code->analyzed) {
      98          156278 :             if (*forwardJump == 0)
      99          119570 :                 *forwardJump = *currentOffset;
     100          156278 :             *currentOffset = offset;
     101                 :         }
     102         2425735 :     } else if (offset > *forwardJump) {
     103         1198736 :         *forwardJump = offset;
     104                 :     }
     105                 : 
     106         2603846 :     return true;
     107                 : }
     108                 : 
     109                 : void
     110         8919598 : ScriptAnalysis::checkAliasedName(JSContext *cx, jsbytecode *pc)
     111                 : {
     112                 :     /*
     113                 :      * Check to see if an accessed name aliases a local or argument in the
     114                 :      * current script, and mark that local/arg as escaping. We don't need to
     115                 :      * worry about marking locals/arguments in scripts this is nested in, as
     116                 :      * the escaping name will be caught by the parser and the nested local/arg
     117                 :      * will be marked as closed.
     118                 :      */
     119                 : 
     120                 :     JSAtom *atom;
     121         8919598 :     if (JSOp(*pc) == JSOP_DEFFUN) {
     122          187443 :         JSFunction *fun = script->getFunction(GET_UINT32_INDEX(pc));
     123          187443 :         atom = fun->atom;
     124                 :     } else {
     125         8732155 :         JS_ASSERT(JOF_TYPE(js_CodeSpec[*pc].format) == JOF_ATOM);
     126         8732155 :         atom = script->getAtom(GET_UINT32_INDEX(pc));
     127                 :     }
     128                 : 
     129                 :     unsigned index;
     130         8919598 :     BindingKind kind = script->bindings.lookup(cx, atom, &index);
     131                 : 
     132         8919598 :     if (kind == ARGUMENT)
     133               0 :         escapedSlots[ArgSlot(index)] = true;
     134         8919598 :     else if (kind == VARIABLE)
     135             180 :         escapedSlots[LocalSlot(script, index)] = true;
     136         8919598 : }
     137                 : 
     138                 : void
     139          900984 : ScriptAnalysis::analyzeBytecode(JSContext *cx)
     140                 : {
     141          900984 :     JS_ASSERT(cx->compartment->activeAnalysis);
     142          900984 :     JS_ASSERT(!ranBytecode());
     143          900984 :     LifoAlloc &tla = cx->typeLifoAlloc();
     144                 : 
     145          900984 :     unsigned length = script->length;
     146          900984 :     unsigned nargs = script->function() ? script->function()->nargs : 0;
     147                 : 
     148          900984 :     numSlots = TotalSlots(script);
     149                 : 
     150          900984 :     codeArray = tla.newArray<Bytecode*>(length);
     151          900984 :     escapedSlots = tla.newArray<bool>(numSlots);
     152                 : 
     153          900984 :     if (!codeArray || !escapedSlots) {
     154               0 :         setOOM(cx);
     155               0 :         return;
     156                 :     }
     157                 : 
     158          900984 :     PodZero(codeArray, length);
     159                 : 
     160                 :     /*
     161                 :      * Populate arg and local slots which can escape and be accessed in ways
     162                 :      * other than through ARG* and LOCAL* opcodes (though arguments can still
     163                 :      * be indirectly read but not written through 'arguments' properties).
     164                 :      * All escaping locals are treated as having possible use-before-defs.
     165                 :      */
     166                 : 
     167          900984 :     PodZero(escapedSlots, numSlots);
     168                 : 
     169          900984 :     if (script->usesEval || script->usesArguments || script->compartment()->debugMode()) {
     170          897713 :         for (unsigned i = 0; i < nargs; i++)
     171          499641 :             escapedSlots[ArgSlot(i)] = true;
     172                 :     } else {
     173          559245 :         for (unsigned i = 0; i < script->nClosedArgs; i++) {
     174           56333 :             unsigned arg = script->getClosedArg(i);
     175           56333 :             JS_ASSERT(arg < nargs);
     176           56333 :             escapedSlots[ArgSlot(arg)] = true;
     177                 :         }
     178                 :     }
     179                 : 
     180          900984 :     if (script->usesEval || script->compartment()->debugMode()) {
     181          793141 :         for (unsigned i = 0; i < script->nfixed; i++)
     182          400106 :             escapedSlots[LocalSlot(script, i)] = true;
     183                 :     } else {
     184          565191 :         for (uint32_t i = 0; i < script->nClosedVars; i++) {
     185           57242 :             unsigned local = script->getClosedVar(i);
     186           57242 :             JS_ASSERT(local < script->nfixed);
     187           57242 :             escapedSlots[LocalSlot(script, local)] = true;
     188                 :         }
     189                 :     }
     190                 : 
     191                 :     /*
     192                 :      * If the script is in debug mode, JS_SetFrameReturnValue can be called at
     193                 :      * any safe point.
     194                 :      */
     195          900984 :     if (cx->compartment->debugMode())
     196          388733 :         usesReturnValue_ = true;
     197                 : 
     198          900984 :     bool heavyweight = script->function() && script->function()->isHeavyweight();
     199                 : 
     200          900984 :     isCompileable = true;
     201                 : 
     202          900984 :     isInlineable = true;
     203         1695981 :     if (script->nClosedArgs || script->nClosedVars || heavyweight ||
     204          794997 :         script->usesEval || script->usesArguments || cx->compartment->debugMode()) {
     205          458253 :         isInlineable = false;
     206                 :     }
     207                 : 
     208          900984 :     modifiesArguments_ = false;
     209          900984 :     if (script->nClosedArgs || heavyweight)
     210           77684 :         modifiesArguments_ = true;
     211                 : 
     212          900984 :     canTrackVars = true;
     213                 : 
     214                 :     /*
     215                 :      * If we are in the middle of one or more jumps, the offset of the highest
     216                 :      * target jumping over this bytecode.  Includes implicit jumps from
     217                 :      * try/catch/finally blocks.
     218                 :      */
     219          900984 :     unsigned forwardJump = 0;
     220                 : 
     221                 :     /*
     222                 :      * If we are in the middle of a try block, the offset of the highest
     223                 :      * catch/finally/enditer.
     224                 :      */
     225          900984 :     unsigned forwardCatch = 0;
     226                 : 
     227                 :     /* Fill in stack depth and definitions at initial bytecode. */
     228          900984 :     Bytecode *startcode = tla.new_<Bytecode>();
     229          900984 :     if (!startcode) {
     230               0 :         setOOM(cx);
     231               0 :         return;
     232                 :     }
     233                 : 
     234          900984 :     startcode->stackDepth = 0;
     235          900984 :     codeArray[0] = startcode;
     236                 : 
     237                 :     /* Number of JOF_TYPESET opcodes we have encountered. */
     238          900984 :     unsigned nTypeSets = 0;
     239          900984 :     types::TypeSet *typeArray = script->types->typeArray();
     240                 : 
     241          900984 :     unsigned offset, nextOffset = 0;
     242       148120476 :     while (nextOffset < length) {
     243       146318508 :         offset = nextOffset;
     244                 : 
     245       146318508 :         JS_ASSERT(forwardCatch <= forwardJump);
     246                 : 
     247                 :         /* Check if the current forward jump/try-block has finished. */
     248       146318508 :         if (forwardJump && forwardJump == offset)
     249         1085897 :             forwardJump = 0;
     250       146318508 :         if (forwardCatch && forwardCatch == offset)
     251          241550 :             forwardCatch = 0;
     252                 : 
     253       146318508 :         Bytecode *code = maybeCode(offset);
     254       146318508 :         jsbytecode *pc = script->code + offset;
     255                 : 
     256       146318508 :         JSOp op = (JSOp)*pc;
     257       146318508 :         JS_ASSERT(op < JSOP_LIMIT);
     258                 : 
     259                 :         /* Immediate successor of this bytecode. */
     260       146318508 :         unsigned successorOffset = offset + GetBytecodeLength(pc);
     261                 : 
     262                 :         /*
     263                 :          * Next bytecode to analyze.  This is either the successor, or is an
     264                 :          * earlier bytecode if this bytecode has a loop backedge.
     265                 :          */
     266       146318508 :         nextOffset = successorOffset;
     267                 : 
     268       146318508 :         if (!code) {
     269                 :             /* Haven't found a path by which this bytecode is reachable. */
     270        10735700 :             continue;
     271                 :         }
     272                 : 
     273       135582808 :         if (code->analyzed) {
     274                 :             /* No need to reanalyze, see Bytecode::mergeDefines. */
     275          798769 :             continue;
     276                 :         }
     277                 : 
     278       134784039 :         code->analyzed = true;
     279                 : 
     280       134784039 :         if (forwardCatch)
     281        13604386 :             code->inTryBlock = true;
     282                 : 
     283       134784039 :         if (script->hasBreakpointsAt(pc)) {
     284             540 :             code->safePoint = true;
     285             540 :             isInlineable = canTrackVars = false;
     286                 :         }
     287                 : 
     288       134784039 :         unsigned stackDepth = code->stackDepth;
     289                 : 
     290       134784039 :         if (!forwardJump)
     291        70332892 :             code->unconditional = true;
     292                 : 
     293                 :         /*
     294                 :          * Treat decompose ops as no-ops which do not adjust the stack. We will
     295                 :          * pick up the stack depths as we go through the decomposed version.
     296                 :          */
     297       134784039 :         if (!(js_CodeSpec[op].format & JOF_DECOMPOSE)) {
     298       131412563 :             unsigned nuses = GetUseCount(script, offset);
     299       131412563 :             unsigned ndefs = GetDefCount(script, offset);
     300                 : 
     301       131412563 :             JS_ASSERT(stackDepth >= nuses);
     302       131412563 :             stackDepth -= nuses;
     303       131412563 :             stackDepth += ndefs;
     304                 :         }
     305                 : 
     306                 :         /*
     307                 :          * Assign an observed type set to each reachable JOF_TYPESET opcode.
     308                 :          * This may be less than the number of type sets in the script if some
     309                 :          * are unreachable, and may be greater in case the number of type sets
     310                 :          * overflows a uint16. In the latter case a single type set will be
     311                 :          * used for the observed types of all ops after the overflow.
     312                 :          */
     313       134784039 :         if ((js_CodeSpec[op].format & JOF_TYPESET) && cx->typeInferenceEnabled()) {
     314         1270588 :             if (nTypeSets < script->nTypeSets) {
     315         1270588 :                 code->observedTypes = &typeArray[nTypeSets++];
     316                 :             } else {
     317               0 :                 JS_ASSERT(nTypeSets == UINT16_MAX);
     318               0 :                 code->observedTypes = &typeArray[nTypeSets - 1];
     319                 :             }
     320                 :         }
     321                 : 
     322       134784039 :         switch (op) {
     323                 : 
     324                 :           case JSOP_RETURN:
     325                 :           case JSOP_STOP:
     326         1083969 :             numReturnSites_++;
     327         1083969 :             break;
     328                 : 
     329                 :           case JSOP_SETRVAL:
     330                 :           case JSOP_POPV:
     331          812177 :             usesReturnValue_ = true;
     332          812177 :             isInlineable = false;
     333          812177 :             break;
     334                 : 
     335                 :           case JSOP_QNAMEPART:
     336                 :           case JSOP_QNAMECONST:
     337            1393 :             isCompileable = false;
     338                 :           case JSOP_NAME:
     339                 :           case JSOP_CALLNAME:
     340                 :           case JSOP_BINDNAME:
     341                 :           case JSOP_SETNAME:
     342                 :           case JSOP_DELNAME:
     343         8244363 :             checkAliasedName(cx, pc);
     344         8244363 :             usesScopeChain_ = true;
     345         8244363 :             isInlineable = false;
     346         8244363 :             break;
     347                 : 
     348                 :           case JSOP_DEFFUN:
     349                 :           case JSOP_DEFVAR:
     350                 :           case JSOP_DEFCONST:
     351                 :           case JSOP_SETCONST:
     352          675235 :             checkAliasedName(cx, pc);
     353          675235 :             extendsScope_ = true;
     354          675235 :             isInlineable = canTrackVars = false;
     355          675235 :             break;
     356                 : 
     357                 :           case JSOP_EVAL:
     358            4422 :             extendsScope_ = true;
     359            4422 :             isInlineable = canTrackVars = false;
     360            4422 :             break;
     361                 : 
     362                 :           case JSOP_ENTERWITH:
     363             684 :             addsScopeObjects_ = true;
     364             684 :             isCompileable = isInlineable = canTrackVars = false;
     365             684 :             break;
     366                 : 
     367                 :           case JSOP_ENTERLET0:
     368                 :           case JSOP_ENTERLET1:
     369                 :           case JSOP_ENTERBLOCK:
     370                 :           case JSOP_LEAVEBLOCK:
     371          776180 :             addsScopeObjects_ = true;
     372          776180 :             isInlineable = false;
     373          776180 :             break;
     374                 : 
     375                 :           case JSOP_THIS:
     376         2064005 :             usesThisValue_ = true;
     377         2064005 :             break;
     378                 : 
     379                 :           case JSOP_CALL:
     380                 :           case JSOP_NEW:
     381                 :             /* Only consider potentially inlineable calls here. */
     382         3263475 :             hasFunctionCalls_ = true;
     383         3263475 :             break;
     384                 : 
     385                 :           case JSOP_TABLESWITCH: {
     386             571 :             isInlineable = false;
     387             571 :             unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
     388             571 :             jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
     389             571 :             int32_t low = GET_JUMP_OFFSET(pc2);
     390             571 :             pc2 += JUMP_OFFSET_LEN;
     391             571 :             int32_t high = GET_JUMP_OFFSET(pc2);
     392             571 :             pc2 += JUMP_OFFSET_LEN;
     393                 : 
     394             571 :             if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump, stackDepth))
     395               0 :                 return;
     396             571 :             getCode(defaultOffset).switchTarget = true;
     397             571 :             getCode(defaultOffset).safePoint = true;
     398                 : 
     399            2128 :             for (int32_t i = low; i <= high; i++) {
     400            1557 :                 unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
     401            1557 :                 if (targetOffset != offset) {
     402            1519 :                     if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, stackDepth))
     403               0 :                         return;
     404                 :                 }
     405            1557 :                 getCode(targetOffset).switchTarget = true;
     406            1557 :                 getCode(targetOffset).safePoint = true;
     407            1557 :                 pc2 += JUMP_OFFSET_LEN;
     408                 :             }
     409             571 :             break;
     410                 :           }
     411                 : 
     412                 :           case JSOP_LOOKUPSWITCH: {
     413            6775 :             isInlineable = false;
     414            6775 :             unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
     415            6775 :             jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
     416            6775 :             unsigned npairs = GET_UINT16(pc2);
     417            6775 :             pc2 += UINT16_LEN;
     418                 : 
     419            6775 :             if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump, stackDepth))
     420               0 :                 return;
     421            6775 :             getCode(defaultOffset).switchTarget = true;
     422            6775 :             getCode(defaultOffset).safePoint = true;
     423                 : 
     424           50696 :             while (npairs) {
     425           37146 :                 pc2 += UINT32_INDEX_LEN;
     426           37146 :                 unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
     427           37146 :                 if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, stackDepth))
     428               0 :                     return;
     429           37146 :                 getCode(targetOffset).switchTarget = true;
     430           37146 :                 getCode(targetOffset).safePoint = true;
     431           37146 :                 pc2 += JUMP_OFFSET_LEN;
     432           37146 :                 npairs--;
     433                 :             }
     434            6775 :             break;
     435                 :           }
     436                 : 
     437                 :           case JSOP_TRY: {
     438                 :             /*
     439                 :              * Everything between a try and corresponding catch or finally is conditional.
     440                 :              * Note that there is no problem with code which is skipped by a thrown
     441                 :              * exception but is not caught by a later handler in the same function:
     442                 :              * no more code will execute, and it does not matter what is defined.
     443                 :              */
     444          254512 :             isInlineable = false;
     445          254512 :             JSTryNote *tn = script->trynotes()->vector;
     446          254512 :             JSTryNote *tnlimit = tn + script->trynotes()->length;
     447       287037078 :             for (; tn < tnlimit; tn++) {
     448       286782566 :                 unsigned startOffset = script->mainOffset + tn->start;
     449       286782566 :                 if (startOffset == offset + 1) {
     450          257932 :                     unsigned catchOffset = startOffset + tn->length;
     451                 : 
     452                 :                     /* This will overestimate try block code, for multiple catch/finally. */
     453          257932 :                     if (catchOffset > forwardCatch)
     454          244908 :                         forwardCatch = catchOffset;
     455                 : 
     456          257932 :                     if (tn->kind != JSTRY_ITER) {
     457          257932 :                         if (!addJump(cx, catchOffset, &nextOffset, &forwardJump, stackDepth))
     458               0 :                             return;
     459          257932 :                         getCode(catchOffset).exceptionEntry = true;
     460          257932 :                         getCode(catchOffset).safePoint = true;
     461                 :                     }
     462                 :                 }
     463                 :             }
     464          254512 :             break;
     465                 :           }
     466                 : 
     467                 :           case JSOP_GETLOCAL: {
     468                 :             /*
     469                 :              * Watch for uses of variables not known to be defined, and mark
     470                 :              * them as having possible uses before definitions.  Ignore GETLOCAL
     471                 :              * followed by a POP, these are generated for, e.g. 'var x;'
     472                 :              */
     473         3014020 :             jsbytecode *next = pc + JSOP_GETLOCAL_LENGTH;
     474         3014020 :             if (JSOp(*next) != JSOP_POP || jumpTarget(next)) {
     475         2813568 :                 uint32_t local = GET_SLOTNO(pc);
     476         2813568 :                 if (local >= script->nfixed) {
     477          683191 :                     localsAliasStack_ = true;
     478          683191 :                     break;
     479                 :                 }
     480                 :             }
     481         2330829 :             break;
     482                 :           }
     483                 : 
     484                 :           case JSOP_CALLLOCAL:
     485                 :           case JSOP_INCLOCAL:
     486                 :           case JSOP_DECLOCAL:
     487                 :           case JSOP_LOCALINC:
     488                 :           case JSOP_LOCALDEC:
     489                 :           case JSOP_SETLOCAL:
     490                 :           case JSOP_SETLOCALPOP: {
     491         1724772 :             uint32_t local = GET_SLOTNO(pc);
     492         1724772 :             if (local >= script->nfixed) {
     493          462893 :                 localsAliasStack_ = true;
     494          462893 :                 break;
     495                 :             }
     496         1261879 :             break;
     497                 :           }
     498                 : 
     499                 :           case JSOP_SETARG:
     500                 :           case JSOP_INCARG:
     501                 :           case JSOP_DECARG:
     502                 :           case JSOP_ARGINC:
     503                 :           case JSOP_ARGDEC:
     504           41496 :             modifiesArguments_ = true;
     505           41496 :             isInlineable = false;
     506           41496 :             break;
     507                 : 
     508                 :           /* Additional opcodes which can be compiled but which can't be inlined. */
     509                 :           case JSOP_ARGUMENTS:
     510                 :           case JSOP_THROW:
     511                 :           case JSOP_EXCEPTION:
     512                 :           case JSOP_DEFLOCALFUN:
     513                 :           case JSOP_DEFLOCALFUN_FC:
     514                 :           case JSOP_LAMBDA:
     515                 :           case JSOP_LAMBDA_FC:
     516                 :           case JSOP_GETFCSLOT:
     517                 :           case JSOP_CALLFCSLOT:
     518                 :           case JSOP_DEBUGGER:
     519                 :           case JSOP_FUNCALL:
     520                 :           case JSOP_FUNAPPLY:
     521         1403601 :             isInlineable = false;
     522         1403601 :             break;
     523                 : 
     524                 :           /* Additional opcodes which can be both compiled both normally and inline. */
     525                 :           case JSOP_NOP:
     526                 :           case JSOP_UNDEFINED:
     527                 :           case JSOP_GOTO:
     528                 :           case JSOP_DEFAULT:
     529                 :           case JSOP_IFEQ:
     530                 :           case JSOP_IFNE:
     531                 :           case JSOP_ITERNEXT:
     532                 :           case JSOP_DUP:
     533                 :           case JSOP_DUP2:
     534                 :           case JSOP_SWAP:
     535                 :           case JSOP_PICK:
     536                 :           case JSOP_BITOR:
     537                 :           case JSOP_BITXOR:
     538                 :           case JSOP_BITAND:
     539                 :           case JSOP_LT:
     540                 :           case JSOP_LE:
     541                 :           case JSOP_GT:
     542                 :           case JSOP_GE:
     543                 :           case JSOP_EQ:
     544                 :           case JSOP_NE:
     545                 :           case JSOP_LSH:
     546                 :           case JSOP_RSH:
     547                 :           case JSOP_URSH:
     548                 :           case JSOP_ADD:
     549                 :           case JSOP_SUB:
     550                 :           case JSOP_MUL:
     551                 :           case JSOP_DIV:
     552                 :           case JSOP_MOD:
     553                 :           case JSOP_NOT:
     554                 :           case JSOP_BITNOT:
     555                 :           case JSOP_NEG:
     556                 :           case JSOP_POS:
     557                 :           case JSOP_DELPROP:
     558                 :           case JSOP_DELELEM:
     559                 :           case JSOP_TYPEOF:
     560                 :           case JSOP_TYPEOFEXPR:
     561                 :           case JSOP_VOID:
     562                 :           case JSOP_GETPROP:
     563                 :           case JSOP_CALLPROP:
     564                 :           case JSOP_LENGTH:
     565                 :           case JSOP_GETELEM:
     566                 :           case JSOP_CALLELEM:
     567                 :           case JSOP_TOID:
     568                 :           case JSOP_SETELEM:
     569                 :           case JSOP_IMPLICITTHIS:
     570                 :           case JSOP_DOUBLE:
     571                 :           case JSOP_STRING:
     572                 :           case JSOP_ZERO:
     573                 :           case JSOP_ONE:
     574                 :           case JSOP_NULL:
     575                 :           case JSOP_FALSE:
     576                 :           case JSOP_TRUE:
     577                 :           case JSOP_OR:
     578                 :           case JSOP_AND:
     579                 :           case JSOP_CASE:
     580                 :           case JSOP_STRICTEQ:
     581                 :           case JSOP_STRICTNE:
     582                 :           case JSOP_ITER:
     583                 :           case JSOP_MOREITER:
     584                 :           case JSOP_ENDITER:
     585                 :           case JSOP_POP:
     586                 :           case JSOP_GETARG:
     587                 :           case JSOP_CALLARG:
     588                 :           case JSOP_BINDGNAME:
     589                 :           case JSOP_UINT16:
     590                 :           case JSOP_NEWINIT:
     591                 :           case JSOP_NEWARRAY:
     592                 :           case JSOP_NEWOBJECT:
     593                 :           case JSOP_ENDINIT:
     594                 :           case JSOP_INITMETHOD:
     595                 :           case JSOP_INITPROP:
     596                 :           case JSOP_INITELEM:
     597                 :           case JSOP_SETPROP:
     598                 :           case JSOP_SETMETHOD:
     599                 :           case JSOP_IN:
     600                 :           case JSOP_INSTANCEOF:
     601                 :           case JSOP_LINENO:
     602                 :           case JSOP_ENUMELEM:
     603                 :           case JSOP_CONDSWITCH:
     604                 :           case JSOP_LABEL:
     605                 :           case JSOP_RETRVAL:
     606                 :           case JSOP_GETGNAME:
     607                 :           case JSOP_CALLGNAME:
     608                 :           case JSOP_SETGNAME:
     609                 :           case JSOP_REGEXP:
     610                 :           case JSOP_OBJECT:
     611                 :           case JSOP_UINT24:
     612                 :           case JSOP_GETXPROP:
     613                 :           case JSOP_INT8:
     614                 :           case JSOP_INT32:
     615                 :           case JSOP_HOLE:
     616                 :           case JSOP_LOOPHEAD:
     617                 :           case JSOP_LOOPENTRY:
     618       107831917 :             break;
     619                 : 
     620                 :           default:
     621         3581865 :             if (!(js_CodeSpec[op].format & JOF_DECOMPOSE))
     622          210389 :                 isCompileable = isInlineable = false;
     623         3581865 :             break;
     624                 :         }
     625                 : 
     626       134784039 :         uint32_t type = JOF_TYPE(js_CodeSpec[op].format);
     627                 : 
     628                 :         /* Check basic jump opcodes, which may or may not have a fallthrough. */
     629       134784039 :         if (type == JOF_JUMP) {
     630                 :             /* Some opcodes behave differently on their branching path. */
     631         2299903 :             unsigned newStackDepth = stackDepth;
     632                 : 
     633         2299903 :             switch (op) {
     634                 :               case JSOP_CASE:
     635                 :                 /* Case instructions do not push the lvalue back when branching. */
     636           22060 :                 newStackDepth--;
     637           22060 :                 break;
     638                 : 
     639                 :               default:;
     640                 :             }
     641                 : 
     642         2299903 :             unsigned targetOffset = offset + GET_JUMP_OFFSET(pc);
     643         2299903 :             if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, newStackDepth))
     644               0 :                 return;
     645                 :         }
     646                 : 
     647                 :         /* Handle any fallthrough from this opcode. */
     648       134784039 :         if (!BytecodeNoFallThrough(op)) {
     649       132560747 :             JS_ASSERT(successorOffset < script->length);
     650                 : 
     651       132560747 :             Bytecode *&nextcode = codeArray[successorOffset];
     652                 : 
     653       132560747 :             if (!nextcode) {
     654       131672025 :                 nextcode = tla.new_<Bytecode>();
     655       131672025 :                 if (!nextcode) {
     656               0 :                     setOOM(cx);
     657               0 :                     return;
     658                 :                 }
     659       131672025 :                 nextcode->stackDepth = stackDepth;
     660                 :             }
     661       132560747 :             JS_ASSERT(nextcode->stackDepth == stackDepth);
     662                 : 
     663       132560747 :             if (type == JOF_JUMP)
     664         1453962 :                 nextcode->jumpFallthrough = true;
     665                 : 
     666                 :             /* Treat the fallthrough of a branch instruction as a jump target. */
     667       132560747 :             if (type == JOF_JUMP)
     668         1453962 :                 nextcode->jumpTarget = true;
     669                 :             else
     670       131106785 :                 nextcode->fallthrough = true;
     671                 :         }
     672                 :     }
     673                 : 
     674          900984 :     JS_ASSERT(!failed());
     675          900984 :     JS_ASSERT(forwardJump == 0 && forwardCatch == 0);
     676                 : 
     677          900984 :     ranBytecode_ = true;
     678                 : }
     679                 : 
     680                 : /////////////////////////////////////////////////////////////////////
     681                 : // Lifetime Analysis
     682                 : /////////////////////////////////////////////////////////////////////
     683                 : 
     684                 : void
     685           38782 : ScriptAnalysis::analyzeLifetimes(JSContext *cx)
     686                 : {
     687           38782 :     JS_ASSERT(cx->compartment->activeAnalysis && !ranLifetimes() && !failed());
     688                 : 
     689           38782 :     if (!ranBytecode()) {
     690               0 :         analyzeBytecode(cx);
     691               0 :         if (failed())
     692               0 :             return;
     693                 :     }
     694                 : 
     695           38782 :     LifoAlloc &tla = cx->typeLifoAlloc();
     696                 : 
     697           38782 :     lifetimes = tla.newArray<LifetimeVariable>(numSlots);
     698           38782 :     if (!lifetimes) {
     699               0 :         setOOM(cx);
     700               0 :         return;
     701                 :     }
     702           38782 :     PodZero(lifetimes, numSlots);
     703                 : 
     704                 :     /*
     705                 :      * Variables which are currently dead. On forward branches to locations
     706                 :      * where these are live, they need to be marked as live.
     707                 :      */
     708                 :     LifetimeVariable **saved = (LifetimeVariable **)
     709           38782 :         cx->calloc_(numSlots * sizeof(LifetimeVariable*));
     710           38782 :     if (!saved) {
     711               0 :         setOOM(cx);
     712               0 :         return;
     713                 :     }
     714           38782 :     unsigned savedCount = 0;
     715                 : 
     716           38782 :     LoopAnalysis *loop = NULL;
     717                 : 
     718           38782 :     uint32_t offset = script->length - 1;
     719         5614898 :     while (offset < script->length) {
     720         5537334 :         Bytecode *code = maybeCode(offset);
     721         5537334 :         if (!code) {
     722         3529630 :             offset--;
     723         3529630 :             continue;
     724                 :         }
     725                 : 
     726         2007704 :         if (loop && code->safePoint)
     727            3946 :             loop->hasSafePoints = true;
     728                 : 
     729         2007704 :         jsbytecode *pc = script->code + offset;
     730                 : 
     731         2007704 :         JSOp op = (JSOp) *pc;
     732                 : 
     733         2007704 :         if (op == JSOP_LOOPHEAD && code->loop) {
     734                 :             /*
     735                 :              * This is the head of a loop, we need to go and make sure that any
     736                 :              * variables live at the head are live at the backedge and points prior.
     737                 :              * For each such variable, look for the last lifetime segment in the body
     738                 :              * and extend it to the end of the loop.
     739                 :              */
     740           10792 :             JS_ASSERT(loop == code->loop);
     741           10792 :             unsigned backedge = code->loop->backedge;
     742           64974 :             for (unsigned i = 0; i < numSlots; i++) {
     743           54182 :                 if (lifetimes[i].lifetime)
     744            9998 :                     extendVariable(cx, lifetimes[i], offset, backedge);
     745                 :             }
     746                 : 
     747           10792 :             loop = loop->parent;
     748           10792 :             JS_ASSERT_IF(loop, loop->head < offset);
     749                 :         }
     750                 : 
     751                 :         /* Find the last jump target in the loop, other than the initial entry point. */
     752         2007704 :         if (loop && code->jumpTarget && offset != loop->entry && offset > loop->lastBlock)
     753            3401 :             loop->lastBlock = offset;
     754                 : 
     755         2007704 :         if (code->exceptionEntry) {
     756           30506 :             DebugOnly<bool> found = false;
     757           15253 :             JSTryNote *tn = script->trynotes()->vector;
     758           15253 :             JSTryNote *tnlimit = tn + script->trynotes()->length;
     759        31815039 :             for (; tn < tnlimit; tn++) {
     760        31815039 :                 unsigned startOffset = script->mainOffset + tn->start;
     761        31815039 :                 if (startOffset + tn->length == offset) {
     762                 :                     /*
     763                 :                      * Extend all live variables at exception entry to the start of
     764                 :                      * the try block.
     765                 :                      */
     766           47071 :                     for (unsigned i = 0; i < numSlots; i++) {
     767           31818 :                         if (lifetimes[i].lifetime)
     768             183 :                             ensureVariable(lifetimes[i], startOffset - 1);
     769                 :                     }
     770                 : 
     771           15253 :                     found = true;
     772           15253 :                     break;
     773                 :                 }
     774                 :             }
     775           15253 :             JS_ASSERT(found);
     776                 :         }
     777                 : 
     778         2007704 :         switch (op) {
     779                 :           case JSOP_GETARG:
     780                 :           case JSOP_CALLARG:
     781                 :           case JSOP_GETLOCAL:
     782                 :           case JSOP_CALLLOCAL:
     783                 :           case JSOP_THIS: {
     784          132521 :             uint32_t slot = GetBytecodeSlot(script, pc);
     785          132521 :             if (!slotEscapes(slot))
     786           52646 :                 addVariable(cx, lifetimes[slot], offset, saved, savedCount);
     787          132521 :             break;
     788                 :           }
     789                 : 
     790                 :           case JSOP_SETARG:
     791                 :           case JSOP_SETLOCAL:
     792                 :           case JSOP_SETLOCALPOP:
     793                 :           case JSOP_DEFLOCALFUN:
     794                 :           case JSOP_DEFLOCALFUN_FC: {
     795           98004 :             uint32_t slot = GetBytecodeSlot(script, pc);
     796           98004 :             if (!slotEscapes(slot))
     797           39795 :                 killVariable(cx, lifetimes[slot], offset, saved, savedCount);
     798           98004 :             break;
     799                 :           }
     800                 : 
     801                 :           case JSOP_INCARG:
     802                 :           case JSOP_DECARG:
     803                 :           case JSOP_ARGINC:
     804                 :           case JSOP_ARGDEC:
     805                 :           case JSOP_INCLOCAL:
     806                 :           case JSOP_DECLOCAL:
     807                 :           case JSOP_LOCALINC:
     808                 :           case JSOP_LOCALDEC: {
     809            6402 :             uint32_t slot = GetBytecodeSlot(script, pc);
     810            6402 :             if (!slotEscapes(slot)) {
     811            2556 :                 killVariable(cx, lifetimes[slot], offset, saved, savedCount);
     812            2556 :                 addVariable(cx, lifetimes[slot], offset, saved, savedCount);
     813                 :             }
     814            6402 :             break;
     815                 :           }
     816                 : 
     817                 :           case JSOP_LOOKUPSWITCH:
     818                 :           case JSOP_TABLESWITCH:
     819                 :             /* Restore all saved variables. :FIXME: maybe do this precisely. */
     820             151 :             for (unsigned i = 0; i < savedCount; i++) {
     821              21 :                 LifetimeVariable &var = *saved[i];
     822              21 :                 var.lifetime = tla.new_<Lifetime>(offset, var.savedEnd, var.saved);
     823              21 :                 if (!var.lifetime) {
     824               0 :                     cx->free_(saved);
     825               0 :                     setOOM(cx);
     826               0 :                     return;
     827                 :                 }
     828              21 :                 var.saved = NULL;
     829              21 :                 saved[i--] = saved[--savedCount];
     830                 :             }
     831             130 :             savedCount = 0;
     832             130 :             break;
     833                 : 
     834                 :           case JSOP_TRY:
     835           47039 :             for (unsigned i = 0; i < numSlots; i++) {
     836           31796 :                 LifetimeVariable &var = lifetimes[i];
     837           31796 :                 if (var.ensured) {
     838             182 :                     JS_ASSERT(var.lifetime);
     839             182 :                     if (var.lifetime->start == offset)
     840             181 :                         var.ensured = false;
     841                 :                 }
     842                 :             }
     843           15243 :             break;
     844                 : 
     845                 :           case JSOP_NEW:
     846                 :           case JSOP_CALL:
     847                 :           case JSOP_EVAL:
     848                 :           case JSOP_FUNAPPLY:
     849                 :           case JSOP_FUNCALL:
     850           56863 :             if (loop)
     851           14788 :                 loop->hasCallsLoops = true;
     852           56863 :             break;
     853                 : 
     854                 :           default:;
     855                 :         }
     856                 : 
     857         2007704 :         uint32_t type = JOF_TYPE(js_CodeSpec[op].format);
     858         2007704 :         if (type == JOF_JUMP) {
     859                 :             /*
     860                 :              * Forward jumps need to pull in all variables which are live at
     861                 :              * their target offset --- the variables live before the jump are
     862                 :              * the union of those live at the fallthrough and at the target.
     863                 :              */
     864           69843 :             uint32_t targetOffset = FollowBranch(cx, script, offset);
     865                 : 
     866                 :             /*
     867                 :              * Watch for 'continue' statements in the loop body, which are
     868                 :              * jumps to the entry offset separate from the initial jump.
     869                 :              */
     870           69843 :             if (loop && loop->entry == targetOffset && loop->entry > loop->lastBlock)
     871             213 :                 loop->lastBlock = loop->entry;
     872                 : 
     873           69843 :             if (targetOffset < offset) {
     874                 :                 /* This is a loop back edge, no lifetime to pull in yet. */
     875                 : 
     876                 : #ifdef DEBUG
     877           10792 :                 JSOp nop = JSOp(script->code[targetOffset]);
     878           10792 :                 JS_ASSERT(nop == JSOP_LOOPHEAD);
     879                 : #endif
     880                 : 
     881                 :                 /*
     882                 :                  * If we already have a loop, it is an outer loop and we
     883                 :                  * need to prune the last block in the loop --- we do not
     884                 :                  * track 'continue' statements for outer loops.
     885                 :                  */
     886           10792 :                 if (loop && loop->entry > loop->lastBlock)
     887            1441 :                     loop->lastBlock = loop->entry;
     888                 : 
     889           10792 :                 LoopAnalysis *nloop = tla.new_<LoopAnalysis>();
     890           10792 :                 if (!nloop) {
     891               0 :                     cx->free_(saved);
     892               0 :                     setOOM(cx);
     893               0 :                     return;
     894                 :                 }
     895           10792 :                 PodZero(nloop);
     896                 : 
     897           10792 :                 if (loop)
     898            1631 :                     loop->hasCallsLoops = true;
     899                 : 
     900           10792 :                 nloop->parent = loop;
     901           10792 :                 loop = nloop;
     902                 : 
     903           10792 :                 getCode(targetOffset).loop = loop;
     904           10792 :                 loop->head = targetOffset;
     905           10792 :                 loop->backedge = offset;
     906           10792 :                 loop->lastBlock = loop->head;
     907                 : 
     908                 :                 /*
     909                 :                  * Find the entry jump, which will be a GOTO for 'for' or
     910                 :                  * 'while' loops or a fallthrough for 'do while' loops.
     911                 :                  */
     912           10792 :                 uint32_t entry = targetOffset;
     913           10792 :                 if (entry) {
     914           53684 :                     do {
     915           53684 :                         entry--;
     916           53684 :                     } while (!maybeCode(entry));
     917                 : 
     918           10792 :                     jsbytecode *entrypc = script->code + entry;
     919                 : 
     920           10792 :                     if (JSOp(*entrypc) == JSOP_GOTO || JSOp(*entrypc) == JSOP_FILTER)
     921           10723 :                         loop->entry = entry + GET_JUMP_OFFSET(entrypc);
     922                 :                     else
     923              69 :                         loop->entry = targetOffset;
     924                 :                 } else {
     925                 :                     /* Do-while loop at the start of the script. */
     926               0 :                     loop->entry = targetOffset;
     927                 :                 }
     928           21515 :                 JS_ASSERT(script->code[loop->entry] == JSOP_LOOPHEAD ||
     929           21515 :                           script->code[loop->entry] == JSOP_LOOPENTRY);
     930                 :             } else {
     931           65261 :                 for (unsigned i = 0; i < savedCount; i++) {
     932            6210 :                     LifetimeVariable &var = *saved[i];
     933            6210 :                     JS_ASSERT(!var.lifetime && var.saved);
     934            6210 :                     if (var.live(targetOffset)) {
     935                 :                         /*
     936                 :                          * Jumping to a place where this variable is live. Make a new
     937                 :                          * lifetime segment for the variable.
     938                 :                          */
     939             500 :                         var.lifetime = tla.new_<Lifetime>(offset, var.savedEnd, var.saved);
     940             500 :                         if (!var.lifetime) {
     941               0 :                             cx->free_(saved);
     942               0 :                             setOOM(cx);
     943               0 :                             return;
     944                 :                         }
     945             500 :                         var.saved = NULL;
     946             500 :                         saved[i--] = saved[--savedCount];
     947            5710 :                     } else if (loop && !var.savedEnd) {
     948                 :                         /*
     949                 :                          * This jump precedes the basic block which killed the variable,
     950                 :                          * remember it and use it for the end of the next lifetime
     951                 :                          * segment should the variable become live again. This is needed
     952                 :                          * for loops, as if we wrap liveness around the loop the isLive
     953                 :                          * test below may have given the wrong answer.
     954                 :                          */
     955             670 :                         var.savedEnd = offset;
     956                 :                     }
     957                 :                 }
     958                 :             }
     959                 :         }
     960                 : 
     961         2007704 :         offset--;
     962                 :     }
     963                 : 
     964           38782 :     cx->free_(saved);
     965                 : 
     966           38782 :     ranLifetimes_ = true;
     967                 : }
     968                 : 
     969                 : #ifdef DEBUG
     970                 : void
     971               0 : LifetimeVariable::print() const
     972                 : {
     973               0 :     Lifetime *segment = lifetime ? lifetime : saved;
     974               0 :     while (segment) {
     975               0 :         printf(" (%u,%u%s)", segment->start, segment->end, segment->loopTail ? ",tail" : "");
     976               0 :         segment = segment->next;
     977                 :     }
     978               0 :     printf("\n");
     979               0 : }
     980                 : #endif /* DEBUG */
     981                 : 
     982                 : inline void
     983           55202 : ScriptAnalysis::addVariable(JSContext *cx, LifetimeVariable &var, unsigned offset,
     984                 :                             LifetimeVariable **&saved, unsigned &savedCount)
     985                 : {
     986           55202 :     if (var.lifetime) {
     987           33335 :         if (var.ensured)
     988              62 :             return;
     989                 : 
     990           33273 :         JS_ASSERT(offset < var.lifetime->start);
     991           33273 :         var.lifetime->start = offset;
     992                 :     } else {
     993           21867 :         if (var.saved) {
     994                 :             /* Remove from the list of saved entries. */
     995            8351 :             for (unsigned i = 0; i < savedCount; i++) {
     996            8351 :                 if (saved[i] == &var) {
     997            4497 :                     JS_ASSERT(savedCount);
     998            4497 :                     saved[i--] = saved[--savedCount];
     999            4497 :                     break;
    1000                 :                 }
    1001                 :             }
    1002                 :         }
    1003           21867 :         var.lifetime = cx->typeLifoAlloc().new_<Lifetime>(offset, var.savedEnd, var.saved);
    1004           21867 :         if (!var.lifetime) {
    1005               0 :             setOOM(cx);
    1006               0 :             return;
    1007                 :         }
    1008           21867 :         var.saved = NULL;
    1009                 :     }
    1010                 : }
    1011                 : 
    1012                 : inline void
    1013           42351 : ScriptAnalysis::killVariable(JSContext *cx, LifetimeVariable &var, unsigned offset,
    1014                 :                              LifetimeVariable **&saved, unsigned &savedCount)
    1015                 : {
    1016           42351 :     if (!var.lifetime) {
    1017                 :         /* Make a point lifetime indicating the write. */
    1018           32269 :         if (!var.saved)
    1019             692 :             saved[savedCount++] = &var;
    1020           32269 :         var.saved = cx->typeLifoAlloc().new_<Lifetime>(offset, var.savedEnd, var.saved);
    1021           32269 :         if (!var.saved) {
    1022               0 :             setOOM(cx);
    1023               0 :             return;
    1024                 :         }
    1025           32269 :         var.saved->write = true;
    1026           32269 :         var.savedEnd = 0;
    1027           32269 :         return;
    1028                 :     }
    1029                 : 
    1030           10082 :     JS_ASSERT_IF(!var.ensured, offset < var.lifetime->start);
    1031           10082 :     unsigned start = var.lifetime->start;
    1032                 : 
    1033                 :     /*
    1034                 :      * The variable is considered to be live at the bytecode which kills it
    1035                 :      * (just not at earlier bytecodes). This behavior is needed by downstream
    1036                 :      * register allocation (see FrameState::bestEvictReg).
    1037                 :      */
    1038           10082 :     var.lifetime->start = offset;
    1039           10082 :     var.lifetime->write = true;
    1040                 : 
    1041           10082 :     if (var.ensured) {
    1042                 :         /*
    1043                 :          * The variable is live even before the write, due to an enclosing try
    1044                 :          * block. We need to split the lifetime to indicate there was a write.
    1045                 :          * We set the new interval's savedEnd to 0, since it will always be
    1046                 :          * adjacent to the old interval, so it never needs to be extended.
    1047                 :          */
    1048              48 :         var.lifetime = cx->typeLifoAlloc().new_<Lifetime>(start, 0, var.lifetime);
    1049              48 :         if (!var.lifetime) {
    1050               0 :             setOOM(cx);
    1051               0 :             return;
    1052                 :         }
    1053              48 :         var.lifetime->end = offset;
    1054                 :     } else {
    1055           10034 :         var.saved = var.lifetime;
    1056           10034 :         var.savedEnd = 0;
    1057           10034 :         var.lifetime = NULL;
    1058                 : 
    1059           10034 :         saved[savedCount++] = &var;
    1060                 :     }
    1061                 : }
    1062                 : 
    1063                 : inline void
    1064            9998 : ScriptAnalysis::extendVariable(JSContext *cx, LifetimeVariable &var,
    1065                 :                                unsigned start, unsigned end)
    1066                 : {
    1067            9998 :     JS_ASSERT(var.lifetime);
    1068            9998 :     if (var.ensured) {
    1069                 :         /*
    1070                 :          * If we are still ensured to be live, the try block must scope over
    1071                 :          * the loop, in which case the variable is already guaranteed to be
    1072                 :          * live for the entire loop.
    1073                 :          */
    1074              38 :         JS_ASSERT(var.lifetime->start < start);
    1075              38 :         return;
    1076                 :     }
    1077                 : 
    1078            9960 :     var.lifetime->start = start;
    1079                 : 
    1080                 :     /*
    1081                 :      * Consider this code:
    1082                 :      *
    1083                 :      *   while (...) { (#1)
    1084                 :      *       use x;    (#2)
    1085                 :      *       ...
    1086                 :      *       x = ...;  (#3)
    1087                 :      *       ...
    1088                 :      *   }             (#4)
    1089                 :      *
    1090                 :      * Just before analyzing the while statement, there would be a live range
    1091                 :      * from #1..#2 and a "point range" at #3. The job of extendVariable is to
    1092                 :      * create a new live range from #3..#4.
    1093                 :      *
    1094                 :      * However, more extensions may be required if the definition of x is
    1095                 :      * conditional. Consider the following.
    1096                 :      *
    1097                 :      *   while (...) {     (#1)
    1098                 :      *       use x;        (#2)
    1099                 :      *       ...
    1100                 :      *       if (...)      (#5)
    1101                 :      *           x = ...;  (#3)
    1102                 :      *       ...
    1103                 :      *   }                 (#4)
    1104                 :      *
    1105                 :      * Assume that x is not used after the loop. Then, before extendVariable is
    1106                 :      * run, the live ranges would be the same as before (#1..#2 and #3..#3). We
    1107                 :      * still need to create a range from #3..#4. But, since the assignment at #3
    1108                 :      * may never run, we also need to create a range from #2..#3. This is done
    1109                 :      * as follows.
    1110                 :      *
    1111                 :      * Each time we create a Lifetime, we store the start of the most recently
    1112                 :      * seen sequence of conditional code in the Lifetime's savedEnd field. So,
    1113                 :      * when creating the Lifetime at #2, we set the Lifetime's savedEnd to
    1114                 :      * #5. (The start of the most recent conditional is cached in each
    1115                 :      * variable's savedEnd field.) Consequently, extendVariable is able to
    1116                 :      * create a new interval from #2..#5 using the savedEnd field of the
    1117                 :      * existing #1..#2 interval.
    1118                 :      */
    1119                 : 
    1120            9960 :     Lifetime *segment = var.lifetime;
    1121           30188 :     while (segment && segment->start < end) {
    1122           13717 :         uint32_t savedEnd = segment->savedEnd;
    1123           13717 :         if (!segment->next || segment->next->start >= end) {
    1124                 :             /*
    1125                 :              * savedEnd is only set for variables killed in the middle of the
    1126                 :              * loop. Make a tail segment connecting the last use with the
    1127                 :              * back edge.
    1128                 :              */
    1129            9960 :             if (segment->end >= end) {
    1130                 :                 /* Variable known to be live after the loop finishes. */
    1131            3449 :                 break;
    1132                 :             }
    1133            6511 :             savedEnd = end;
    1134                 :         }
    1135           10268 :         JS_ASSERT(savedEnd <= end);
    1136           10268 :         if (savedEnd > segment->end) {
    1137            6556 :             Lifetime *tail = cx->typeLifoAlloc().new_<Lifetime>(savedEnd, 0, segment->next);
    1138            6556 :             if (!tail) {
    1139               0 :                 setOOM(cx);
    1140               0 :                 return;
    1141                 :             }
    1142            6556 :             tail->start = segment->end;
    1143            6556 :             tail->loopTail = true;
    1144                 : 
    1145                 :             /*
    1146                 :              * Clear the segment's saved end, but preserve in the tail if this
    1147                 :              * is the last segment in the loop and the variable is killed in an
    1148                 :              * outer loop before the backedge.
    1149                 :              */
    1150            6556 :             if (segment->savedEnd > end) {
    1151              25 :                 JS_ASSERT(savedEnd == end);
    1152              25 :                 tail->savedEnd = segment->savedEnd;
    1153                 :             }
    1154            6556 :             segment->savedEnd = 0;
    1155                 : 
    1156            6556 :             segment->next = tail;
    1157            6556 :             segment = tail->next;
    1158                 :         } else {
    1159            3712 :             JS_ASSERT(segment->savedEnd == 0);
    1160            3712 :             segment = segment->next;
    1161                 :         }
    1162                 :     }
    1163                 : }
    1164                 : 
    1165                 : inline void
    1166             183 : ScriptAnalysis::ensureVariable(LifetimeVariable &var, unsigned until)
    1167                 : {
    1168             183 :     JS_ASSERT(var.lifetime);
    1169                 : 
    1170                 :     /*
    1171                 :      * If we are already ensured, the current range we are trying to ensure
    1172                 :      * should already be included.
    1173                 :      */
    1174             183 :     if (var.ensured) {
    1175               2 :         JS_ASSERT(var.lifetime->start <= until);
    1176               2 :         return;
    1177                 :     }
    1178                 : 
    1179             181 :     JS_ASSERT(until < var.lifetime->start);
    1180             181 :     var.lifetime->start = until;
    1181             181 :     var.ensured = true;
    1182                 : }
    1183                 : 
    1184                 : void
    1185          131876 : ScriptAnalysis::clearAllocations()
    1186                 : {
    1187                 :     /*
    1188                 :      * Clear out storage used for register allocations in a compilation once
    1189                 :      * that compilation has finished. Register allocations are only used for
    1190                 :      * a single compilation.
    1191                 :      */
    1192        33567394 :     for (unsigned i = 0; i < script->length; i++) {
    1193        33435518 :         Bytecode *code = maybeCode(i);
    1194        33435518 :         if (code)
    1195        13142778 :             code->allocation = NULL;
    1196                 :     }
    1197          131876 : }
    1198                 : 
    1199                 : /////////////////////////////////////////////////////////////////////
    1200                 : // SSA Analysis
    1201                 : /////////////////////////////////////////////////////////////////////
    1202                 : 
    1203                 : void
    1204           38782 : ScriptAnalysis::analyzeSSA(JSContext *cx)
    1205                 : {
    1206           38782 :     JS_ASSERT(cx->compartment->activeAnalysis && !ranSSA() && !failed());
    1207                 : 
    1208           38782 :     if (!ranLifetimes()) {
    1209           38782 :         analyzeLifetimes(cx);
    1210           38782 :         if (failed())
    1211               0 :             return;
    1212                 :     }
    1213                 : 
    1214           38782 :     LifoAlloc &tla = cx->typeLifoAlloc();
    1215           38782 :     unsigned maxDepth = script->nslots - script->nfixed;
    1216                 : 
    1217                 :     /*
    1218                 :      * Current value of each variable and stack value. Empty for missing or
    1219                 :      * untracked entries, i.e. escaping locals and arguments.
    1220                 :      */
    1221                 :     SSAValueInfo *values = (SSAValueInfo *)
    1222           38782 :         cx->calloc_((numSlots + maxDepth) * sizeof(SSAValueInfo));
    1223           38782 :     if (!values) {
    1224               0 :         setOOM(cx);
    1225               0 :         return;
    1226                 :     }
    1227                 :     struct FreeSSAValues {
    1228                 :         JSContext *cx;
    1229                 :         SSAValueInfo *values;
    1230           38782 :         FreeSSAValues(JSContext *cx, SSAValueInfo *values) : cx(cx), values(values) {}
    1231           38782 :         ~FreeSSAValues() { cx->free_(values); }
    1232           77564 :     } free(cx, values);
    1233                 : 
    1234           38782 :     SSAValueInfo *stack = values + numSlots;
    1235           38782 :     uint32_t stackDepth = 0;
    1236                 : 
    1237          117398 :     for (uint32_t slot = ArgSlot(0); slot < numSlots; slot++) {
    1238           78616 :         if (trackSlot(slot))
    1239           12559 :             values[slot].v.initInitial(slot);
    1240                 :     }
    1241                 : 
    1242                 :     /*
    1243                 :      * All target offsets for forward jumps we have seen (including ones whose
    1244                 :      * target we have advanced past). We lazily add pending entries at these
    1245                 :      * targets for the original value of variables modified before the branch
    1246                 :      * rejoins.
    1247                 :      */
    1248           77564 :     Vector<uint32_t> branchTargets(cx);
    1249                 : 
    1250                 :     /*
    1251                 :      * Subset of branchTargets which are exception handlers at future offsets.
    1252                 :      * Any new value of a variable modified before the target is reached is a
    1253                 :      * potential value at that target, along with the lazy original value.
    1254                 :      */
    1255           77564 :     Vector<uint32_t> exceptionTargets(cx);
    1256                 : 
    1257           38782 :     uint32_t offset = 0;
    1258         2107993 :     while (offset < script->length) {
    1259         2030429 :         jsbytecode *pc = script->code + offset;
    1260         2030429 :         JSOp op = (JSOp)*pc;
    1261                 : 
    1262         2030429 :         uint32_t successorOffset = offset + GetBytecodeLength(pc);
    1263                 : 
    1264         2030429 :         Bytecode *code = maybeCode(pc);
    1265         2030429 :         if (!code) {
    1266           22725 :             offset = successorOffset;
    1267           22725 :             continue;
    1268                 :         }
    1269                 : 
    1270         2007704 :         if (code->exceptionEntry) {
    1271                 :             /* Remove from exception targets list, which reflects only future targets. */
    1272           15261 :             for (size_t i = 0; i < exceptionTargets.length(); i++) {
    1273           15261 :                 if (exceptionTargets[i] == offset) {
    1274           15253 :                     exceptionTargets[i] = exceptionTargets.back();
    1275           15253 :                     exceptionTargets.popBack();
    1276           15253 :                     break;
    1277                 :                 }
    1278                 :             }
    1279                 :         }
    1280                 : 
    1281         2007704 :         if (code->stackDepth > stackDepth)
    1282             272 :             PodZero(stack + stackDepth, code->stackDepth - stackDepth);
    1283         2007704 :         stackDepth = code->stackDepth;
    1284                 : 
    1285         2007704 :         if (op == JSOP_LOOPHEAD && code->loop) {
    1286                 :             /*
    1287                 :              * Make sure there is a pending value array for phi nodes at the
    1288                 :              * loop head. We won't be able to clear these until we reach the
    1289                 :              * loop's back edge.
    1290                 :              *
    1291                 :              * We need phi nodes for all variables which might be modified
    1292                 :              * during the loop. This ensures that in the loop body we have
    1293                 :              * already updated state to reflect possible changes that happen
    1294                 :              * before the back edge, and don't need to go back and fix things
    1295                 :              * up when we *do* get to the back edge. This could be made lazier.
    1296                 :              *
    1297                 :              * We don't make phi nodes for values on the stack at the head of
    1298                 :              * the loop. These may be popped during the loop (i.e. for ITER
    1299                 :              * loops), but in such cases the original value is pushed back.
    1300                 :              */
    1301           10792 :             Vector<SlotValue> *&pending = code->pendingValues;
    1302           10792 :             if (!pending) {
    1303           10792 :                 pending = cx->new_< Vector<SlotValue> >(cx);
    1304           10792 :                 if (!pending) {
    1305               0 :                     setOOM(cx);
    1306                 :                     return;
    1307                 :                 }
    1308                 :             }
    1309                 : 
    1310                 :             /*
    1311                 :              * Make phi nodes and update state for slots which are already in
    1312                 :              * pending from previous branches to the loop head, and which are
    1313                 :              * modified in the body of the loop.
    1314                 :              */
    1315           10792 :             for (unsigned i = 0; i < pending->length(); i++) {
    1316               0 :                 SlotValue &v = (*pending)[i];
    1317               0 :                 if (v.slot < numSlots && liveness(v.slot).firstWrite(code->loop) != UINT32_MAX) {
    1318               0 :                     if (v.value.kind() != SSAValue::PHI || v.value.phiOffset() != offset) {
    1319               0 :                         JS_ASSERT(v.value.phiOffset() < offset);
    1320               0 :                         SSAValue ov = v.value;
    1321               0 :                         if (!makePhi(cx, v.slot, offset, &ov))
    1322                 :                             return;
    1323               0 :                         insertPhi(cx, ov, v.value);
    1324               0 :                         v.value = ov;
    1325                 :                     }
    1326                 :                 }
    1327               0 :                 if (code->fallthrough || code->jumpFallthrough)
    1328               0 :                     mergeValue(cx, offset, values[v.slot].v, &v);
    1329               0 :                 mergeBranchTarget(cx, values[v.slot], v.slot, branchTargets, offset - 1);
    1330               0 :                 values[v.slot].v = v.value;
    1331                 :             }
    1332                 : 
    1333                 :             /*
    1334                 :              * Make phi nodes for all other slots which might be modified
    1335                 :              * during the loop. This ensures that in the loop body we have
    1336                 :              * already updated state to reflect possible changes that happen
    1337                 :              * before the back edge, and don't need to go back and fix things
    1338                 :              * up when we *do* get to the back edge. This could be made lazier.
    1339                 :              */
    1340           63589 :             for (uint32_t slot = ArgSlot(0); slot < numSlots + stackDepth; slot++) {
    1341           52797 :                 if (slot >= numSlots || !trackSlot(slot))
    1342           38014 :                     continue;
    1343           14783 :                 if (liveness(slot).firstWrite(code->loop) == UINT32_MAX)
    1344            9256 :                     continue;
    1345            5527 :                 if (values[slot].v.kind() == SSAValue::PHI && values[slot].v.phiOffset() == offset) {
    1346                 :                     /* There is already a pending entry for this slot. */
    1347               0 :                     continue;
    1348                 :                 }
    1349                 :                 SSAValue ov;
    1350            5527 :                 if (!makePhi(cx, slot, offset, &ov))
    1351                 :                     return;
    1352            5527 :                 if (code->fallthrough || code->jumpFallthrough)
    1353              16 :                     insertPhi(cx, ov, values[slot].v);
    1354            5527 :                 mergeBranchTarget(cx, values[slot], slot, branchTargets, offset - 1);
    1355            5527 :                 values[slot].v = ov;
    1356            5527 :                 if (!pending->append(SlotValue(slot, ov))) {
    1357               0 :                     setOOM(cx);
    1358                 :                     return;
    1359                 :                 }
    1360           10792 :             }
    1361         1996912 :         } else if (code->pendingValues) {
    1362                 :             /*
    1363                 :              * New values at this point from a previous jump to this bytecode.
    1364                 :              * If there is fallthrough from the previous instruction, merge
    1365                 :              * with the current state and create phi nodes where necessary,
    1366                 :              * otherwise replace current values with the new values.
    1367                 :              *
    1368                 :              * Catch blocks are artifically treated as having fallthrough, so
    1369                 :              * that values written inside the block but not subsequently
    1370                 :              * overwritten are picked up.
    1371                 :              */
    1372           58773 :             bool exception = getCode(offset).exceptionEntry;
    1373           58773 :             Vector<SlotValue> *pending = code->pendingValues;
    1374           96568 :             for (unsigned i = 0; i < pending->length(); i++) {
    1375           37795 :                 SlotValue &v = (*pending)[i];
    1376           38005 :                 if (code->fallthrough || code->jumpFallthrough ||
    1377             210 :                     (exception && values[v.slot].v.kind() != SSAValue::EMPTY)) {
    1378           32918 :                     mergeValue(cx, offset, values[v.slot].v, &v);
    1379                 :                 }
    1380           37795 :                 mergeBranchTarget(cx, values[v.slot], v.slot, branchTargets, offset);
    1381           37795 :                 values[v.slot].v = v.value;
    1382                 :             }
    1383           58773 :             freezeNewValues(cx, offset);
    1384                 :         }
    1385                 : 
    1386         2007704 :         if (js_CodeSpec[op].format & JOF_DECOMPOSE) {
    1387            3660 :             offset = successorOffset;
    1388            3660 :             continue;
    1389                 :         }
    1390                 : 
    1391         2004044 :         unsigned nuses = GetUseCount(script, offset);
    1392         2004044 :         unsigned ndefs = GetDefCount(script, offset);
    1393         2004044 :         JS_ASSERT(stackDepth >= nuses);
    1394                 : 
    1395         2004044 :         unsigned xuses = ExtendedUse(pc) ? nuses + 1 : nuses;
    1396                 : 
    1397         2004044 :         if (xuses) {
    1398         1097141 :             code->poppedValues = tla.newArray<SSAValue>(xuses);
    1399         1097141 :             if (!code->poppedValues) {
    1400               0 :                 setOOM(cx);
    1401                 :                 return;
    1402                 :             }
    1403         2770226 :             for (unsigned i = 0; i < nuses; i++) {
    1404         1673085 :                 SSAValue &v = stack[stackDepth - 1 - i].v;
    1405         1673085 :                 code->poppedValues[i] = v;
    1406         1673085 :                 v.clear();
    1407                 :             }
    1408         1097141 :             if (xuses > nuses) {
    1409                 :                 /*
    1410                 :                  * For SETLOCAL, INCLOCAL, etc. opcodes, add an extra popped
    1411                 :                  * value holding the value of the local before the op.
    1412                 :                  */
    1413          223858 :                 uint32_t slot = GetBytecodeSlot(script, pc);
    1414          223858 :                 if (trackSlot(slot))
    1415           81888 :                     code->poppedValues[nuses] = values[slot].v;
    1416                 :                 else
    1417          141970 :                     code->poppedValues[nuses].clear();
    1418                 :             }
    1419                 : 
    1420         1097141 :             if (xuses) {
    1421         1097141 :                 SSAUseChain *useChains = tla.newArray<SSAUseChain>(xuses);
    1422         1097141 :                 if (!useChains) {
    1423               0 :                     setOOM(cx);
    1424                 :                     return;
    1425                 :                 }
    1426         1097141 :                 PodZero(useChains, xuses);
    1427         2994084 :                 for (unsigned i = 0; i < xuses; i++) {
    1428         1896943 :                     const SSAValue &v = code->poppedValues[i];
    1429         1896943 :                     if (trackUseChain(v)) {
    1430         1723912 :                         SSAUseChain *&uses = useChain(v);
    1431         1723912 :                         useChains[i].popped = true;
    1432         1723912 :                         useChains[i].offset = offset;
    1433         1723912 :                         useChains[i].u.which = i;
    1434         1723912 :                         useChains[i].next = uses;
    1435         1723912 :                         uses = &useChains[i];
    1436                 :                     }
    1437                 :                 }
    1438                 :             }
    1439                 :         }
    1440                 : 
    1441         2004044 :         stackDepth -= nuses;
    1442                 : 
    1443         3678408 :         for (unsigned i = 0; i < ndefs; i++)
    1444         1674364 :             stack[stackDepth + i].v.initPushed(offset, i);
    1445                 : 
    1446         2004044 :         unsigned xdefs = ExtendedDef(pc) ? ndefs + 1 : ndefs;
    1447         2004044 :         if (xdefs) {
    1448         1592364 :             code->pushedUses = tla.newArray<SSAUseChain *>(xdefs);
    1449         1592364 :             if (!code->pushedUses) {
    1450               0 :                 setOOM(cx);
    1451                 :                 return;
    1452                 :             }
    1453         1592364 :             PodZero(code->pushedUses, xdefs);
    1454                 :         }
    1455                 : 
    1456         2004044 :         stackDepth += ndefs;
    1457                 : 
    1458         2004044 :         if (BytecodeUpdatesSlot(op)) {
    1459          104406 :             uint32_t slot = GetBytecodeSlot(script, pc);
    1460          104406 :             if (trackSlot(slot)) {
    1461           42338 :                 mergeBranchTarget(cx, values[slot], slot, branchTargets, offset);
    1462           42338 :                 mergeExceptionTarget(cx, values[slot].v, slot, exceptionTargets);
    1463           42338 :                 values[slot].v.initWritten(slot, offset);
    1464                 :             }
    1465                 :         }
    1466                 : 
    1467         2004044 :         switch (op) {
    1468                 :           case JSOP_GETARG:
    1469                 :           case JSOP_GETLOCAL: {
    1470          117762 :             uint32_t slot = GetBytecodeSlot(script, pc);
    1471          117762 :             if (trackSlot(slot)) {
    1472                 :                 /*
    1473                 :                  * Propagate the current value of the local to the pushed value,
    1474                 :                  * and remember it with an extended use on the opcode.
    1475                 :                  */
    1476           39046 :                 stack[stackDepth - 1].v = code->poppedValues[0] = values[slot].v;
    1477                 :             }
    1478          117762 :             break;
    1479                 :           }
    1480                 : 
    1481                 :           /* Short circuit ops which push back one of their operands. */
    1482                 : 
    1483                 :           case JSOP_MOREITER:
    1484            1643 :             stack[stackDepth - 2].v = code->poppedValues[0];
    1485            1643 :             break;
    1486                 : 
    1487                 :           case JSOP_INITPROP:
    1488                 :           case JSOP_INITMETHOD:
    1489            4938 :             stack[stackDepth - 1].v = code->poppedValues[1];
    1490            4938 :             break;
    1491                 : 
    1492                 :           case JSOP_INITELEM:
    1493           11160 :             stack[stackDepth - 1].v = code->poppedValues[2];
    1494           11160 :             break;
    1495                 : 
    1496                 :           case JSOP_DUP:
    1497           20712 :             stack[stackDepth - 1].v = stack[stackDepth - 2].v = code->poppedValues[0];
    1498           20712 :             break;
    1499                 : 
    1500                 :           case JSOP_DUP2:
    1501             296 :             stack[stackDepth - 1].v = stack[stackDepth - 3].v = code->poppedValues[0];
    1502             296 :             stack[stackDepth - 2].v = stack[stackDepth - 4].v = code->poppedValues[1];
    1503             296 :             break;
    1504                 : 
    1505                 :           case JSOP_SWAP:
    1506                 :             /* Swap is like pick 1. */
    1507                 :           case JSOP_PICK: {
    1508           21662 :             unsigned pickedDepth = (op == JSOP_SWAP ? 1 : pc[1]);
    1509           21662 :             stack[stackDepth - 1].v = code->poppedValues[pickedDepth];
    1510           46452 :             for (unsigned i = 0; i < pickedDepth; i++)
    1511           24790 :                 stack[stackDepth - 2 - i].v = code->poppedValues[i];
    1512           21662 :             break;
    1513                 :           }
    1514                 : 
    1515                 :           /*
    1516                 :            * Switch and try blocks preserve the stack between the original op
    1517                 :            * and all case statements or exception/finally handlers.
    1518                 :            */
    1519                 : 
    1520                 :           case JSOP_TABLESWITCH: {
    1521             104 :             unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
    1522             104 :             jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
    1523             104 :             int32_t low = GET_JUMP_OFFSET(pc2);
    1524             104 :             pc2 += JUMP_OFFSET_LEN;
    1525             104 :             int32_t high = GET_JUMP_OFFSET(pc2);
    1526             104 :             pc2 += JUMP_OFFSET_LEN;
    1527                 : 
    1528             402 :             for (int32_t i = low; i <= high; i++) {
    1529             298 :                 unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
    1530             298 :                 if (targetOffset != offset)
    1531             290 :                     checkBranchTarget(cx, targetOffset, branchTargets, values, stackDepth);
    1532             298 :                 pc2 += JUMP_OFFSET_LEN;
    1533                 :             }
    1534                 : 
    1535             104 :             checkBranchTarget(cx, defaultOffset, branchTargets, values, stackDepth);
    1536             104 :             break;
    1537                 :           }
    1538                 : 
    1539                 :           case JSOP_LOOKUPSWITCH: {
    1540              26 :             unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
    1541              26 :             jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
    1542              26 :             unsigned npairs = GET_UINT16(pc2);
    1543              26 :             pc2 += UINT16_LEN;
    1544                 : 
    1545             164 :             while (npairs) {
    1546             112 :                 pc2 += UINT32_INDEX_LEN;
    1547             112 :                 unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
    1548             112 :                 checkBranchTarget(cx, targetOffset, branchTargets, values, stackDepth);
    1549             112 :                 pc2 += JUMP_OFFSET_LEN;
    1550             112 :                 npairs--;
    1551                 :             }
    1552                 : 
    1553              26 :             checkBranchTarget(cx, defaultOffset, branchTargets, values, stackDepth);
    1554              26 :             break;
    1555                 :           }
    1556                 : 
    1557                 :           case JSOP_TRY: { 
    1558           15243 :             JSTryNote *tn = script->trynotes()->vector;
    1559           15243 :             JSTryNote *tnlimit = tn + script->trynotes()->length;
    1560        63629948 :             for (; tn < tnlimit; tn++) {
    1561        63614705 :                 unsigned startOffset = script->mainOffset + tn->start;
    1562        63614705 :                 if (startOffset == offset + 1) {
    1563           15253 :                     unsigned catchOffset = startOffset + tn->length;
    1564                 : 
    1565           15253 :                     if (tn->kind != JSTRY_ITER) {
    1566           15253 :                         checkBranchTarget(cx, catchOffset, branchTargets, values, stackDepth);
    1567           15253 :                         checkExceptionTarget(cx, catchOffset, exceptionTargets);
    1568                 :                     }
    1569                 :                 }
    1570                 :             }
    1571           15243 :             break;
    1572                 :           }
    1573                 : 
    1574                 :           case JSOP_THROW:
    1575                 :           case JSOP_RETURN:
    1576                 :           case JSOP_STOP:
    1577                 :           case JSOP_RETRVAL:
    1578           40509 :             mergeAllExceptionTargets(cx, values, exceptionTargets);
    1579           40509 :             break;
    1580                 : 
    1581                 :           default:;
    1582                 :         }
    1583                 : 
    1584         2004044 :         uint32_t type = JOF_TYPE(js_CodeSpec[op].format);
    1585         2004044 :         if (type == JOF_JUMP) {
    1586           69843 :             unsigned targetOffset = FollowBranch(cx, script, offset);
    1587           69843 :             checkBranchTarget(cx, targetOffset, branchTargets, values, stackDepth);
    1588                 : 
    1589                 :             /*
    1590                 :              * If this is a back edge, we're done with the loop and can freeze
    1591                 :              * the phi values at the head now.
    1592                 :              */
    1593           69843 :             if (targetOffset < offset)
    1594           10792 :                 freezeNewValues(cx, targetOffset);
    1595                 :         }
    1596                 : 
    1597         2004044 :         offset = successorOffset;
    1598                 :     }
    1599                 : 
    1600           77564 :     ranSSA_ = true;
    1601                 : }
    1602                 : 
    1603                 : /* Get a phi node's capacity for a given length. */
    1604                 : static inline unsigned
    1605           39671 : PhiNodeCapacity(unsigned length)
    1606                 : {
    1607           39671 :     if (length <= 4)
    1608           39652 :         return 4;
    1609                 : 
    1610                 :     unsigned log2;
    1611              19 :     JS_FLOOR_LOG2(log2, length - 1);
    1612              19 :     return 1 << (log2 + 1);
    1613                 : }
    1614                 : 
    1615                 : bool
    1616           14910 : ScriptAnalysis::makePhi(JSContext *cx, uint32_t slot, uint32_t offset, SSAValue *pv)
    1617                 : {
    1618           14910 :     SSAPhiNode *node = cx->typeLifoAlloc().new_<SSAPhiNode>();
    1619           14910 :     SSAValue *options = cx->typeLifoAlloc().newArray<SSAValue>(PhiNodeCapacity(0));
    1620           14910 :     if (!node || !options) {
    1621               0 :         setOOM(cx);
    1622               0 :         return false;
    1623                 :     }
    1624           14910 :     node->slot = slot;
    1625           14910 :     node->options = options;
    1626           14910 :     pv->initPhi(offset, node);
    1627           14910 :     return true;
    1628                 : }
    1629                 : 
    1630                 : void
    1631           24915 : ScriptAnalysis::insertPhi(JSContext *cx, SSAValue &phi, const SSAValue &v)
    1632                 : {
    1633           24915 :     JS_ASSERT(phi.kind() == SSAValue::PHI);
    1634           24915 :     SSAPhiNode *node = phi.phiNode();
    1635                 : 
    1636                 :     /*
    1637                 :      * Filter dupes inserted into small nodes to keep things clean and avoid
    1638                 :      * extra type constraints, but don't bother on large phi nodes to avoid
    1639                 :      * quadratic behavior.
    1640                 :      */
    1641           24915 :     if (node->length <= 8) {
    1642           35290 :         for (unsigned i = 0; i < node->length; i++) {
    1643           10546 :             if (v == node->options[i])
    1644             171 :                 return;
    1645                 :         }
    1646                 :     }
    1647                 : 
    1648           24744 :     if (trackUseChain(v)) {
    1649           22666 :         SSAUseChain *&uses = useChain(v);
    1650                 : 
    1651           22666 :         SSAUseChain *use = cx->typeLifoAlloc().new_<SSAUseChain>();
    1652           22666 :         if (!use) {
    1653               0 :             setOOM(cx);
    1654               0 :             return;
    1655                 :         }
    1656                 : 
    1657           22666 :         use->popped = false;
    1658           22666 :         use->offset = phi.phiOffset();
    1659           22666 :         use->u.phi = node;
    1660           22666 :         use->next = uses;
    1661           22666 :         uses = use;
    1662                 :     }
    1663                 : 
    1664           24744 :     if (node->length < PhiNodeCapacity(node->length)) {
    1665           24727 :         node->options[node->length++] = v;
    1666           24727 :         return;
    1667                 :     }
    1668                 : 
    1669                 :     SSAValue *newOptions =
    1670              17 :         cx->typeLifoAlloc().newArray<SSAValue>(PhiNodeCapacity(node->length + 1));
    1671              17 :     if (!newOptions) {
    1672               0 :         setOOM(cx);
    1673               0 :         return;
    1674                 :     }
    1675                 : 
    1676              17 :     PodCopy(newOptions, node->options, node->length);
    1677              17 :     node->options = newOptions;
    1678              17 :     node->options[node->length++] = v;
    1679                 : }
    1680                 : 
    1681                 : inline void
    1682           39761 : ScriptAnalysis::mergeValue(JSContext *cx, uint32_t offset, const SSAValue &v, SlotValue *pv)
    1683                 : {
    1684                 :     /* Make sure that v is accounted for in the pending value or phi value at pv. */
    1685           39761 :     JS_ASSERT(v.kind() != SSAValue::EMPTY && pv->value.kind() != SSAValue::EMPTY);
    1686                 : 
    1687           39761 :     if (v == pv->value)
    1688           24245 :         return;
    1689                 : 
    1690           15516 :     if (pv->value.kind() != SSAValue::PHI || pv->value.phiOffset() < offset) {
    1691            9383 :         SSAValue ov = pv->value;
    1692            9383 :         if (makePhi(cx, pv->slot, offset, &pv->value)) {
    1693            9383 :             insertPhi(cx, pv->value, v);
    1694            9383 :             insertPhi(cx, pv->value, ov);
    1695                 :         }
    1696            9383 :         return;
    1697                 :     }
    1698                 : 
    1699            6133 :     JS_ASSERT(pv->value.phiOffset() == offset);
    1700            6133 :     insertPhi(cx, pv->value, v);
    1701                 : }
    1702                 : 
    1703                 : void
    1704           58408 : ScriptAnalysis::checkPendingValue(JSContext *cx, const SSAValue &v, uint32_t slot,
    1705                 :                                   Vector<SlotValue> *pending)
    1706                 : {
    1707           58408 :     JS_ASSERT(v.kind() != SSAValue::EMPTY);
    1708                 : 
    1709        67206180 :     for (unsigned i = 0; i < pending->length(); i++) {
    1710        67148186 :         if ((*pending)[i].slot == slot)
    1711             414 :             return;
    1712                 :     }
    1713                 : 
    1714           57994 :     if (!pending->append(SlotValue(slot, v)))
    1715               0 :         setOOM(cx);
    1716                 : }
    1717                 : 
    1718                 : void
    1719           85628 : ScriptAnalysis::checkBranchTarget(JSContext *cx, uint32_t targetOffset,
    1720                 :                                   Vector<uint32_t> &branchTargets,
    1721                 :                                   SSAValueInfo *values, uint32_t stackDepth)
    1722                 : {
    1723           85628 :     unsigned targetDepth = getCode(targetOffset).stackDepth;
    1724           85628 :     JS_ASSERT(targetDepth <= stackDepth);
    1725                 : 
    1726                 :     /*
    1727                 :      * If there is already an active branch to target, make sure its pending
    1728                 :      * values reflect any changes made since the first branch. Otherwise, add a
    1729                 :      * new pending branch and determine its pending values lazily.
    1730                 :      */
    1731           85628 :     Vector<SlotValue> *&pending = getCode(targetOffset).pendingValues;
    1732           85628 :     if (pending) {
    1733           33489 :         for (unsigned i = 0; i < pending->length(); i++) {
    1734            6634 :             SlotValue &v = (*pending)[i];
    1735            6634 :             mergeValue(cx, targetOffset, values[v.slot].v, &v);
    1736                 :         }
    1737                 :     } else {
    1738           58773 :         pending = cx->new_< Vector<SlotValue> >(cx);
    1739           58773 :         if (!pending || !branchTargets.append(targetOffset)) {
    1740               0 :             setOOM(cx);
    1741               0 :             return;
    1742                 :         }
    1743                 :     }
    1744                 : 
    1745                 :     /*
    1746                 :      * Make sure there is a pending entry for each value on the stack.
    1747                 :      * The number of stack entries at join points is usually zero, and
    1748                 :      * we don't want to look at the active branches while popping and
    1749                 :      * pushing values in each opcode.
    1750                 :      */
    1751          136984 :     for (unsigned i = 0; i < targetDepth; i++) {
    1752           51356 :         uint32_t slot = StackSlot(script, i);
    1753           51356 :         checkPendingValue(cx, values[slot].v, slot, pending);
    1754                 :     }
    1755                 : }
    1756                 : 
    1757                 : void
    1758           15253 : ScriptAnalysis::checkExceptionTarget(JSContext *cx, uint32_t catchOffset,
    1759                 :                                      Vector<uint32_t> &exceptionTargets)
    1760                 : {
    1761           15253 :     JS_ASSERT(getCode(catchOffset).exceptionEntry);
    1762                 : 
    1763                 :     /*
    1764                 :      * The catch offset will already be in the branch targets, just check
    1765                 :      * whether this is already a known exception target.
    1766                 :      */
    1767           15271 :     for (unsigned i = 0; i < exceptionTargets.length(); i++) {
    1768              18 :         if (exceptionTargets[i] == catchOffset)
    1769               0 :             return;
    1770                 :     }
    1771           15253 :     if (!exceptionTargets.append(catchOffset))
    1772               0 :         setOOM(cx);
    1773                 : }
    1774                 : 
    1775                 : void
    1776           85660 : ScriptAnalysis::mergeBranchTarget(JSContext *cx, SSAValueInfo &value, uint32_t slot,
    1777                 :                                   const Vector<uint32_t> &branchTargets, uint32_t currentOffset)
    1778                 : {
    1779           85660 :     if (slot >= numSlots) {
    1780                 :         /*
    1781                 :          * There is no need to lazily check that there are pending values at
    1782                 :          * branch targets for slots on the stack, these are added to pending
    1783                 :          * eagerly.
    1784                 :          */
    1785           30743 :         return;
    1786                 :     }
    1787                 : 
    1788           54917 :     JS_ASSERT(trackSlot(slot));
    1789                 : 
    1790                 :     /*
    1791                 :      * Before changing the value of a variable, make sure the old value is
    1792                 :      * marked at the target of any branches jumping over the current opcode.
    1793                 :      * Only look at new branch targets which have appeared since the last time
    1794                 :      * the variable was written.
    1795                 :      */
    1796           69293 :     for (int i = branchTargets.length() - 1; i >= value.branchSize; i--) {
    1797           14376 :         if (branchTargets[i] <= currentOffset)
    1798            7324 :             continue;
    1799                 : 
    1800            7052 :         const Bytecode &code = getCode(branchTargets[i]);
    1801                 : 
    1802            7052 :         Vector<SlotValue> *pending = code.pendingValues;
    1803            7052 :         checkPendingValue(cx, value.v, slot, pending);
    1804                 :     }
    1805                 : 
    1806           54917 :     value.branchSize = branchTargets.length();
    1807                 : }
    1808                 : 
    1809                 : void
    1810           42348 : ScriptAnalysis::mergeExceptionTarget(JSContext *cx, const SSAValue &value, uint32_t slot,
    1811                 :                                      const Vector<uint32_t> &exceptionTargets)
    1812                 : {
    1813           42348 :     JS_ASSERT(trackSlot(slot));
    1814                 : 
    1815                 :     /*
    1816                 :      * Update the value at exception targets with the value of a variable
    1817                 :      * before it is overwritten. Unlike mergeBranchTarget, this is done whether
    1818                 :      * or not the overwritten value is the value of the variable at the
    1819                 :      * original branch. Values for a variable which are written after the
    1820                 :      * try block starts and overwritten before it is finished can still be
    1821                 :      * seen at exception handlers via exception paths.
    1822                 :      */
    1823           42557 :     for (unsigned i = 0; i < exceptionTargets.length(); i++) {
    1824             209 :         unsigned offset = exceptionTargets[i];
    1825             209 :         Vector<SlotValue> *pending = getCode(offset).pendingValues;
    1826                 : 
    1827             209 :         bool duplicate = false;
    1828             251 :         for (unsigned i = 0; i < pending->length(); i++) {
    1829             251 :             if ((*pending)[i].slot == slot) {
    1830             209 :                 duplicate = true;
    1831             209 :                 SlotValue &v = (*pending)[i];
    1832             209 :                 mergeValue(cx, offset, value, &v);
    1833             209 :                 break;
    1834                 :             }
    1835                 :         }
    1836                 : 
    1837             209 :         if (!duplicate && !pending->append(SlotValue(slot, value)))
    1838               0 :             setOOM(cx);
    1839                 :     }
    1840           42348 : }
    1841                 : 
    1842                 : void
    1843           40509 : ScriptAnalysis::mergeAllExceptionTargets(JSContext *cx, SSAValueInfo *values,
    1844                 :                                          const Vector<uint32_t> &exceptionTargets)
    1845                 : {
    1846           40629 :     for (unsigned i = 0; i < exceptionTargets.length(); i++) {
    1847             120 :         Vector<SlotValue> *pending = getCode(exceptionTargets[i]).pendingValues;
    1848             142 :         for (unsigned i = 0; i < pending->length(); i++) {
    1849              22 :             const SlotValue &v = (*pending)[i];
    1850              22 :             if (trackSlot(v.slot))
    1851              10 :                 mergeExceptionTarget(cx, values[v.slot].v, v.slot, exceptionTargets);
    1852                 :         }
    1853                 :     }
    1854           40509 : }
    1855                 : 
    1856                 : void
    1857           69565 : ScriptAnalysis::freezeNewValues(JSContext *cx, uint32_t offset)
    1858                 : {
    1859           69565 :     Bytecode &code = getCode(offset);
    1860                 : 
    1861           69565 :     Vector<SlotValue> *pending = code.pendingValues;
    1862           69565 :     code.pendingValues = NULL;
    1863                 : 
    1864           69565 :     unsigned count = pending->length();
    1865           69565 :     if (count == 0) {
    1866           53349 :         cx->delete_(pending);
    1867           53349 :         return;
    1868                 :     }
    1869                 : 
    1870           16216 :     code.newValues = cx->typeLifoAlloc().newArray<SlotValue>(count + 1);
    1871           16216 :     if (!code.newValues) {
    1872               0 :         setOOM(cx);
    1873               0 :         return;
    1874                 :     }
    1875                 : 
    1876           79737 :     for (unsigned i = 0; i < count; i++)
    1877           63521 :         code.newValues[i] = (*pending)[i];
    1878           16216 :     code.newValues[count].slot = 0;
    1879           16216 :     code.newValues[count].value.clear();
    1880                 : 
    1881           16216 :     cx->delete_(pending);
    1882                 : }
    1883                 : 
    1884                 : CrossSSAValue
    1885           25464 : CrossScriptSSA::foldValue(const CrossSSAValue &cv)
    1886                 : {
    1887           25464 :     const Frame &frame = getFrame(cv.frame);
    1888           25464 :     const SSAValue &v = cv.v;
    1889                 : 
    1890           25464 :     JSScript *parentScript = NULL;
    1891           25464 :     ScriptAnalysis *parentAnalysis = NULL;
    1892           25464 :     if (frame.parent != INVALID_FRAME) {
    1893             730 :         parentScript = getFrame(frame.parent).script;
    1894             730 :         parentAnalysis = parentScript->analysis();
    1895                 :     }
    1896                 : 
    1897           25464 :     if (v.kind() == SSAValue::VAR && v.varInitial() && parentScript) {
    1898              98 :         uint32_t slot = v.varSlot();
    1899              98 :         if (slot >= ArgSlot(0) && slot < LocalSlot(frame.script, 0)) {
    1900              98 :             uint32_t argc = GET_ARGC(frame.parentpc);
    1901              98 :             SSAValue argv = parentAnalysis->poppedValue(frame.parentpc, argc - 1 - (slot - ArgSlot(0)));
    1902              98 :             return foldValue(CrossSSAValue(frame.parent, argv));
    1903                 :         }
    1904                 :     }
    1905                 : 
    1906           25366 :     if (v.kind() == SSAValue::PUSHED) {
    1907           12803 :         jsbytecode *pc = frame.script->code + v.pushedOffset();
    1908                 : 
    1909           12803 :         switch (JSOp(*pc)) {
    1910                 :           case JSOP_THIS:
    1911             609 :             if (parentScript) {
    1912             349 :                 uint32_t argc = GET_ARGC(frame.parentpc);
    1913             349 :                 SSAValue thisv = parentAnalysis->poppedValue(frame.parentpc, argc);
    1914             349 :                 return foldValue(CrossSSAValue(frame.parent, thisv));
    1915                 :             }
    1916             260 :             break;
    1917                 : 
    1918                 :           case JSOP_CALL: {
    1919                 :             /*
    1920                 :              * If there is a single inline callee with a single return site,
    1921                 :              * propagate back to that.
    1922                 :              */
    1923              73 :             JSScript *callee = NULL;
    1924              73 :             uint32_t calleeFrame = INVALID_FRAME;
    1925             607 :             for (unsigned i = 0; i < numFrames(); i++) {
    1926             534 :                 if (iterFrame(i).parent == cv.frame && iterFrame(i).parentpc == pc) {
    1927              65 :                     if (callee)
    1928               0 :                         return cv;  /* Multiple callees */
    1929              65 :                     callee = iterFrame(i).script;
    1930              65 :                     calleeFrame = iterFrame(i).index;
    1931                 :                 }
    1932                 :             }
    1933              73 :             if (callee && callee->analysis()->numReturnSites() == 1) {
    1934              63 :                 ScriptAnalysis *analysis = callee->analysis();
    1935              63 :                 uint32_t offset = 0;
    1936             354 :                 while (offset < callee->length) {
    1937             291 :                     jsbytecode *pc = callee->code + offset;
    1938             291 :                     if (analysis->maybeCode(pc) && JSOp(*pc) == JSOP_RETURN)
    1939              63 :                         return foldValue(CrossSSAValue(calleeFrame, analysis->poppedValue(pc, 0)));
    1940             228 :                     offset += GetBytecodeLength(pc);
    1941                 :                 }
    1942                 :             }
    1943              10 :             break;
    1944                 :           }
    1945                 : 
    1946                 :           case JSOP_TOID: {
    1947                 :             /*
    1948                 :              * TOID acts as identity for integers, so to get better precision
    1949                 :              * we should propagate its popped values forward if it acted as
    1950                 :              * identity.
    1951                 :              */
    1952             344 :             ScriptAnalysis *analysis = frame.script->analysis();
    1953             344 :             SSAValue toidv = analysis->poppedValue(pc, 0);
    1954             344 :             if (analysis->getValueTypes(toidv)->getKnownTypeTag(cx) == JSVAL_TYPE_INT32)
    1955             338 :                 return foldValue(CrossSSAValue(cv.frame, toidv));
    1956               6 :             break;
    1957                 :           }
    1958                 : 
    1959                 :           default:;
    1960                 :         }
    1961                 :     }
    1962                 : 
    1963           24616 :     return cv;
    1964                 : }
    1965                 : 
    1966                 : #ifdef DEBUG
    1967                 : 
    1968                 : void
    1969               0 : ScriptAnalysis::printSSA(JSContext *cx)
    1970                 : {
    1971               0 :     AutoEnterAnalysis enter(cx);
    1972                 : 
    1973               0 :     printf("\n");
    1974                 : 
    1975               0 :     for (unsigned offset = 0; offset < script->length; offset++) {
    1976               0 :         Bytecode *code = maybeCode(offset);
    1977               0 :         if (!code)
    1978               0 :             continue;
    1979                 : 
    1980               0 :         jsbytecode *pc = script->code + offset;
    1981                 : 
    1982               0 :         PrintBytecode(cx, script, pc);
    1983                 : 
    1984               0 :         SlotValue *newv = code->newValues;
    1985               0 :         if (newv) {
    1986               0 :             while (newv->slot) {
    1987               0 :                 if (newv->value.kind() != SSAValue::PHI || newv->value.phiOffset() != offset) {
    1988               0 :                     newv++;
    1989               0 :                     continue;
    1990                 :                 }
    1991               0 :                 printf("  phi ");
    1992               0 :                 newv->value.print();
    1993               0 :                 printf(" [");
    1994               0 :                 for (unsigned i = 0; i < newv->value.phiLength(); i++) {
    1995               0 :                     if (i)
    1996               0 :                         printf(",");
    1997               0 :                     newv->value.phiValue(i).print();
    1998                 :                 }
    1999               0 :                 printf("]\n");
    2000               0 :                 newv++;
    2001                 :             }
    2002                 :         }
    2003                 : 
    2004               0 :         unsigned nuses = GetUseCount(script, offset);
    2005               0 :         unsigned xuses = ExtendedUse(pc) ? nuses + 1 : nuses;
    2006                 : 
    2007               0 :         for (unsigned i = 0; i < xuses; i++) {
    2008               0 :             printf("  popped%d: ", i);
    2009               0 :             code->poppedValues[i].print();
    2010               0 :             printf("\n");
    2011                 :         }
    2012                 :     }
    2013                 : 
    2014               0 :     printf("\n"); 
    2015               0 : }
    2016                 : 
    2017                 : void
    2018               0 : SSAValue::print() const
    2019                 : {
    2020               0 :     switch (kind()) {
    2021                 : 
    2022                 :       case EMPTY:
    2023               0 :         printf("empty");
    2024               0 :         break;
    2025                 : 
    2026                 :       case PUSHED:
    2027               0 :         printf("pushed:%05u#%u", pushedOffset(), pushedIndex());
    2028               0 :         break;
    2029                 : 
    2030                 :       case VAR:
    2031               0 :         if (varInitial())
    2032               0 :             printf("initial:%u", varSlot());
    2033                 :         else
    2034               0 :             printf("write:%05u", varOffset());
    2035               0 :         break;
    2036                 : 
    2037                 :       case PHI:
    2038               0 :         printf("phi:%05u#%u", phiOffset(), phiSlot());
    2039               0 :         break;
    2040                 : 
    2041                 :       default:
    2042               0 :         JS_NOT_REACHED("Bad kind");
    2043                 :     }
    2044               0 : }
    2045                 : 
    2046                 : void
    2047          136548 : ScriptAnalysis::assertMatchingDebugMode()
    2048                 : {
    2049          136548 :     JS_ASSERT(!!script->compartment()->debugMode() == !!originalDebugMode_);
    2050          136548 : }
    2051                 : 
    2052                 : #endif  /* DEBUG */
    2053                 : 
    2054                 : } /* namespace analyze */
    2055                 : } /* namespace js */

Generated by: LCOV version 1.7