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