1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=99:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 : * May 28, 2008.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Brendan Eich <brendan@mozilla.org>
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 : #if !defined jsjaeger_loopstate_h__ && defined JS_METHODJIT
40 : #define jsjaeger_loopstate_h__
41 :
42 : #include "jsanalyze.h"
43 : #include "methodjit/Compiler.h"
44 :
45 : namespace js {
46 : namespace mjit {
47 :
48 : /*
49 : * The LoopState keeps track of register and analysis state within the loop
50 : * currently being processed by the Compiler.
51 : *
52 : * There are several analyses we do that are specific to loops: loop carried
53 : * registers, bounds check hoisting, and loop invariant code motion. Brief
54 : * descriptions of these analyses:
55 : *
56 : * Loop carried registers. We allocate registers as we emit code, in a single
57 : * forward pass over the script. Normally this would mean we need to pick the
58 : * register allocation at the head of the loop before any of the body has been
59 : * processed. Instead, while processing the loop body we retroactively mark
60 : * registers as holding the payload of certain entries at the head (being
61 : * carried around the loop), so that the head's allocation ends up holding
62 : * registers that are likely to be used shortly. This can be done provided that
63 : * (a) the register has not been touched since the loop head, (b) the slot
64 : * has not been modified or separately assigned a different register, and (c)
65 : * all prior slow path rejoins in the loop are patched with reloads of the
66 : * register. The register allocation at the loop head must have all entries
67 : * synced, so that prior slow path syncs do not also need patching.
68 : *
69 : * Bounds check hoisting. If we can determine a loop invariant test which
70 : * implies the bounds check at one or more array accesses, we hoist that and
71 : * check it when initially entering the loop (from JIT code or the
72 : * interpreter) and after every stub or C++ call.
73 : *
74 : * Loop invariant code motion. If we can determine a computation (arithmetic,
75 : * array slot pointer or property access) is loop invariant, we give it a slot
76 : * on the stack and preserve its value throughout the loop. We can allocate
77 : * and carry registers for loop invariant slots as for normal slots. These
78 : * slots sit above the frame's normal slots, and are transient --- they are
79 : * clobbered whenever a new frame is pushed. We thus regenerate the loop
80 : * invariant slots after every C++ and scripted call, and avoid doing LICM on
81 : * loops which have such calls. This has a nice property that the slots only
82 : * need to be loop invariant wrt the side effects that happen directly in the
83 : * loop; if C++ calls a getter which scribbles on the object properties
84 : * involved in an 'invariant' then we will reload the invariant's new value
85 : * after the call finishes.
86 : */
87 :
88 : struct TemporaryCopy;
89 :
90 : enum InvariantArrayKind { DENSE_ARRAY, TYPED_ARRAY };
91 :
92 : class LoopState : public MacroAssemblerTypedefs
93 33602 : {
94 : JSContext *cx;
95 : analyze::CrossScriptSSA *ssa;
96 : JSScript *outerScript;
97 : analyze::ScriptAnalysis *outerAnalysis;
98 :
99 : Compiler &cc;
100 : FrameState &frame;
101 :
102 : /* Basic information about this loop. */
103 : analyze::LoopAnalysis *lifetime;
104 :
105 : /* Allocation at the head of the loop, has all loop carried variables. */
106 : RegisterAllocation *alloc;
107 :
108 : /*
109 : * Set if this is not a do-while loop and the compiler has advanced past
110 : * the loop's entry point.
111 : */
112 : bool reachedEntryPoint;
113 :
114 : /*
115 : * Jump which initially enters the loop. The state is synced when this jump
116 : * occurs, and needs a trampoline generated to load the right registers
117 : * before going to entryTarget.
118 : */
119 : Jump entry;
120 :
121 : /* Registers available for loop variables. */
122 : Registers loopRegs;
123 :
124 : /* Whether to skip all bounds check hoisting and loop invariant code analysis. */
125 : bool skipAnalysis;
126 :
127 : /* Prior stub rejoins to patch when new loop registers are allocated. */
128 624214 : struct StubJoin {
129 : unsigned index;
130 : bool script;
131 : };
132 : Vector<StubJoin,16,CompilerAllocPolicy> loopJoins;
133 :
134 : /* Pending loads to patch for stub rejoins. */
135 171400 : struct StubJoinPatch {
136 : StubJoin join;
137 : Address address;
138 : AnyRegisterID reg;
139 : };
140 : Vector<StubJoinPatch,16,CompilerAllocPolicy> loopPatches;
141 :
142 : /*
143 : * Pair of a jump/label immediately after each call in the loop, to patch
144 : * with restores of the loop invariant stack values.
145 : */
146 102269 : struct RestoreInvariantCall {
147 : Jump jump;
148 : Label label;
149 : bool ool;
150 : bool entry;
151 : unsigned patchIndex; /* Index into Compiler's callSites. */
152 :
153 : /* Any copies of temporaries on the stack */
154 : Vector<TemporaryCopy> *temporaryCopies;
155 : };
156 : Vector<RestoreInvariantCall> restoreInvariantCalls;
157 :
158 : /*
159 : * Aggregate structure for all loop invariant code and hoisted checks we
160 : * can perform. These are all stored in the same vector as they may depend
161 : * on each other and we need to emit code restoring them in order.
162 : */
163 4066 : struct InvariantEntry {
164 : enum EntryKind {
165 : /*
166 : * initializedLength(array) > value1 + value2 + constant.
167 : * Unsigned comparison, so will fail if value + constant < 0
168 : */
169 : DENSE_ARRAY_BOUNDS_CHECK,
170 : TYPED_ARRAY_BOUNDS_CHECK,
171 :
172 : /* value1 + constant >= 0 */
173 : NEGATIVE_CHECK,
174 :
175 : /* constant >= value1 + value2 */
176 : RANGE_CHECK,
177 :
178 : /* For dense arrays */
179 : DENSE_ARRAY_SLOTS,
180 : DENSE_ARRAY_LENGTH,
181 :
182 : /* For typed arrays */
183 : TYPED_ARRAY_SLOTS,
184 : TYPED_ARRAY_LENGTH,
185 :
186 : /* For lazy arguments */
187 : INVARIANT_ARGS_BASE,
188 : INVARIANT_ARGS_LENGTH,
189 :
190 : /* For definite properties */
191 : INVARIANT_PROPERTY
192 : } kind;
193 : union {
194 : struct {
195 : uint32_t arraySlot;
196 : uint32_t valueSlot1;
197 : uint32_t valueSlot2;
198 : int32_t constant;
199 : } check;
200 : struct {
201 : uint32_t arraySlot;
202 : uint32_t temporary;
203 : } array;
204 : struct {
205 : uint32_t objectSlot;
206 : uint32_t propertySlot;
207 : uint32_t temporary;
208 : jsid id;
209 : } property;
210 : } u;
211 4434 : InvariantEntry() { PodZero(this); }
212 14643 : bool isBoundsCheck() const {
213 14643 : return kind == DENSE_ARRAY_BOUNDS_CHECK || kind == TYPED_ARRAY_BOUNDS_CHECK;
214 : }
215 13512 : bool isCheck() const {
216 13512 : return isBoundsCheck() || kind == NEGATIVE_CHECK || kind == RANGE_CHECK;
217 : }
218 : };
219 : Vector<InvariantEntry, 4, CompilerAllocPolicy> invariantEntries;
220 :
221 : static inline bool entryRedundant(const InvariantEntry &e0, const InvariantEntry &e1);
222 : bool checkRedundantEntry(const InvariantEntry &entry);
223 :
224 : bool loopInvariantEntry(uint32_t slot);
225 : bool addHoistedCheck(InvariantArrayKind arrayKind, uint32_t arraySlot,
226 : uint32_t valueSlot1, uint32_t valueSlot2, int32_t constant);
227 : void addNegativeCheck(uint32_t valueSlot, int32_t constant);
228 : void addRangeCheck(uint32_t valueSlot1, uint32_t valueSlot2, int32_t constant);
229 : bool hasTestLinearRelationship(uint32_t slot);
230 :
231 33590 : bool hasInvariants() { return !invariantEntries.empty(); }
232 : void restoreInvariants(jsbytecode *pc, Assembler &masm,
233 : Vector<TemporaryCopy> *temporaryCopies, Vector<Jump> *jumps);
234 :
235 : public:
236 :
237 : /* Outer loop to this one, in case of loop nesting. */
238 : LoopState *outer;
239 :
240 : /* Offset from the outermost frame at which temporaries should be allocated. */
241 : uint32_t temporariesStart;
242 :
243 : LoopState(JSContext *cx, analyze::CrossScriptSSA *ssa,
244 : Compiler *cc, FrameState *frame);
245 : bool init(jsbytecode *head, Jump entry, jsbytecode *entryTarget);
246 :
247 926309 : void setOuterPC(jsbytecode *pc)
248 : {
249 926309 : if (uint32_t(pc - outerScript->code) == lifetime->entry && lifetime->entry != lifetime->head)
250 33521 : reachedEntryPoint = true;
251 926309 : }
252 :
253 715914 : bool generatingInvariants() { return !skipAnalysis; }
254 :
255 : /* Add a call with trailing jump/label, after which invariants need to be restored. */
256 : void addInvariantCall(Jump jump, Label label, bool ool, bool entry, unsigned patchIndex, Uses uses);
257 :
258 94076 : uint32_t headOffset() { return lifetime->head; }
259 124153 : uint32_t getLoopRegs() { return loopRegs.freeMask; }
260 :
261 33590 : Jump entryJump() { return entry; }
262 33590 : uint32_t entryOffset() { return lifetime->entry; }
263 1286 : uint32_t backedgeOffset() { return lifetime->backedge; }
264 :
265 : /* Whether the payload of slot is carried around the loop in a register. */
266 905 : bool carriesLoopReg(FrameEntry *fe) { return alloc->hasAnyReg(frame.entrySlot(fe)); }
267 :
268 : void setLoopReg(AnyRegisterID reg, FrameEntry *fe);
269 :
270 865512 : void clearLoopReg(AnyRegisterID reg)
271 : {
272 : /*
273 : * Mark reg as having been modified since the start of the loop; it
274 : * cannot subsequently be marked to carry a register around the loop.
275 : */
276 865512 : JS_ASSERT(loopRegs.hasReg(reg) == alloc->loop(reg));
277 865512 : if (loopRegs.hasReg(reg)) {
278 136418 : loopRegs.takeReg(reg);
279 136418 : alloc->setUnassigned(reg);
280 136418 : JaegerSpew(JSpew_Regalloc, "clearing loop register %s\n", reg.name());
281 : }
282 865512 : }
283 :
284 : void addJoin(unsigned index, bool script);
285 : void clearLoopRegisters();
286 :
287 : void flushLoop(StubCompiler &stubcc);
288 :
289 : /*
290 : * These should only be used for entries which are known to be dense arrays
291 : * (if they are objects at all).
292 : */
293 : bool hoistArrayLengthCheck(InvariantArrayKind arrayKind,
294 : const analyze::CrossSSAValue &obj,
295 : const analyze::CrossSSAValue &index);
296 : FrameEntry *invariantArraySlots(const analyze::CrossSSAValue &obj);
297 :
298 : /* Methods for accesses on lazy arguments. */
299 : bool hoistArgsLengthCheck(const analyze::CrossSSAValue &index);
300 : FrameEntry *invariantArguments();
301 :
302 : FrameEntry *invariantLength(const analyze::CrossSSAValue &obj);
303 : FrameEntry *invariantProperty(const analyze::CrossSSAValue &obj, jsid id);
304 :
305 : /* Whether a binary or inc/dec op's result cannot overflow. */
306 : bool cannotIntegerOverflow(const analyze::CrossSSAValue &pushed);
307 :
308 : /*
309 : * Whether integer overflow in addition or negative zeros in multiplication
310 : * at a binary op can be safely ignored.
311 : */
312 : bool ignoreIntegerOverflow(const analyze::CrossSSAValue &pushed);
313 :
314 : private:
315 : /* Analysis information for the loop. */
316 :
317 : /*
318 : * Any inequality known to hold at the head of the loop. This has the
319 : * form 'lhs <= rhs + constant' or 'lhs >= rhs + constant', depending on
320 : * lessEqual. The lhs may be modified within the loop body (the test is
321 : * invalid afterwards), and the rhs is invariant. This information is only
322 : * valid if the LHS/RHS are known integers.
323 : */
324 : enum { UNASSIGNED = UINT32_MAX };
325 : uint32_t testLHS;
326 : uint32_t testRHS;
327 : int32_t testConstant;
328 : bool testLessEqual;
329 :
330 : /*
331 : * A variable which will be incremented or decremented exactly once in each
332 : * iteration of the loop. The offset of the operation is indicated, which
333 : * may or may not run after the initial entry into the loop.
334 : */
335 9889 : struct Increment {
336 : uint32_t slot;
337 : uint32_t offset;
338 : };
339 : Vector<Increment, 4, CompilerAllocPolicy> increments;
340 :
341 : /* It is unknown which arrays grow or which objects are modified in this loop. */
342 : bool unknownModset;
343 :
344 : /*
345 : * Arrays which might grow during this loop. This is a guess, and may
346 : * underapproximate the actual set of such arrays.
347 : */
348 : Vector<types::TypeObject *, 4, CompilerAllocPolicy> growArrays;
349 :
350 : /* Properties which might be modified during this loop. */
351 4747 : struct ModifiedProperty {
352 : types::TypeObject *object;
353 : jsid id;
354 : };
355 : Vector<ModifiedProperty, 4, CompilerAllocPolicy> modifiedProperties;
356 :
357 : /*
358 : * Whether this loop only performs integer and double arithmetic and dense
359 : * array accesses. Integer overflows in this loop which only flow to bitops
360 : * can be ignored.
361 : */
362 : bool constrainedLoop;
363 :
364 : void analyzeLoopTest();
365 : void analyzeLoopIncrements();
366 : void analyzeLoopBody(unsigned frame);
367 :
368 : bool definiteArrayAccess(const analyze::SSAValue &obj, const analyze::SSAValue &index);
369 : bool getLoopTestAccess(const analyze::SSAValue &v, uint32_t *pslot, int32_t *pconstant);
370 :
371 : bool addGrowArray(types::TypeObject *object);
372 : bool addModifiedProperty(types::TypeObject *object, jsid id);
373 :
374 : bool hasGrowArray(types::TypeObject *object);
375 : bool hasModifiedProperty(types::TypeObject *object, jsid id);
376 :
377 : uint32_t getIncrement(uint32_t slot);
378 : int32_t adjustConstantForIncrement(jsbytecode *pc, uint32_t slot);
379 :
380 : bool getEntryValue(const analyze::CrossSSAValue &v, uint32_t *pslot, int32_t *pconstant);
381 : bool computeInterval(const analyze::CrossSSAValue &v, int32_t *pmin, int32_t *pmax);
382 : bool valueFlowsToBitops(const analyze::SSAValue &v);
383 : };
384 :
385 : } /* namespace mjit */
386 : } /* namespace js */
387 :
388 : #endif /* jsjaeger_loopstate_h__ */
|