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

LCOV - code coverage report
| Current view: | directory - js/src - jsanalyze.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 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