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 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : /* Definitions for javascript analysis. */
40 :
41 : #ifndef jsanalyze_h___
42 : #define jsanalyze_h___
43 :
44 : #include "jscompartment.h"
45 : #include "jscntxt.h"
46 : #include "jsinfer.h"
47 : #include "jsscript.h"
48 :
49 : #include "ds/LifoAlloc.h"
50 : #include "js/TemplateLib.h"
51 :
52 : struct JSScript;
53 :
54 : /* Forward declaration of downstream register allocations computed for join points. */
55 : namespace js { namespace mjit { struct RegisterAllocation; } }
56 :
57 : namespace js {
58 : namespace analyze {
59 :
60 : /*
61 : * There are three analyses we can perform on a JSScript, outlined below.
62 : * The results of all three are stored in ScriptAnalysis, but the analyses
63 : * themselves can be performed separately. Along with type inference results,
64 : * per-script analysis results are tied to the per-compartment analysis pool
65 : * and are freed on every garbage collection.
66 : *
67 : * - Basic bytecode analysis. For each bytecode, determine the stack depth at
68 : * that point and control flow information needed for compilation. Also does
69 : * a defined-variables analysis to look for local variables which have uses
70 : * before definitions.
71 : *
72 : * - Lifetime analysis. Makes a backwards pass over the script to approximate
73 : * the regions where each variable is live, avoiding a full fixpointing
74 : * live-variables pass. This is based on the algorithm described in:
75 : *
76 : * "Quality and Speed in Linear-scan Register Allocation"
77 : * Traub et. al.
78 : * PLDI, 1998
79 : *
80 : * - SSA analysis of the script's variables and stack values. For each stack
81 : * value popped and non-escaping local variable or argument read, determines
82 : * which push(es) or write(s) produced that value.
83 : *
84 : * Intermediate type inference results are additionally stored here. The above
85 : * analyses are independent from type inference.
86 : */
87 :
88 : /* Information about a bytecode instruction. */
89 : class Bytecode
90 : {
91 : friend class ScriptAnalysis;
92 :
93 : public:
94 134784039 : Bytecode() { PodZero(this); }
95 :
96 : /* --------- Bytecode analysis --------- */
97 :
98 : /* Whether there are any incoming jumps to this instruction. */
99 : bool jumpTarget : 1;
100 :
101 : /* Whether there is fallthrough to this instruction from a non-branching instruction. */
102 : bool fallthrough : 1;
103 :
104 : /* Whether this instruction is the fall through point of a conditional jump. */
105 : bool jumpFallthrough : 1;
106 :
107 : /* Whether this instruction can be branched to from a switch statement. Implies jumpTarget. */
108 : bool switchTarget : 1;
109 :
110 : /*
111 : * Whether this instruction must always execute, unless the script throws
112 : * an exception which it does not later catch.
113 : */
114 : bool unconditional : 1;
115 :
116 : /* Whether this instruction has been analyzed to get its output defines and stack. */
117 : bool analyzed : 1;
118 :
119 : /* Whether this is a catch/finally entry point. */
120 : bool exceptionEntry : 1;
121 :
122 : /* Whether this is in a try block. */
123 : bool inTryBlock : 1;
124 :
125 : /* Method JIT safe point. */
126 : bool safePoint : 1;
127 :
128 : /*
129 : * Side effects of this bytecode were not determined by type inference.
130 : * Either a property set with unknown lvalue, or call with unknown callee.
131 : */
132 : bool monitoredTypes : 1;
133 :
134 : /* Call whose result should be monitored. */
135 : bool monitoredTypesReturn : 1;
136 :
137 : /*
138 : * Dynamically observed state about the execution of this opcode. These are
139 : * hints about the script for use during compilation.
140 : */
141 : bool arrayWriteHole: 1; /* SETELEM which has written to an array hole. */
142 : bool getStringElement:1; /* GETELEM which has accessed string properties. */
143 : bool accessGetter: 1; /* Property read on a shape with a getter hook. */
144 :
145 : /* Stack depth before this opcode. */
146 : uint32_t stackDepth;
147 :
148 : private:
149 :
150 : union {
151 : /* If this is a JOF_TYPESET opcode, index into the observed types for the op. */
152 : types::TypeSet *observedTypes;
153 :
154 : /* If this is a loop head (TRACE or NOTRACE), information about the loop. */
155 : LoopAnalysis *loop;
156 : };
157 :
158 : /* --------- Lifetime analysis --------- */
159 :
160 : /* Any allocation computed downstream for this bytecode. */
161 : mjit::RegisterAllocation *allocation;
162 :
163 : /* --------- SSA analysis --------- */
164 :
165 : /* Generated location of each value popped by this bytecode. */
166 : SSAValue *poppedValues;
167 :
168 : /* Points where values pushed or written by this bytecode are popped. */
169 : SSAUseChain **pushedUses;
170 :
171 : union {
172 : /*
173 : * If this is a join point (implies jumpTarget), any slots at this
174 : * point which can have a different values than at the immediate
175 : * predecessor in the bytecode. Array is terminated by an entry with
176 : * a zero slot.
177 : */
178 : SlotValue *newValues;
179 :
180 : /*
181 : * Vector used during SSA analysis to store values in need of merging
182 : * at this point. If this has incoming forward jumps and we have not
183 : * yet reached this point, stores values for entries on the stack and
184 : * for variables which have changed since the branch. If this is a loop
185 : * head and we haven't reached the back edge yet, stores loop phi nodes
186 : * for variables and entries live at the head of the loop.
187 : */
188 : Vector<SlotValue> *pendingValues;
189 : };
190 :
191 : /* --------- Type inference --------- */
192 :
193 : /* Types for all values pushed by this bytecode. */
194 : types::TypeSet *pushedTypes;
195 :
196 : /* Any type barriers in place at this bytecode. */
197 : types::TypeBarrier *typeBarriers;
198 : };
199 :
200 : static inline unsigned
201 164837094 : GetDefCount(JSScript *script, unsigned offset)
202 : {
203 164837094 : JS_ASSERT(offset < script->length);
204 164837094 : jsbytecode *pc = script->code + offset;
205 :
206 : /*
207 : * Add an extra pushed value for OR/AND opcodes, so that they are included
208 : * in the pushed array of stack values for type inference.
209 : */
210 164837094 : switch (JSOp(*pc)) {
211 : case JSOP_OR:
212 : case JSOP_AND:
213 297975 : return 1;
214 : case JSOP_FILTER:
215 105 : return 2;
216 : case JSOP_PICK:
217 : /*
218 : * Pick pops and pushes how deep it looks in the stack + 1
219 : * items. i.e. if the stack were |a b[2] c[1] d[0]|, pick 2
220 : * would pop b, c, and d to rearrange the stack to |a c[0]
221 : * d[1] b[2]|.
222 : */
223 10081931 : return (pc[1] + 1);
224 : default:
225 154457083 : return StackDefs(script, pc);
226 : }
227 : }
228 :
229 : static inline unsigned
230 138034805 : GetUseCount(JSScript *script, unsigned offset)
231 : {
232 138034805 : JS_ASSERT(offset < script->length);
233 138034805 : jsbytecode *pc = script->code + offset;
234 :
235 138034805 : if (JSOp(*pc) == JSOP_PICK)
236 10045098 : return (pc[1] + 1);
237 127989707 : if (js_CodeSpec[*pc].nuses == -1)
238 4390751 : return StackUses(script, pc);
239 123598956 : return js_CodeSpec[*pc].nuses;
240 : }
241 :
242 : /*
243 : * For opcodes which assign to a local variable or argument, track an extra def
244 : * during SSA analysis for the value's use chain and assigned type.
245 : */
246 : static inline bool
247 24193355 : ExtendedDef(jsbytecode *pc)
248 : {
249 24193355 : switch ((JSOp)*pc) {
250 : case JSOP_SETARG:
251 : case JSOP_INCARG:
252 : case JSOP_DECARG:
253 : case JSOP_ARGINC:
254 : case JSOP_ARGDEC:
255 : case JSOP_SETLOCAL:
256 : case JSOP_SETLOCALPOP:
257 : case JSOP_DEFLOCALFUN:
258 : case JSOP_DEFLOCALFUN_FC:
259 : case JSOP_INCLOCAL:
260 : case JSOP_DECLOCAL:
261 : case JSOP_LOCALINC:
262 : case JSOP_LOCALDEC:
263 1235604 : return true;
264 : default:
265 22957751 : return false;
266 : }
267 : }
268 :
269 : /* Return whether op bytecodes do not fallthrough (they may do a jump). */
270 : static inline bool
271 134787239 : BytecodeNoFallThrough(JSOp op)
272 : {
273 134787239 : switch (op) {
274 : case JSOP_GOTO:
275 : case JSOP_DEFAULT:
276 : case JSOP_RETURN:
277 : case JSOP_STOP:
278 : case JSOP_RETRVAL:
279 : case JSOP_THROW:
280 : case JSOP_TABLESWITCH:
281 : case JSOP_LOOKUPSWITCH:
282 : case JSOP_FILTER:
283 2223350 : return true;
284 : case JSOP_GOSUB:
285 : /* These fall through indirectly, after executing a 'finally'. */
286 19693 : return false;
287 : default:
288 132544196 : return false;
289 : }
290 : }
291 :
292 : /*
293 : * For opcodes which access local variables or arguments, we track an extra
294 : * use during SSA analysis for the value of the variable before/after the op.
295 : */
296 : static inline bool
297 4568256 : ExtendedUse(jsbytecode *pc)
298 : {
299 4568256 : if (ExtendedDef(pc))
300 259565 : return true;
301 4308691 : switch ((JSOp)*pc) {
302 : case JSOP_GETARG:
303 : case JSOP_CALLARG:
304 : case JSOP_GETLOCAL:
305 : case JSOP_CALLLOCAL:
306 159002 : return true;
307 : default:
308 4149689 : return false;
309 : }
310 : }
311 :
312 : static inline JSOp
313 141 : ReverseCompareOp(JSOp op)
314 : {
315 141 : switch (op) {
316 : case JSOP_GT:
317 69 : return JSOP_LT;
318 : case JSOP_GE:
319 32 : return JSOP_LE;
320 : case JSOP_LT:
321 25 : return JSOP_GT;
322 : case JSOP_LE:
323 15 : return JSOP_GE;
324 : default:
325 0 : JS_NOT_REACHED("unrecognized op");
326 : return op;
327 : }
328 : }
329 :
330 : static inline unsigned
331 292386 : FollowBranch(JSContext *cx, JSScript *script, unsigned offset)
332 : {
333 : /*
334 : * Get the target offset of a branch. For GOTO opcodes implementing
335 : * 'continue' statements, short circuit any artificial backwards jump
336 : * inserted by the emitter.
337 : */
338 292386 : jsbytecode *pc = script->code + offset;
339 292386 : unsigned targetOffset = offset + GET_JUMP_OFFSET(pc);
340 292386 : if (targetOffset < offset) {
341 22308 : jsbytecode *target = script->code + targetOffset;
342 22308 : JSOp nop = JSOp(*target);
343 22308 : if (nop == JSOP_GOTO)
344 128 : return targetOffset + GET_JUMP_OFFSET(target);
345 : }
346 292258 : return targetOffset;
347 : }
348 :
349 : /* Common representation of slots throughout analyses and the compiler. */
350 270927 : static inline uint32_t CalleeSlot() {
351 270927 : return 0;
352 : }
353 29456986 : static inline uint32_t ThisSlot() {
354 29456986 : return 1;
355 : }
356 46375392 : static inline uint32_t ArgSlot(uint32_t arg) {
357 46375392 : return 2 + arg;
358 : }
359 6347974 : static inline uint32_t LocalSlot(JSScript *script, uint32_t local) {
360 6347974 : return 2 + (script->function() ? script->function()->nargs : 0) + local;
361 : }
362 3048045 : static inline uint32_t TotalSlots(JSScript *script) {
363 3048045 : return LocalSlot(script, 0) + script->nfixed;
364 : }
365 :
366 51356 : static inline uint32_t StackSlot(JSScript *script, uint32_t index) {
367 51356 : return TotalSlots(script) + index;
368 : }
369 :
370 1481654 : static inline uint32_t GetBytecodeSlot(JSScript *script, jsbytecode *pc)
371 : {
372 1481654 : switch (JSOp(*pc)) {
373 :
374 : case JSOP_GETARG:
375 : case JSOP_CALLARG:
376 : case JSOP_SETARG:
377 : case JSOP_INCARG:
378 : case JSOP_DECARG:
379 : case JSOP_ARGINC:
380 : case JSOP_ARGDEC:
381 177143 : return ArgSlot(GET_SLOTNO(pc));
382 :
383 : case JSOP_GETLOCAL:
384 : case JSOP_CALLLOCAL:
385 : case JSOP_SETLOCAL:
386 : case JSOP_SETLOCALPOP:
387 : case JSOP_DEFLOCALFUN:
388 : case JSOP_DEFLOCALFUN_FC:
389 : case JSOP_INCLOCAL:
390 : case JSOP_DECLOCAL:
391 : case JSOP_LOCALINC:
392 : case JSOP_LOCALDEC:
393 1291442 : return LocalSlot(script, GET_SLOTNO(pc));
394 :
395 : case JSOP_THIS:
396 13069 : return ThisSlot();
397 :
398 : default:
399 0 : JS_NOT_REACHED("Bad slot opcode");
400 : return 0;
401 : }
402 : }
403 :
404 : /* Slot opcodes which update SSA information. */
405 : static inline bool
406 3933441 : BytecodeUpdatesSlot(JSOp op)
407 : {
408 3933441 : switch (op) {
409 : case JSOP_SETARG:
410 : case JSOP_SETLOCAL:
411 : case JSOP_SETLOCALPOP:
412 : case JSOP_DEFLOCALFUN:
413 : case JSOP_DEFLOCALFUN_FC:
414 : case JSOP_INCARG:
415 : case JSOP_DECARG:
416 : case JSOP_ARGINC:
417 : case JSOP_ARGDEC:
418 : case JSOP_INCLOCAL:
419 : case JSOP_DECLOCAL:
420 : case JSOP_LOCALINC:
421 : case JSOP_LOCALDEC:
422 313834 : return true;
423 : default:
424 3619607 : return false;
425 : }
426 : }
427 :
428 : static inline int32_t
429 15600 : GetBytecodeInteger(jsbytecode *pc)
430 : {
431 15600 : switch (JSOp(*pc)) {
432 693 : case JSOP_ZERO: return 0;
433 248 : case JSOP_ONE: return 1;
434 2939 : case JSOP_UINT16: return GET_UINT16(pc);
435 24 : case JSOP_UINT24: return GET_UINT24(pc);
436 11686 : case JSOP_INT8: return GET_INT8(pc);
437 10 : case JSOP_INT32: return GET_INT32(pc);
438 : default:
439 0 : JS_NOT_REACHED("Bad op");
440 : return 0;
441 : }
442 : }
443 :
444 : /*
445 : * Information about the lifetime of a local or argument. These form a linked
446 : * list describing successive intervals in the program where the variable's
447 : * value may be live. At points in the script not in one of these segments
448 : * (points in a 'lifetime hole'), the variable is dead and registers containing
449 : * its type/payload can be discarded without needing to be synced.
450 : */
451 : struct Lifetime
452 : {
453 : /*
454 : * Start and end offsets of this lifetime. The variable is live at the
455 : * beginning of every bytecode in this (inclusive) range.
456 : */
457 : uint32_t start;
458 : uint32_t end;
459 :
460 : /*
461 : * In a loop body, endpoint to extend this lifetime with if the variable is
462 : * live in the next iteration.
463 : */
464 : uint32_t savedEnd;
465 :
466 : /*
467 : * This is an artificial segment extending the lifetime of this variable
468 : * when it is live at the head of the loop. It will not be used until the
469 : * next iteration.
470 : */
471 : bool loopTail;
472 :
473 : /*
474 : * The start of this lifetime is a bytecode writing the variable. Each
475 : * write to a variable is associated with a lifetime.
476 : */
477 : bool write;
478 :
479 : /* Next lifetime. The variable is dead from this->end to next->start. */
480 : Lifetime *next;
481 :
482 61261 : Lifetime(uint32_t offset, uint32_t savedEnd, Lifetime *next)
483 : : start(offset), end(offset), savedEnd(savedEnd),
484 61261 : loopTail(false), write(false), next(next)
485 61261 : {}
486 : };
487 :
488 : /* Basic information for a loop. */
489 : class LoopAnalysis
490 : {
491 : public:
492 : /* Any loop this one is nested in. */
493 : LoopAnalysis *parent;
494 :
495 : /* Offset of the head of the loop. */
496 : uint32_t head;
497 :
498 : /*
499 : * Offset of the unique jump going to the head of the loop. The code
500 : * between the head and the backedge forms the loop body.
501 : */
502 : uint32_t backedge;
503 :
504 : /* Target offset of the initial jump or fallthrough into the loop. */
505 : uint32_t entry;
506 :
507 : /*
508 : * Start of the last basic block in the loop, excluding the initial jump to
509 : * entry. All code between lastBlock and the backedge runs in every
510 : * iteration, and if entry >= lastBlock all code between entry and the
511 : * backedge runs when the loop is initially entered.
512 : */
513 : uint32_t lastBlock;
514 :
515 : /*
516 : * This loop contains safe points in its body which the interpreter might
517 : * join at directly.
518 : */
519 : bool hasSafePoints;
520 :
521 : /* This loop has calls or inner loops. */
522 : bool hasCallsLoops;
523 : };
524 :
525 : /* Current lifetime information for a variable. */
526 : struct LifetimeVariable
527 : {
528 : /* If the variable is currently live, the lifetime segment. */
529 : Lifetime *lifetime;
530 :
531 : /* If the variable is currently dead, the next live segment. */
532 : Lifetime *saved;
533 :
534 : /* Jump preceding the basic block which killed this variable. */
535 : uint32_t savedEnd : 31;
536 :
537 : /* If the variable needs to be kept alive until lifetime->start. */
538 : bool ensured : 1;
539 :
540 : /* Whether this variable is live at offset. */
541 210816 : Lifetime * live(uint32_t offset) const {
542 210816 : if (lifetime && lifetime->end >= offset)
543 18730 : return lifetime;
544 192086 : Lifetime *segment = lifetime ? lifetime : saved;
545 1009620 : while (segment && segment->start <= offset) {
546 729470 : if (segment->end >= offset)
547 104022 : return segment;
548 625448 : segment = segment->next;
549 : }
550 88064 : return NULL;
551 : }
552 :
553 : /*
554 : * Get the offset of the first write to the variable in an inclusive range,
555 : * UINT32_MAX if the variable is not written in the range.
556 : */
557 33508 : uint32_t firstWrite(uint32_t start, uint32_t end) const {
558 33508 : Lifetime *segment = lifetime ? lifetime : saved;
559 215156 : while (segment && segment->start <= end) {
560 154798 : if (segment->start >= start && segment->write)
561 6658 : return segment->start;
562 148140 : segment = segment->next;
563 : }
564 26850 : return UINT32_MAX;
565 : }
566 23543 : uint32_t firstWrite(LoopAnalysis *loop) const {
567 23543 : return firstWrite(loop->head, loop->backedge);
568 : }
569 :
570 : /* Return true if the variable cannot decrease during the body of a loop. */
571 719 : bool nonDecreasing(JSScript *script, LoopAnalysis *loop) const {
572 719 : Lifetime *segment = lifetime ? lifetime : saved;
573 4743 : while (segment && segment->start <= loop->backedge) {
574 3363 : if (segment->start >= loop->head && segment->write) {
575 719 : switch (JSOp(script->code[segment->start])) {
576 : case JSOP_INCLOCAL:
577 : case JSOP_LOCALINC:
578 : case JSOP_INCARG:
579 : case JSOP_ARGINC:
580 : break;
581 : default:
582 58 : return false;
583 : }
584 : }
585 3305 : segment = segment->next;
586 : }
587 661 : return true;
588 : }
589 :
590 : /*
591 : * If the variable is only written once in the body of a loop, offset of
592 : * that write. UINT32_MAX otherwise.
593 : */
594 56367 : uint32_t onlyWrite(LoopAnalysis *loop) const {
595 56367 : uint32_t offset = UINT32_MAX;
596 56367 : Lifetime *segment = lifetime ? lifetime : saved;
597 301508 : while (segment && segment->start <= loop->backedge) {
598 190327 : if (segment->start >= loop->head && segment->write) {
599 20347 : if (offset != UINT32_MAX)
600 1553 : return UINT32_MAX;
601 18794 : offset = segment->start;
602 : }
603 188774 : segment = segment->next;
604 : }
605 54814 : return offset;
606 : }
607 :
608 : #ifdef DEBUG
609 : void print() const;
610 : #endif
611 : };
612 :
613 : struct SSAPhiNode;
614 :
615 : /*
616 : * Representation of values on stack or in slots at each point in the script.
617 : * Values are independent from the bytecode position, and mean the same thing
618 : * everywhere in the script. SSA values are immutable, except for contents of
619 : * the values and types in an SSAPhiNode.
620 : */
621 : class SSAValue
622 : {
623 : friend class ScriptAnalysis;
624 :
625 : public:
626 : enum Kind {
627 : EMPTY = 0, /* Invalid entry. */
628 : PUSHED = 1, /* Value pushed by some bytecode. */
629 : VAR = 2, /* Initial or written value to some argument or local. */
630 : PHI = 3 /* Selector for one of several values. */
631 : };
632 :
633 29651796 : Kind kind() const {
634 29651796 : JS_ASSERT(u.pushed.kind == u.var.kind && u.pushed.kind == u.phi.kind);
635 :
636 : /* Use a bitmask because MSVC wants to use -1 for PHI nodes. */
637 29651796 : return (Kind) (u.pushed.kind & 0x3);
638 : }
639 :
640 51178 : bool operator==(const SSAValue &o) const {
641 51178 : return !memcmp(this, &o, sizeof(SSAValue));
642 : }
643 :
644 : /* Accessors for values pushed by a bytecode within this script. */
645 :
646 4046203 : uint32_t pushedOffset() const {
647 4046203 : JS_ASSERT(kind() == PUSHED);
648 4046203 : return u.pushed.offset;
649 : }
650 :
651 3926693 : uint32_t pushedIndex() const {
652 3926693 : JS_ASSERT(kind() == PUSHED);
653 3926693 : return u.pushed.index;
654 : }
655 :
656 : /* Accessors for initial and written values of arguments and (undefined) locals. */
657 :
658 468498 : bool varInitial() const {
659 468498 : JS_ASSERT(kind() == VAR);
660 468498 : return u.var.initial;
661 : }
662 :
663 329679 : uint32_t varSlot() const {
664 329679 : JS_ASSERT(kind() == VAR);
665 329679 : return u.var.slot;
666 : }
667 :
668 184627 : uint32_t varOffset() const {
669 184627 : JS_ASSERT(!varInitial());
670 184627 : return u.var.offset;
671 : }
672 :
673 : /* Accessors for phi nodes. */
674 :
675 : uint32_t phiSlot() const;
676 : uint32_t phiLength() const;
677 : const SSAValue &phiValue(uint32_t i) const;
678 : types::TypeSet *phiTypes() const;
679 :
680 : /* Offset at which this phi node was created. */
681 156266 : uint32_t phiOffset() const {
682 156266 : JS_ASSERT(kind() == PHI);
683 156266 : return u.phi.offset;
684 : }
685 :
686 321047 : SSAPhiNode *phiNode() const {
687 321047 : JS_ASSERT(kind() == PHI);
688 321047 : return u.phi.node;
689 : }
690 :
691 : /* Other accessors. */
692 :
693 : #ifdef DEBUG
694 : void print() const;
695 : #endif
696 :
697 4232001 : void clear() {
698 4232001 : PodZero(this);
699 4232001 : JS_ASSERT(kind() == EMPTY);
700 4232001 : }
701 :
702 2330699 : void initPushed(uint32_t offset, uint32_t index) {
703 2330699 : clear();
704 2330699 : u.pushed.kind = PUSHED;
705 2330699 : u.pushed.offset = offset;
706 2330699 : u.pushed.index = index;
707 2330699 : }
708 :
709 656281 : static SSAValue PushedValue(uint32_t offset, uint32_t index) {
710 : SSAValue v;
711 656281 : v.initPushed(offset, index);
712 : return v;
713 : }
714 :
715 12559 : void initInitial(uint32_t slot) {
716 12559 : clear();
717 12559 : u.var.kind = VAR;
718 12559 : u.var.initial = true;
719 12559 : u.var.slot = slot;
720 12559 : }
721 :
722 42507 : void initWritten(uint32_t slot, uint32_t offset) {
723 42507 : clear();
724 42507 : u.var.kind = VAR;
725 42507 : u.var.initial = false;
726 42507 : u.var.slot = slot;
727 42507 : u.var.offset = offset;
728 42507 : }
729 :
730 84 : static SSAValue WrittenVar(uint32_t slot, uint32_t offset) {
731 : SSAValue v;
732 84 : v.initWritten(slot, offset);
733 : return v;
734 : }
735 :
736 14965 : void initPhi(uint32_t offset, SSAPhiNode *node) {
737 14965 : clear();
738 14965 : u.phi.kind = PHI;
739 14965 : u.phi.offset = offset;
740 14965 : u.phi.node = node;
741 14965 : }
742 :
743 55 : static SSAValue PhiValue(uint32_t offset, SSAPhiNode *node) {
744 : SSAValue v;
745 55 : v.initPhi(offset, node);
746 : return v;
747 : }
748 :
749 : private:
750 : union {
751 : struct {
752 : Kind kind : 2;
753 : uint32_t offset : 30;
754 : uint32_t index;
755 : } pushed;
756 : struct {
757 : Kind kind : 2;
758 : bool initial : 1;
759 : uint32_t slot : 29;
760 : uint32_t offset;
761 : } var;
762 : struct {
763 : Kind kind : 2;
764 : uint32_t offset : 30;
765 : SSAPhiNode *node;
766 : } phi;
767 : } u;
768 : };
769 :
770 : /*
771 : * Mutable component of a phi node, with the possible values of the phi
772 : * and the possible types of the node as determined by type inference.
773 : * When phi nodes are copied around, any updates to the original will
774 : * be seen by all copies made.
775 : */
776 : struct SSAPhiNode
777 : {
778 : types::TypeSet types;
779 : uint32_t slot;
780 : uint32_t length;
781 : SSAValue *options;
782 : SSAUseChain *uses;
783 14910 : SSAPhiNode() { PodZero(this); }
784 : };
785 :
786 : inline uint32_t
787 18458 : SSAValue::phiSlot() const
788 : {
789 18458 : return u.phi.node->slot;
790 : }
791 :
792 : inline uint32_t
793 0 : SSAValue::phiLength() const
794 : {
795 0 : JS_ASSERT(kind() == PHI);
796 0 : return u.phi.node->length;
797 : }
798 :
799 : inline const SSAValue &
800 0 : SSAValue::phiValue(uint32_t i) const
801 : {
802 0 : JS_ASSERT(kind() == PHI && i < phiLength());
803 0 : return u.phi.node->options[i];
804 : }
805 :
806 : inline types::TypeSet *
807 : SSAValue::phiTypes() const
808 : {
809 : JS_ASSERT(kind() == PHI);
810 : return &u.phi.node->types;
811 : }
812 :
813 : class SSAUseChain
814 : {
815 : public:
816 : bool popped : 1;
817 : uint32_t offset : 31;
818 : /* FIXME: Assert that only the proper arm of this union is accessed. */
819 : union {
820 : uint32_t which;
821 : SSAPhiNode *phi;
822 : } u;
823 : SSAUseChain *next;
824 :
825 22666 : SSAUseChain() { PodZero(this); }
826 : };
827 :
828 : class SlotValue
829 : {
830 : public:
831 : uint32_t slot;
832 : SSAValue value;
833 63521 : SlotValue(uint32_t slot, const SSAValue &value) : slot(slot), value(value) {}
834 : };
835 :
836 : /* Analysis information about a script. */
837 : class ScriptAnalysis
838 : {
839 : friend class Bytecode;
840 :
841 : JSScript *script;
842 :
843 : Bytecode **codeArray;
844 :
845 : uint32_t numSlots;
846 :
847 : bool outOfMemory;
848 : bool hadFailure;
849 :
850 : bool *escapedSlots;
851 :
852 : /* Which analyses have been performed. */
853 : bool ranBytecode_;
854 : bool ranSSA_;
855 : bool ranLifetimes_;
856 : bool ranInference_;
857 :
858 : #ifdef DEBUG
859 : /* Whether the compartment was in debug mode when we performed the analysis. */
860 : bool originalDebugMode_: 1;
861 : #endif
862 :
863 : /* --------- Bytecode analysis --------- */
864 :
865 : bool usesReturnValue_:1;
866 : bool usesScopeChain_:1;
867 : bool usesThisValue_:1;
868 : bool hasFunctionCalls_:1;
869 : bool modifiesArguments_:1;
870 : bool extendsScope_:1;
871 : bool addsScopeObjects_:1;
872 : bool localsAliasStack_:1;
873 : bool isInlineable:1;
874 : bool isCompileable:1;
875 : bool canTrackVars:1;
876 :
877 : uint32_t numReturnSites_;
878 :
879 : /* --------- Lifetime analysis --------- */
880 :
881 : LifetimeVariable *lifetimes;
882 :
883 : public:
884 :
885 900984 : ScriptAnalysis(JSScript *script) {
886 900984 : PodZero(this);
887 900984 : this->script = script;
888 : #ifdef DEBUG
889 900984 : this->originalDebugMode_ = script->compartment()->debugMode();
890 : #endif
891 900984 : }
892 :
893 111634636 : bool ranBytecode() { return ranBytecode_; }
894 77564 : bool ranSSA() { return ranSSA_; }
895 489683 : bool ranLifetimes() { return ranLifetimes_; }
896 862832469 : bool ranInference() { return ranInference_; }
897 :
898 : void analyzeBytecode(JSContext *cx);
899 : void analyzeSSA(JSContext *cx);
900 : void analyzeLifetimes(JSContext *cx);
901 : void analyzeTypes(JSContext *cx);
902 :
903 : /* Analyze the effect of invoking 'new' on script. */
904 : void analyzeTypesNew(JSContext *cx);
905 :
906 5404399 : bool OOM() { return outOfMemory; }
907 1212362 : bool failed() { return hadFailure; }
908 4645 : bool inlineable(uint32_t argc) { return isInlineable && argc == script->function()->nargs; }
909 141758 : bool compileable() { return isCompileable; }
910 :
911 : /* Whether there are POPV/SETRVAL bytecodes which can write to the frame's rval. */
912 194132 : bool usesReturnValue() const { return usesReturnValue_; }
913 :
914 : /* Whether there are NAME bytecodes which can access the frame's scope chain. */
915 84483 : bool usesScopeChain() const { return usesScopeChain_; }
916 :
917 3033 : bool usesThisValue() const { return usesThisValue_; }
918 24289 : bool hasFunctionCalls() const { return hasFunctionCalls_; }
919 3060 : uint32_t numReturnSites() const { return numReturnSites_; }
920 :
921 : /*
922 : * True if all named formal arguments are not modified. If the arguments
923 : * object cannot escape, the arguments are never modified within the script.
924 : */
925 201 : bool modifiesArguments() { return modifiesArguments_; }
926 :
927 : /*
928 : * True if the script may extend declarations in its top level scope with
929 : * dynamic fun/var declarations or through eval.
930 : */
931 1130 : bool extendsScope() { return extendsScope_; }
932 :
933 : /* True if the script may add block or with objects to its scope chain. */
934 77104 : bool addsScopeObjects() { return addsScopeObjects_; }
935 :
936 : /*
937 : * True if there are any LOCAL opcodes aliasing values on the stack (above
938 : * script->nfixed).
939 : */
940 25090 : bool localsAliasStack() { return localsAliasStack_; }
941 :
942 : /* Accessors for bytecode information. */
943 :
944 110850434 : Bytecode& getCode(uint32_t offset) {
945 110850434 : JS_ASSERT(offset < script->length);
946 110850434 : JS_ASSERT(codeArray[offset]);
947 110850434 : return *codeArray[offset];
948 : }
949 84871463 : Bytecode& getCode(const jsbytecode *pc) { return getCode(pc - script->code); }
950 :
951 251870218 : Bytecode* maybeCode(uint32_t offset) {
952 251870218 : JS_ASSERT(offset < script->length);
953 251870218 : return codeArray[offset];
954 : }
955 19230293 : Bytecode* maybeCode(const jsbytecode *pc) { return maybeCode(pc - script->code); }
956 :
957 1003928 : bool jumpTarget(uint32_t offset) {
958 1003928 : JS_ASSERT(offset < script->length);
959 1003928 : return codeArray[offset] && codeArray[offset]->jumpTarget;
960 : }
961 1003928 : bool jumpTarget(const jsbytecode *pc) { return jumpTarget(pc - script->code); }
962 :
963 69524 : bool popGuaranteed(jsbytecode *pc) {
964 69524 : jsbytecode *next = pc + GetBytecodeLength(pc);
965 69524 : return JSOp(*next) == JSOP_POP && !jumpTarget(next);
966 : }
967 :
968 39085 : bool incrementInitialValueObserved(jsbytecode *pc) {
969 39085 : const JSCodeSpec *cs = &js_CodeSpec[*pc];
970 39085 : return (cs->format & JOF_POST) && !popGuaranteed(pc);
971 : }
972 :
973 77763024 : types::TypeSet *bytecodeTypes(const jsbytecode *pc) {
974 77763024 : JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET);
975 77763024 : return getCode(pc).observedTypes;
976 : }
977 :
978 2564212 : const SSAValue &poppedValue(uint32_t offset, uint32_t which) {
979 2564212 : JS_ASSERT(offset < script->length);
980 2564212 : JS_ASSERT(which < GetUseCount(script, offset) +
981 2564212 : (ExtendedUse(script->code + offset) ? 1 : 0));
982 2564212 : return getCode(offset).poppedValues[which];
983 : }
984 2530968 : const SSAValue &poppedValue(const jsbytecode *pc, uint32_t which) {
985 2530968 : return poppedValue(pc - script->code, which);
986 : }
987 :
988 432985 : const SlotValue *newValues(uint32_t offset) {
989 432985 : JS_ASSERT(offset < script->length);
990 432985 : return getCode(offset).newValues;
991 : }
992 432985 : const SlotValue *newValues(const jsbytecode *pc) { return newValues(pc - script->code); }
993 :
994 15609450 : types::TypeSet *pushedTypes(uint32_t offset, uint32_t which = 0) {
995 15609450 : JS_ASSERT(offset < script->length);
996 15609450 : JS_ASSERT(which < GetDefCount(script, offset) +
997 15609450 : (ExtendedDef(script->code + offset) ? 1 : 0));
998 15609450 : types::TypeSet *array = getCode(offset).pushedTypes;
999 15609450 : JS_ASSERT(array);
1000 15609450 : return array + which;
1001 : }
1002 11575522 : types::TypeSet *pushedTypes(const jsbytecode *pc, uint32_t which) {
1003 11575522 : return pushedTypes(pc - script->code, which);
1004 : }
1005 :
1006 : bool hasPushedTypes(const jsbytecode *pc) { return getCode(pc).pushedTypes != NULL; }
1007 :
1008 514462 : types::TypeBarrier *typeBarriers(JSContext *cx, uint32_t offset) {
1009 514462 : if (getCode(offset).typeBarriers)
1010 296774 : pruneTypeBarriers(cx, offset);
1011 514462 : return getCode(offset).typeBarriers;
1012 : }
1013 514462 : types::TypeBarrier *typeBarriers(JSContext *cx, const jsbytecode *pc) {
1014 514462 : return typeBarriers(cx, pc - script->code);
1015 : }
1016 : void addTypeBarrier(JSContext *cx, const jsbytecode *pc,
1017 : types::TypeSet *target, types::Type type);
1018 : void addSingletonTypeBarrier(JSContext *cx, const jsbytecode *pc,
1019 : types::TypeSet *target, JSObject *singleton, jsid singletonId);
1020 :
1021 : /* Remove obsolete type barriers at the given offset. */
1022 : void pruneTypeBarriers(JSContext *cx, uint32_t offset);
1023 :
1024 : /*
1025 : * Remove still-active type barriers at the given offset. If 'all' is set,
1026 : * then all barriers are removed, otherwise only those deemed excessive
1027 : * are removed.
1028 : */
1029 : void breakTypeBarriers(JSContext *cx, uint32_t offset, bool all);
1030 :
1031 : /* Break all type barriers used in computing v. */
1032 : void breakTypeBarriersSSA(JSContext *cx, const SSAValue &v);
1033 :
1034 : inline void addPushedType(JSContext *cx, uint32_t offset, uint32_t which, types::Type type);
1035 :
1036 2563102 : types::TypeSet *getValueTypes(const SSAValue &v) {
1037 2563102 : switch (v.kind()) {
1038 : case SSAValue::PUSHED:
1039 2240512 : return pushedTypes(v.pushedOffset(), v.pushedIndex());
1040 : case SSAValue::VAR:
1041 109291 : JS_ASSERT(!slotEscapes(v.varSlot()));
1042 109291 : if (v.varInitial()) {
1043 54378 : return types::TypeScript::SlotTypes(script, v.varSlot());
1044 : } else {
1045 : /*
1046 : * Results of intermediate assignments have the same type as
1047 : * the first type pushed by the assignment op. Note that this
1048 : * may not be the exact same value as was pushed, due to
1049 : * post-inc/dec ops.
1050 : */
1051 54913 : return pushedTypes(v.varOffset(), 0);
1052 : }
1053 : case SSAValue::PHI:
1054 213299 : return &v.phiNode()->types;
1055 : default:
1056 : /* Cannot compute types for empty SSA values. */
1057 0 : JS_NOT_REACHED("Bad SSA value");
1058 : return NULL;
1059 : }
1060 : }
1061 :
1062 4 : types::TypeSet *poppedTypes(uint32_t offset, uint32_t which) {
1063 4 : return getValueTypes(poppedValue(offset, which));
1064 : }
1065 2367893 : types::TypeSet *poppedTypes(const jsbytecode *pc, uint32_t which) {
1066 2367893 : return getValueTypes(poppedValue(pc, which));
1067 : }
1068 :
1069 : /* Whether an arithmetic operation is operating on integers, with an integer result. */
1070 : bool integerOperation(JSContext *cx, jsbytecode *pc);
1071 :
1072 3724426 : bool trackUseChain(const SSAValue &v) {
1073 3724426 : JS_ASSERT_IF(v.kind() == SSAValue::VAR, trackSlot(v.varSlot()));
1074 3724426 : return v.kind() != SSAValue::EMPTY &&
1075 3724426 : (v.kind() != SSAValue::VAR || !v.varInitial());
1076 : }
1077 :
1078 : /*
1079 : * Get the use chain for an SSA value. May be invalid for some opcodes in
1080 : * scripts where localsAliasStack(). You have been warned!
1081 : */
1082 1801327 : SSAUseChain *& useChain(const SSAValue &v) {
1083 1801327 : JS_ASSERT(trackUseChain(v));
1084 1801327 : if (v.kind() == SSAValue::PUSHED)
1085 1686181 : return getCode(v.pushedOffset()).pushedUses[v.pushedIndex()];
1086 115146 : if (v.kind() == SSAValue::VAR)
1087 62133 : return getCode(v.varOffset()).pushedUses[GetDefCount(script, v.varOffset())];
1088 53013 : return v.phiNode()->uses;
1089 : }
1090 :
1091 1026727 : mjit::RegisterAllocation *&getAllocation(uint32_t offset) {
1092 1026727 : JS_ASSERT(offset < script->length);
1093 1026727 : return getCode(offset).allocation;
1094 : }
1095 1026085 : mjit::RegisterAllocation *&getAllocation(const jsbytecode *pc) {
1096 1026085 : return getAllocation(pc - script->code);
1097 : }
1098 :
1099 263685 : LoopAnalysis *getLoop(uint32_t offset) {
1100 263685 : JS_ASSERT(offset < script->length);
1101 263685 : return getCode(offset).loop;
1102 : }
1103 263142 : LoopAnalysis *getLoop(const jsbytecode *pc) { return getLoop(pc - script->code); }
1104 :
1105 : /* For a JSOP_CALL* op, get the pc of the corresponding JSOP_CALL/NEW/etc. */
1106 47110 : jsbytecode *getCallPC(jsbytecode *pc)
1107 : {
1108 47110 : SSAUseChain *uses = useChain(SSAValue::PushedValue(pc - script->code, 0));
1109 47110 : JS_ASSERT(uses && uses->popped);
1110 47110 : JS_ASSERT(js_CodeSpec[script->code[uses->offset]].format & JOF_INVOKE);
1111 47110 : return script->code + uses->offset;
1112 : }
1113 :
1114 : /* Accessors for local variable information. */
1115 :
1116 : /*
1117 : * Escaping slots include all slots that can be accessed in ways other than
1118 : * through the corresponding LOCAL/ARG opcode. This includes all closed
1119 : * slots in the script, all slots in scripts which use eval or are in debug
1120 : * mode, and slots which are aliased by NAME or similar opcodes in the
1121 : * containing script (which does not imply the variable is closed).
1122 : */
1123 3308870 : bool slotEscapes(uint32_t slot) {
1124 3308870 : JS_ASSERT(script->compartment()->activeAnalysis);
1125 3308870 : if (slot >= numSlots)
1126 256588 : return true;
1127 3052282 : return escapedSlots[slot];
1128 : }
1129 :
1130 : /*
1131 : * Whether we distinguish different writes of this variable while doing
1132 : * SSA analysis. Escaping locals can be written in other scripts, and the
1133 : * presence of NAME opcodes which could alias local variables or arguments
1134 : * keeps us from tracking variable values at each point.
1135 : */
1136 1528239 : bool trackSlot(uint32_t slot) { return !slotEscapes(slot) && canTrackVars; }
1137 :
1138 295200 : const LifetimeVariable & liveness(uint32_t slot) {
1139 295200 : JS_ASSERT(script->compartment()->activeAnalysis);
1140 295200 : JS_ASSERT(!slotEscapes(slot));
1141 295200 : return lifetimes[slot];
1142 : }
1143 :
1144 : /*
1145 : * If a NAME or similar opcode is definitely accessing a particular slot
1146 : * of a script this one is nested in, get that script/slot.
1147 : */
1148 : struct NameAccess {
1149 : JSScript *script;
1150 : types::TypeScriptNesting *nesting;
1151 : uint32_t slot;
1152 :
1153 : /* Decompose the slot above. */
1154 : bool arg;
1155 : uint32_t index;
1156 :
1157 28543 : const Value **basePointer() const {
1158 28543 : return arg ? &nesting->argArray : &nesting->varArray;
1159 : }
1160 : };
1161 : NameAccess resolveNameAccess(JSContext *cx, jsid id, bool addDependency = false);
1162 :
1163 : void printSSA(JSContext *cx);
1164 : void printTypes(JSContext *cx);
1165 :
1166 : void clearAllocations();
1167 :
1168 : private:
1169 0 : void setOOM(JSContext *cx) {
1170 0 : if (!outOfMemory)
1171 0 : js_ReportOutOfMemory(cx);
1172 0 : outOfMemory = true;
1173 0 : hadFailure = true;
1174 0 : }
1175 :
1176 : /* Bytecode helpers */
1177 : inline bool addJump(JSContext *cx, unsigned offset,
1178 : unsigned *currentOffset, unsigned *forwardJump,
1179 : unsigned stackDepth);
1180 : void checkAliasedName(JSContext *cx, jsbytecode *pc);
1181 :
1182 : /* Lifetime helpers */
1183 : inline void addVariable(JSContext *cx, LifetimeVariable &var, unsigned offset,
1184 : LifetimeVariable **&saved, unsigned &savedCount);
1185 : inline void killVariable(JSContext *cx, LifetimeVariable &var, unsigned offset,
1186 : LifetimeVariable **&saved, unsigned &savedCount);
1187 : inline void extendVariable(JSContext *cx, LifetimeVariable &var, unsigned start, unsigned end);
1188 : inline void ensureVariable(LifetimeVariable &var, unsigned until);
1189 :
1190 : /* Current value for a variable or stack value, as tracked during SSA. */
1191 : struct SSAValueInfo
1192 : {
1193 : SSAValue v;
1194 :
1195 : /*
1196 : * Sizes of branchTargets the last time this slot was written. Branches less
1197 : * than this threshold do not need to be inspected if the slot is written
1198 : * again, as they will already reflect the slot's value at the branch.
1199 : */
1200 : int32_t branchSize;
1201 : };
1202 :
1203 : /* SSA helpers */
1204 : bool makePhi(JSContext *cx, uint32_t slot, uint32_t offset, SSAValue *pv);
1205 : void insertPhi(JSContext *cx, SSAValue &phi, const SSAValue &v);
1206 : void mergeValue(JSContext *cx, uint32_t offset, const SSAValue &v, SlotValue *pv);
1207 : void checkPendingValue(JSContext *cx, const SSAValue &v, uint32_t slot,
1208 : Vector<SlotValue> *pending);
1209 : void checkBranchTarget(JSContext *cx, uint32_t targetOffset, Vector<uint32_t> &branchTargets,
1210 : SSAValueInfo *values, uint32_t stackDepth);
1211 : void checkExceptionTarget(JSContext *cx, uint32_t catchOffset,
1212 : Vector<uint32_t> &exceptionTargets);
1213 : void mergeBranchTarget(JSContext *cx, SSAValueInfo &value, uint32_t slot,
1214 : const Vector<uint32_t> &branchTargets, uint32_t currentOffset);
1215 : void mergeExceptionTarget(JSContext *cx, const SSAValue &value, uint32_t slot,
1216 : const Vector<uint32_t> &exceptionTargets);
1217 : void mergeAllExceptionTargets(JSContext *cx, SSAValueInfo *values,
1218 : const Vector<uint32_t> &exceptionTargets);
1219 : void freezeNewValues(JSContext *cx, uint32_t offset);
1220 :
1221 38782 : struct TypeInferenceState {
1222 : Vector<SSAPhiNode *> phiNodes;
1223 : bool hasGetSet;
1224 : bool hasHole;
1225 : types::TypeSet *forTypes;
1226 38782 : TypeInferenceState(JSContext *cx)
1227 38782 : : phiNodes(cx), hasGetSet(false), hasHole(false), forTypes(NULL)
1228 38782 : {}
1229 : };
1230 :
1231 : /* Type inference helpers */
1232 : bool analyzeTypesBytecode(JSContext *cx, unsigned offset, TypeInferenceState &state);
1233 : bool followEscapingArguments(JSContext *cx, const SSAValue &v, Vector<SSAValue> *seen);
1234 : bool followEscapingArguments(JSContext *cx, SSAUseChain *use, Vector<SSAValue> *seen);
1235 :
1236 : public:
1237 : #ifdef DEBUG
1238 : void assertMatchingDebugMode();
1239 : #else
1240 : void assertMatchingDebugMode() { }
1241 : #endif
1242 : };
1243 :
1244 : /* Protect analysis structures from GC while they are being used. */
1245 : class AutoEnterAnalysis
1246 : {
1247 : JSCompartment *compartment;
1248 : bool oldActiveAnalysis;
1249 : bool left;
1250 :
1251 1003112 : void construct(JSCompartment *compartment)
1252 : {
1253 1003112 : this->compartment = compartment;
1254 1003112 : oldActiveAnalysis = compartment->activeAnalysis;
1255 1003112 : compartment->activeAnalysis = true;
1256 1003112 : left = false;
1257 1003112 : }
1258 :
1259 : public:
1260 950909 : AutoEnterAnalysis(JSContext *cx) { construct(cx->compartment); }
1261 52203 : AutoEnterAnalysis(JSCompartment *compartment) { construct(compartment); }
1262 :
1263 1003130 : void leave()
1264 : {
1265 1003130 : if (!left) {
1266 1003112 : left = true;
1267 1003112 : compartment->activeAnalysis = oldActiveAnalysis;
1268 : }
1269 1003130 : }
1270 :
1271 1003112 : ~AutoEnterAnalysis()
1272 : {
1273 1003112 : leave();
1274 1003112 : }
1275 : };
1276 :
1277 : /* SSA value as used by CrossScriptSSA, identifies the frame it came from. */
1278 : struct CrossSSAValue
1279 : {
1280 : unsigned frame;
1281 : SSAValue v;
1282 699589 : CrossSSAValue(unsigned frame, const SSAValue &v) : frame(frame), v(v) {}
1283 : };
1284 :
1285 : /*
1286 : * Analysis for managing SSA values from multiple call stack frames. These are
1287 : * created by the backend compiler when inlining functions, and allow for
1288 : * values to be tracked as they flow into or out of the inlined frames.
1289 : */
1290 : class CrossScriptSSA
1291 134091 : {
1292 : public:
1293 :
1294 : static const uint32_t OUTER_FRAME = UINT32_MAX;
1295 : static const unsigned INVALID_FRAME = uint32_t(-2);
1296 :
1297 6422 : struct Frame {
1298 : uint32_t index;
1299 : JSScript *script;
1300 : uint32_t depth; /* Distance from outer frame to this frame, in sizeof(Value) */
1301 : uint32_t parent;
1302 : jsbytecode *parentpc;
1303 :
1304 137096 : Frame(uint32_t index, JSScript *script, uint32_t depth, uint32_t parent, jsbytecode *parentpc)
1305 137096 : : index(index), script(script), depth(depth), parent(parent), parentpc(parentpc)
1306 137096 : {}
1307 : };
1308 :
1309 452137 : const Frame &getFrame(uint32_t index) {
1310 452137 : if (index == OUTER_FRAME)
1311 420564 : return outerFrame;
1312 31573 : return inlineFrames[index];
1313 : }
1314 :
1315 445650 : unsigned numFrames() { return 1 + inlineFrames.length(); }
1316 358781 : const Frame &iterFrame(unsigned i) {
1317 358781 : if (i == 0)
1318 127660 : return outerFrame;
1319 231121 : return inlineFrames[i - 1];
1320 : }
1321 :
1322 33602 : JSScript *outerScript() { return outerFrame.script; }
1323 :
1324 : /* Total length of scripts preceding a frame. */
1325 131876 : size_t frameLength(uint32_t index) {
1326 131876 : if (index == OUTER_FRAME)
1327 128881 : return 0;
1328 2995 : size_t res = outerFrame.script->length;
1329 68161 : for (unsigned i = 0; i < index; i++)
1330 65166 : res += inlineFrames[i].script->length;
1331 2995 : return res;
1332 : }
1333 :
1334 4281 : types::TypeSet *getValueTypes(const CrossSSAValue &cv) {
1335 4281 : return getFrame(cv.frame).script->analysis()->getValueTypes(cv.v);
1336 : }
1337 :
1338 3005 : bool addInlineFrame(JSScript *script, uint32_t depth, uint32_t parent, jsbytecode *parentpc)
1339 : {
1340 3005 : uint32_t index = inlineFrames.length();
1341 3005 : return inlineFrames.append(Frame(index, script, depth, parent, parentpc));
1342 : }
1343 :
1344 134091 : CrossScriptSSA(JSContext *cx, JSScript *outer)
1345 134091 : : cx(cx), outerFrame(OUTER_FRAME, outer, 0, INVALID_FRAME, NULL), inlineFrames(cx)
1346 134091 : {}
1347 :
1348 : CrossSSAValue foldValue(const CrossSSAValue &cv);
1349 :
1350 : private:
1351 : JSContext *cx;
1352 :
1353 : Frame outerFrame;
1354 : Vector<Frame> inlineFrames;
1355 : };
1356 :
1357 : #ifdef DEBUG
1358 : void PrintBytecode(JSContext *cx, JSScript *script, jsbytecode *pc);
1359 : #endif
1360 :
1361 : } /* namespace analyze */
1362 : } /* namespace js */
1363 :
1364 : namespace js {
1365 : namespace tl {
1366 :
1367 : template <> struct IsPodType<js::analyze::LifetimeVariable> { static const bool result = true; };
1368 : template <> struct IsPodType<js::analyze::LoopAnalysis> { static const bool result = true; };
1369 : template <> struct IsPodType<js::analyze::SlotValue> { static const bool result = true; };
1370 : template <> struct IsPodType<js::analyze::SSAValue> { static const bool result = true; };
1371 : template <> struct IsPodType<js::analyze::SSAUseChain> { static const bool result = true; };
1372 :
1373 : } /* namespace tl */
1374 : } /* namespace js */
1375 :
1376 : #endif // jsanalyze_h___
|