|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "jit/BytecodeAnalysis.h" |
|
8 |
|
9 #include "jsopcode.h" |
|
10 #include "jit/IonSpewer.h" |
|
11 #include "jsopcodeinlines.h" |
|
12 |
|
13 using namespace js; |
|
14 using namespace js::jit; |
|
15 |
|
16 BytecodeAnalysis::BytecodeAnalysis(TempAllocator &alloc, JSScript *script) |
|
17 : script_(script), |
|
18 infos_(alloc), |
|
19 usesScopeChain_(false), |
|
20 hasTryFinally_(false), |
|
21 hasSetArg_(false) |
|
22 { |
|
23 } |
|
24 |
|
25 // Bytecode range containing only catch or finally code. |
|
26 struct CatchFinallyRange |
|
27 { |
|
28 uint32_t start; // Inclusive. |
|
29 uint32_t end; // Exclusive. |
|
30 |
|
31 CatchFinallyRange(uint32_t start, uint32_t end) |
|
32 : start(start), end(end) |
|
33 { |
|
34 JS_ASSERT(end > start); |
|
35 } |
|
36 |
|
37 bool contains(uint32_t offset) const { |
|
38 return start <= offset && offset < end; |
|
39 } |
|
40 }; |
|
41 |
|
42 bool |
|
43 BytecodeAnalysis::init(TempAllocator &alloc, GSNCache &gsn) |
|
44 { |
|
45 if (!infos_.growByUninitialized(script_->length())) |
|
46 return false; |
|
47 |
|
48 jsbytecode *end = script_->codeEnd(); |
|
49 |
|
50 // Clear all BytecodeInfo. |
|
51 mozilla::PodZero(infos_.begin(), infos_.length()); |
|
52 infos_[0].init(/*stackDepth=*/0); |
|
53 |
|
54 Vector<CatchFinallyRange, 0, IonAllocPolicy> catchFinallyRanges(alloc); |
|
55 |
|
56 for (jsbytecode *pc = script_->code(); pc < end; pc += GetBytecodeLength(pc)) { |
|
57 JSOp op = JSOp(*pc); |
|
58 unsigned offset = script_->pcToOffset(pc); |
|
59 |
|
60 IonSpew(IonSpew_BaselineOp, "Analyzing op @ %d (end=%d): %s", |
|
61 int(script_->pcToOffset(pc)), int(script_->length()), js_CodeName[op]); |
|
62 |
|
63 // If this bytecode info has not yet been initialized, it's not reachable. |
|
64 if (!infos_[offset].initialized) |
|
65 continue; |
|
66 |
|
67 |
|
68 unsigned stackDepth = infos_[offset].stackDepth; |
|
69 #ifdef DEBUG |
|
70 for (jsbytecode *chkpc = pc + 1; chkpc < (pc + GetBytecodeLength(pc)); chkpc++) |
|
71 JS_ASSERT(!infos_[script_->pcToOffset(chkpc)].initialized); |
|
72 #endif |
|
73 |
|
74 unsigned nuses = GetUseCount(script_, offset); |
|
75 unsigned ndefs = GetDefCount(script_, offset); |
|
76 |
|
77 JS_ASSERT(stackDepth >= nuses); |
|
78 stackDepth -= nuses; |
|
79 stackDepth += ndefs; |
|
80 |
|
81 // If stack depth exceeds max allowed by analysis, fail fast. |
|
82 JS_ASSERT(stackDepth <= BytecodeInfo::MAX_STACK_DEPTH); |
|
83 |
|
84 switch (op) { |
|
85 case JSOP_TABLESWITCH: { |
|
86 unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc); |
|
87 jsbytecode *pc2 = pc + JUMP_OFFSET_LEN; |
|
88 int32_t low = GET_JUMP_OFFSET(pc2); |
|
89 pc2 += JUMP_OFFSET_LEN; |
|
90 int32_t high = GET_JUMP_OFFSET(pc2); |
|
91 pc2 += JUMP_OFFSET_LEN; |
|
92 |
|
93 infos_[defaultOffset].init(stackDepth); |
|
94 infos_[defaultOffset].jumpTarget = true; |
|
95 |
|
96 for (int32_t i = low; i <= high; i++) { |
|
97 unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2); |
|
98 if (targetOffset != offset) { |
|
99 infos_[targetOffset].init(stackDepth); |
|
100 infos_[targetOffset].jumpTarget = true; |
|
101 } |
|
102 pc2 += JUMP_OFFSET_LEN; |
|
103 } |
|
104 break; |
|
105 } |
|
106 |
|
107 case JSOP_TRY: { |
|
108 JSTryNote *tn = script_->trynotes()->vector; |
|
109 JSTryNote *tnlimit = tn + script_->trynotes()->length; |
|
110 for (; tn < tnlimit; tn++) { |
|
111 unsigned startOffset = script_->mainOffset() + tn->start; |
|
112 if (startOffset == offset + 1) { |
|
113 unsigned catchOffset = startOffset + tn->length; |
|
114 |
|
115 if (tn->kind != JSTRY_ITER) { |
|
116 infos_[catchOffset].init(stackDepth); |
|
117 infos_[catchOffset].jumpTarget = true; |
|
118 } |
|
119 } |
|
120 } |
|
121 |
|
122 // Get the pc of the last instruction in the try block. It's a JSOP_GOTO to |
|
123 // jump over the catch/finally blocks. |
|
124 jssrcnote *sn = GetSrcNote(gsn, script_, pc); |
|
125 JS_ASSERT(SN_TYPE(sn) == SRC_TRY); |
|
126 |
|
127 jsbytecode *endOfTry = pc + js_GetSrcNoteOffset(sn, 0); |
|
128 JS_ASSERT(JSOp(*endOfTry) == JSOP_GOTO); |
|
129 |
|
130 jsbytecode *afterTry = endOfTry + GET_JUMP_OFFSET(endOfTry); |
|
131 JS_ASSERT(afterTry > endOfTry); |
|
132 |
|
133 // Pop CatchFinallyRanges that are no longer needed. |
|
134 while (!catchFinallyRanges.empty() && catchFinallyRanges.back().end <= offset) |
|
135 catchFinallyRanges.popBack(); |
|
136 |
|
137 CatchFinallyRange range(script_->pcToOffset(endOfTry), script_->pcToOffset(afterTry)); |
|
138 if (!catchFinallyRanges.append(range)) |
|
139 return false; |
|
140 break; |
|
141 } |
|
142 |
|
143 case JSOP_LOOPENTRY: |
|
144 for (size_t i = 0; i < catchFinallyRanges.length(); i++) { |
|
145 if (catchFinallyRanges[i].contains(offset)) |
|
146 infos_[offset].loopEntryInCatchOrFinally = true; |
|
147 } |
|
148 break; |
|
149 |
|
150 case JSOP_NAME: |
|
151 case JSOP_BINDNAME: |
|
152 case JSOP_SETNAME: |
|
153 case JSOP_DELNAME: |
|
154 case JSOP_GETALIASEDVAR: |
|
155 case JSOP_SETALIASEDVAR: |
|
156 case JSOP_LAMBDA: |
|
157 case JSOP_LAMBDA_ARROW: |
|
158 case JSOP_DEFFUN: |
|
159 case JSOP_DEFVAR: |
|
160 case JSOP_DEFCONST: |
|
161 case JSOP_SETCONST: |
|
162 usesScopeChain_ = true; |
|
163 break; |
|
164 |
|
165 case JSOP_FINALLY: |
|
166 hasTryFinally_ = true; |
|
167 break; |
|
168 |
|
169 case JSOP_SETARG: |
|
170 hasSetArg_ = true; |
|
171 break; |
|
172 |
|
173 default: |
|
174 break; |
|
175 } |
|
176 |
|
177 bool jump = IsJumpOpcode(op); |
|
178 if (jump) { |
|
179 // Case instructions do not push the lvalue back when branching. |
|
180 unsigned newStackDepth = stackDepth; |
|
181 if (op == JSOP_CASE) |
|
182 newStackDepth--; |
|
183 |
|
184 unsigned targetOffset = offset + GET_JUMP_OFFSET(pc); |
|
185 |
|
186 // If this is a a backedge to an un-analyzed segment, analyze from there. |
|
187 bool jumpBack = (targetOffset < offset) && !infos_[targetOffset].initialized; |
|
188 |
|
189 infos_[targetOffset].init(newStackDepth); |
|
190 infos_[targetOffset].jumpTarget = true; |
|
191 |
|
192 if (jumpBack) |
|
193 pc = script_->offsetToPC(targetOffset); |
|
194 } |
|
195 |
|
196 // Handle any fallthrough from this opcode. |
|
197 if (BytecodeFallsThrough(op)) { |
|
198 jsbytecode *nextpc = pc + GetBytecodeLength(pc); |
|
199 JS_ASSERT(nextpc < end); |
|
200 unsigned nextOffset = script_->pcToOffset(nextpc); |
|
201 |
|
202 infos_[nextOffset].init(stackDepth); |
|
203 |
|
204 if (jump) |
|
205 infos_[nextOffset].jumpFallthrough = true; |
|
206 |
|
207 // Treat the fallthrough of a branch instruction as a jump target. |
|
208 if (jump) |
|
209 infos_[nextOffset].jumpTarget = true; |
|
210 else |
|
211 infos_[nextOffset].fallthrough = true; |
|
212 } |
|
213 } |
|
214 |
|
215 return true; |
|
216 } |