|
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 /* Definitions for javascript analysis. */ |
|
8 |
|
9 #ifndef jsanalyze_h |
|
10 #define jsanalyze_h |
|
11 |
|
12 #include "jscompartment.h" |
|
13 |
|
14 namespace js { |
|
15 namespace analyze { |
|
16 |
|
17 class Bytecode; |
|
18 struct LifetimeVariable; |
|
19 class SlotValue; |
|
20 class SSAValue; |
|
21 struct SSAValueInfo; |
|
22 class SSAUseChain; |
|
23 |
|
24 // Common representation of slots between ScriptAnalysis, TypeScript, and in the |
|
25 // case of TotalSlots, Ion. |
|
26 static inline uint32_t ThisSlot() { |
|
27 return 0; |
|
28 } |
|
29 static inline uint32_t ArgSlot(uint32_t arg) { |
|
30 return 1 + arg; |
|
31 } |
|
32 static inline uint32_t LocalSlot(JSScript *script, uint32_t local) { |
|
33 return 1 + local + |
|
34 (script->functionNonDelazifying() ? script->functionNonDelazifying()->nargs() : 0); |
|
35 } |
|
36 static inline uint32_t TotalSlots(JSScript *script) { |
|
37 return LocalSlot(script, 0) + script->nfixed(); |
|
38 } |
|
39 |
|
40 // Analysis information about a script. FIXME: At this point, the entire |
|
41 // purpose of this class is to compute JSScript::needsArgsObj, and to support |
|
42 // isReachable() in order for jsinfer.cpp:FindPreviousInnerInitializer to get |
|
43 // the previous opcode. For that purpose, it is completely overkill. |
|
44 class ScriptAnalysis |
|
45 { |
|
46 friend class Bytecode; |
|
47 |
|
48 JSScript *script_; |
|
49 |
|
50 Bytecode **codeArray; |
|
51 |
|
52 uint32_t numSlots; |
|
53 |
|
54 bool *escapedSlots; |
|
55 |
|
56 #ifdef DEBUG |
|
57 /* Whether the compartment was in debug mode when we performed the analysis. */ |
|
58 bool originalDebugMode_: 1; |
|
59 #endif |
|
60 |
|
61 /* --------- Bytecode analysis --------- */ |
|
62 |
|
63 bool canTrackVars:1; |
|
64 bool argumentsContentsObserved_:1; |
|
65 |
|
66 /* --------- Lifetime analysis --------- */ |
|
67 |
|
68 LifetimeVariable *lifetimes; |
|
69 |
|
70 public: |
|
71 ScriptAnalysis(JSScript *script) { |
|
72 mozilla::PodZero(this); |
|
73 this->script_ = script; |
|
74 #ifdef DEBUG |
|
75 this->originalDebugMode_ = script_->compartment()->debugMode(); |
|
76 #endif |
|
77 } |
|
78 |
|
79 MOZ_WARN_UNUSED_RESULT |
|
80 bool analyzeBytecode(JSContext *cx); |
|
81 |
|
82 bool isReachable(const jsbytecode *pc) { return maybeCode(pc); } |
|
83 |
|
84 private: |
|
85 MOZ_WARN_UNUSED_RESULT |
|
86 bool analyzeSSA(JSContext *cx); |
|
87 MOZ_WARN_UNUSED_RESULT |
|
88 bool analyzeLifetimes(JSContext *cx); |
|
89 |
|
90 /* Accessors for bytecode information. */ |
|
91 Bytecode& getCode(uint32_t offset) { |
|
92 JS_ASSERT(offset < script_->length()); |
|
93 JS_ASSERT(codeArray[offset]); |
|
94 return *codeArray[offset]; |
|
95 } |
|
96 Bytecode& getCode(const jsbytecode *pc) { return getCode(script_->pcToOffset(pc)); } |
|
97 |
|
98 Bytecode* maybeCode(uint32_t offset) { |
|
99 JS_ASSERT(offset < script_->length()); |
|
100 return codeArray[offset]; |
|
101 } |
|
102 Bytecode* maybeCode(const jsbytecode *pc) { return maybeCode(script_->pcToOffset(pc)); } |
|
103 |
|
104 inline bool jumpTarget(uint32_t offset); |
|
105 inline bool jumpTarget(const jsbytecode *pc); |
|
106 |
|
107 inline const SSAValue &poppedValue(uint32_t offset, uint32_t which); |
|
108 inline const SSAValue &poppedValue(const jsbytecode *pc, uint32_t which); |
|
109 |
|
110 inline const SlotValue *newValues(uint32_t offset); |
|
111 inline const SlotValue *newValues(const jsbytecode *pc); |
|
112 |
|
113 inline bool trackUseChain(const SSAValue &v); |
|
114 |
|
115 /* |
|
116 * Get the use chain for an SSA value. |
|
117 */ |
|
118 inline SSAUseChain *& useChain(const SSAValue &v); |
|
119 |
|
120 |
|
121 /* For a JSOP_CALL* op, get the pc of the corresponding JSOP_CALL/NEW/etc. */ |
|
122 inline jsbytecode *getCallPC(jsbytecode *pc); |
|
123 |
|
124 /* Accessors for local variable information. */ |
|
125 |
|
126 /* |
|
127 * Escaping slots include all slots that can be accessed in ways other than |
|
128 * through the corresponding LOCAL/ARG opcode. This includes all closed |
|
129 * slots in the script, all slots in scripts which use eval or are in debug |
|
130 * mode, and slots which are aliased by NAME or similar opcodes in the |
|
131 * containing script (which does not imply the variable is closed). |
|
132 */ |
|
133 inline bool slotEscapes(uint32_t slot); |
|
134 |
|
135 /* |
|
136 * Whether we distinguish different writes of this variable while doing |
|
137 * SSA analysis. Escaping locals can be written in other scripts, and the |
|
138 * presence of NAME opcodes which could alias local variables or arguments |
|
139 * keeps us from tracking variable values at each point. |
|
140 */ |
|
141 inline bool trackSlot(uint32_t slot); |
|
142 |
|
143 inline const LifetimeVariable & liveness(uint32_t slot); |
|
144 |
|
145 void printSSA(JSContext *cx); |
|
146 void printTypes(JSContext *cx); |
|
147 |
|
148 /* Bytecode helpers */ |
|
149 MOZ_WARN_UNUSED_RESULT |
|
150 inline bool addJump(JSContext *cx, unsigned offset, |
|
151 unsigned *currentOffset, unsigned *forwardJump, unsigned *forwardLoop, |
|
152 unsigned stackDepth); |
|
153 |
|
154 /* Lifetime helpers */ |
|
155 MOZ_WARN_UNUSED_RESULT |
|
156 inline bool addVariable(JSContext *cx, LifetimeVariable &var, unsigned offset, |
|
157 LifetimeVariable **&saved, unsigned &savedCount); |
|
158 MOZ_WARN_UNUSED_RESULT |
|
159 inline bool killVariable(JSContext *cx, LifetimeVariable &var, unsigned offset, |
|
160 LifetimeVariable **&saved, unsigned &savedCount); |
|
161 MOZ_WARN_UNUSED_RESULT |
|
162 inline bool extendVariable(JSContext *cx, LifetimeVariable &var, unsigned start, unsigned end); |
|
163 |
|
164 inline void ensureVariable(LifetimeVariable &var, unsigned until); |
|
165 |
|
166 /* SSA helpers */ |
|
167 MOZ_WARN_UNUSED_RESULT |
|
168 bool makePhi(JSContext *cx, uint32_t slot, uint32_t offset, SSAValue *pv); |
|
169 MOZ_WARN_UNUSED_RESULT |
|
170 bool insertPhi(JSContext *cx, SSAValue &phi, const SSAValue &v); |
|
171 MOZ_WARN_UNUSED_RESULT |
|
172 bool mergeValue(JSContext *cx, uint32_t offset, const SSAValue &v, SlotValue *pv); |
|
173 MOZ_WARN_UNUSED_RESULT |
|
174 bool checkPendingValue(JSContext *cx, const SSAValue &v, uint32_t slot, |
|
175 Vector<SlotValue> *pending); |
|
176 MOZ_WARN_UNUSED_RESULT |
|
177 bool checkBranchTarget(JSContext *cx, uint32_t targetOffset, Vector<uint32_t> &branchTargets, |
|
178 SSAValueInfo *values, uint32_t stackDepth); |
|
179 MOZ_WARN_UNUSED_RESULT |
|
180 bool checkExceptionTarget(JSContext *cx, uint32_t catchOffset, |
|
181 Vector<uint32_t> &exceptionTargets); |
|
182 MOZ_WARN_UNUSED_RESULT |
|
183 bool mergeBranchTarget(JSContext *cx, SSAValueInfo &value, uint32_t slot, |
|
184 const Vector<uint32_t> &branchTargets, uint32_t currentOffset); |
|
185 MOZ_WARN_UNUSED_RESULT |
|
186 bool mergeExceptionTarget(JSContext *cx, const SSAValue &value, uint32_t slot, |
|
187 const Vector<uint32_t> &exceptionTargets); |
|
188 MOZ_WARN_UNUSED_RESULT |
|
189 bool mergeAllExceptionTargets(JSContext *cx, SSAValueInfo *values, |
|
190 const Vector<uint32_t> &exceptionTargets); |
|
191 MOZ_WARN_UNUSED_RESULT |
|
192 bool freezeNewValues(JSContext *cx, uint32_t offset); |
|
193 |
|
194 typedef Vector<SSAValue, 16> SeenVector; |
|
195 bool needsArgsObj(JSContext *cx, SeenVector &seen, const SSAValue &v); |
|
196 bool needsArgsObj(JSContext *cx, SeenVector &seen, SSAUseChain *use); |
|
197 bool needsArgsObj(JSContext *cx); |
|
198 |
|
199 public: |
|
200 #ifdef DEBUG |
|
201 void assertMatchingDebugMode(); |
|
202 void assertMatchingStackDepthAtOffset(uint32_t offset, uint32_t stackDepth); |
|
203 #else |
|
204 void assertMatchingDebugMode() { } |
|
205 void assertMatchingStackDepthAtOffset(uint32_t offset, uint32_t stackDepth) { } |
|
206 #endif |
|
207 }; |
|
208 |
|
209 #ifdef DEBUG |
|
210 void PrintBytecode(JSContext *cx, HandleScript script, jsbytecode *pc); |
|
211 #endif |
|
212 |
|
213 } /* namespace analyze */ |
|
214 } /* namespace js */ |
|
215 |
|
216 #endif /* jsanalyze_h */ |