Line data Source code
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/JitSpewer.h"
11 : #include "jsopcodeinlines.h"
12 : #include "jsscriptinlines.h"
13 :
14 : using namespace js;
15 : using namespace js::jit;
16 :
17 957 : BytecodeAnalysis::BytecodeAnalysis(TempAllocator& alloc, JSScript* script)
18 : : script_(script),
19 : infos_(alloc),
20 : usesEnvironmentChain_(false),
21 : hasTryFinally_(false),
22 957 : hasSetArg_(false)
23 : {
24 957 : }
25 :
26 : // Bytecode range containing only catch or finally code.
27 : struct CatchFinallyRange
28 : {
29 : uint32_t start; // Inclusive.
30 : uint32_t end; // Exclusive.
31 :
32 282 : CatchFinallyRange(uint32_t start, uint32_t end)
33 282 : : start(start), end(end)
34 : {
35 282 : MOZ_ASSERT(end > start);
36 282 : }
37 :
38 388 : bool contains(uint32_t offset) const {
39 388 : return start <= offset && offset < end;
40 : }
41 : };
42 :
43 : bool
44 957 : BytecodeAnalysis::init(TempAllocator& alloc, GSNCache& gsn)
45 : {
46 957 : if (!infos_.growByUninitialized(script_->length()))
47 0 : return false;
48 :
49 : // Initialize the env chain slot if either the function needs some
50 : // EnvironmentObject (like a CallObject) or the script uses the env
51 : // chain. The latter case is handled below.
52 1914 : usesEnvironmentChain_ = script_->module() || script_->initialEnvironmentShape() ||
53 1751 : (script_->functionDelazifying() &&
54 873 : script_->functionDelazifying()->needsSomeEnvironmentObject());
55 :
56 957 : jsbytecode* end = script_->codeEnd();
57 :
58 : // Clear all BytecodeInfo.
59 957 : mozilla::PodZero(infos_.begin(), infos_.length());
60 957 : infos_[0].init(/*stackDepth=*/0);
61 :
62 1914 : Vector<CatchFinallyRange, 0, JitAllocPolicy> catchFinallyRanges(alloc);
63 :
64 : jsbytecode* nextpc;
65 139052 : for (jsbytecode* pc = script_->code(); pc < end; pc = nextpc) {
66 138095 : JSOp op = JSOp(*pc);
67 138095 : nextpc = pc + GetBytecodeLength(pc);
68 138095 : unsigned offset = script_->pcToOffset(pc);
69 :
70 414285 : JitSpew(JitSpew_BaselineOp, "Analyzing op @ %d (end=%d): %s",
71 414285 : int(script_->pcToOffset(pc)), int(script_->length()), CodeName[op]);
72 :
73 : // If this bytecode info has not yet been initialized, it's not reachable.
74 138095 : if (!infos_[offset].initialized)
75 33427 : continue;
76 :
77 104668 : unsigned stackDepth = infos_[offset].stackDepth;
78 : #ifdef DEBUG
79 278155 : for (jsbytecode* chkpc = pc + 1; chkpc < (pc + GetBytecodeLength(pc)); chkpc++)
80 173487 : MOZ_ASSERT(!infos_[script_->pcToOffset(chkpc)].initialized);
81 : #endif
82 :
83 104668 : unsigned nuses = GetUseCount(script_, offset);
84 104668 : unsigned ndefs = GetDefCount(script_, offset);
85 :
86 104668 : MOZ_ASSERT(stackDepth >= nuses);
87 104668 : stackDepth -= nuses;
88 104668 : stackDepth += ndefs;
89 :
90 : // If stack depth exceeds max allowed by analysis, fail fast.
91 104668 : MOZ_ASSERT(stackDepth <= BytecodeInfo::MAX_STACK_DEPTH);
92 :
93 104668 : switch (op) {
94 : case JSOP_TABLESWITCH: {
95 99 : unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
96 99 : jsbytecode* pc2 = pc + JUMP_OFFSET_LEN;
97 99 : int32_t low = GET_JUMP_OFFSET(pc2);
98 99 : pc2 += JUMP_OFFSET_LEN;
99 99 : int32_t high = GET_JUMP_OFFSET(pc2);
100 99 : pc2 += JUMP_OFFSET_LEN;
101 :
102 99 : infos_[defaultOffset].init(stackDepth);
103 99 : infos_[defaultOffset].jumpTarget = true;
104 :
105 674 : for (int32_t i = low; i <= high; i++) {
106 575 : unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
107 575 : if (targetOffset != offset) {
108 575 : infos_[targetOffset].init(stackDepth);
109 575 : infos_[targetOffset].jumpTarget = true;
110 : }
111 575 : pc2 += JUMP_OFFSET_LEN;
112 : }
113 99 : break;
114 : }
115 :
116 : case JSOP_TRY: {
117 282 : JSTryNote* tn = script_->trynotes()->vector;
118 282 : JSTryNote* tnlimit = tn + script_->trynotes()->length;
119 5220 : for (; tn < tnlimit; tn++) {
120 2469 : unsigned startOffset = script_->mainOffset() + tn->start;
121 2469 : if (startOffset == offset + 1) {
122 290 : unsigned catchOffset = startOffset + tn->length;
123 :
124 290 : if (tn->kind != JSTRY_FOR_IN) {
125 290 : infos_[catchOffset].init(stackDepth);
126 290 : infos_[catchOffset].jumpTarget = true;
127 : }
128 : }
129 : }
130 :
131 : // Get the pc of the last instruction in the try block. It's a JSOP_GOTO to
132 : // jump over the catch/finally blocks.
133 282 : jssrcnote* sn = GetSrcNote(gsn, script_, pc);
134 282 : MOZ_ASSERT(SN_TYPE(sn) == SRC_TRY);
135 :
136 282 : jsbytecode* endOfTry = pc + GetSrcNoteOffset(sn, 0);
137 282 : MOZ_ASSERT(JSOp(*endOfTry) == JSOP_GOTO);
138 :
139 282 : jsbytecode* afterTry = endOfTry + GET_JUMP_OFFSET(endOfTry);
140 282 : MOZ_ASSERT(afterTry > endOfTry);
141 :
142 : // Pop CatchFinallyRanges that are no longer needed.
143 90 : while (!catchFinallyRanges.empty() && catchFinallyRanges.back().end <= offset)
144 90 : catchFinallyRanges.popBack();
145 :
146 282 : CatchFinallyRange range(script_->pcToOffset(endOfTry), script_->pcToOffset(afterTry));
147 282 : if (!catchFinallyRanges.append(range))
148 0 : return false;
149 282 : break;
150 : }
151 :
152 : case JSOP_LOOPENTRY:
153 1428 : for (size_t i = 0; i < catchFinallyRanges.length(); i++) {
154 388 : if (catchFinallyRanges[i].contains(offset))
155 4 : infos_[offset].loopEntryInCatchOrFinally = true;
156 : }
157 1040 : break;
158 :
159 : case JSOP_GETNAME:
160 : case JSOP_BINDNAME:
161 : case JSOP_BINDVAR:
162 : case JSOP_SETNAME:
163 : case JSOP_STRICTSETNAME:
164 : case JSOP_DELNAME:
165 : case JSOP_GETALIASEDVAR:
166 : case JSOP_SETALIASEDVAR:
167 : case JSOP_LAMBDA:
168 : case JSOP_LAMBDA_ARROW:
169 : case JSOP_DEFFUN:
170 : case JSOP_DEFVAR:
171 : case JSOP_PUSHLEXICALENV:
172 : case JSOP_POPLEXICALENV:
173 2649 : usesEnvironmentChain_ = true;
174 2649 : break;
175 :
176 : case JSOP_GETGNAME:
177 : case JSOP_SETGNAME:
178 : case JSOP_STRICTSETGNAME:
179 1736 : if (script_->hasNonSyntacticScope())
180 0 : usesEnvironmentChain_ = true;
181 1736 : break;
182 :
183 : case JSOP_FINALLY:
184 12 : hasTryFinally_ = true;
185 12 : break;
186 :
187 : case JSOP_SETARG:
188 136 : hasSetArg_ = true;
189 136 : break;
190 :
191 : default:
192 98714 : break;
193 : }
194 :
195 104668 : bool jump = IsJumpOpcode(op);
196 104668 : if (jump) {
197 : // Case instructions do not push the lvalue back when branching.
198 6877 : unsigned newStackDepth = stackDepth;
199 6877 : if (op == JSOP_CASE)
200 23 : newStackDepth--;
201 :
202 6877 : unsigned targetOffset = offset + GET_JUMP_OFFSET(pc);
203 :
204 : // If this is a a backedge to an un-analyzed segment, analyze from there.
205 6877 : bool jumpBack = (targetOffset < offset) && !infos_[targetOffset].initialized;
206 :
207 6877 : infos_[targetOffset].init(newStackDepth);
208 6877 : infos_[targetOffset].jumpTarget = true;
209 :
210 6877 : if (jumpBack)
211 522 : nextpc = script_->offsetToPC(targetOffset);
212 : }
213 :
214 : // Handle any fallthrough from this opcode.
215 104668 : if (BytecodeFallsThrough(op)) {
216 100181 : jsbytecode* fallthrough = pc + GetBytecodeLength(pc);
217 100181 : MOZ_ASSERT(fallthrough < end);
218 100181 : unsigned fallthroughOffset = script_->pcToOffset(fallthrough);
219 :
220 100181 : infos_[fallthroughOffset].init(stackDepth);
221 :
222 : // Treat the fallthrough of a branch instruction as a jump target.
223 100181 : if (jump)
224 4852 : infos_[fallthroughOffset].jumpTarget = true;
225 : }
226 : }
227 :
228 957 : return true;
229 9 : }
|