JSOP_ENDINIT fast path, obsoletes stub call. (r=dvander)
[mozilla-central.git] / js / src / methodjit / FrameState.h
blobaf3532a79a3518938bfcb3e72773bdd02958c07f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 * May 28, 2008.
20 * The Initial Developer of the Original Code is
21 * Brendan Eich <brendan@mozilla.org>
23 * Contributor(s):
24 * David Anderson <danderson@mozilla.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #if !defined jsjaeger_framestate_h__ && defined JS_METHODJIT
41 #define jsjaeger_framestate_h__
43 #include "jsapi.h"
44 #include "methodjit/MachineRegs.h"
45 #include "methodjit/FrameEntry.h"
46 #include "CodeGenIncludes.h"
47 #include "ImmutableSync.h"
49 namespace js {
50 namespace mjit {
52 struct StateRemat {
53 typedef JSC::MacroAssembler::RegisterID RegisterID;
54 union {
55 RegisterID reg : 31;
56 uint32 offset : 31;
58 bool inReg : 1;
62 * The FrameState keeps track of values on the frame during compilation.
63 * The compiler can query FrameState for information about arguments, locals,
64 * and stack slots (all hereby referred to as "slots"). Slot information can
65 * be requested in constant time. For each slot there is a FrameEntry *. If
66 * this is non-NULL, it contains valid information and can be returned.
68 * The register allocator keeps track of registers as being in one of two
69 * states. These are:
71 * 1) Unowned. Some code in the compiler is working on a register.
72 * 2) Owned. The FrameState owns the register, and may spill it at any time.
74 * ------------------ Implementation Details ------------------
76 * Observations:
78 * 1) We totally blow away known information quite often; branches, merge points.
79 * 2) Every time we need a slow call, we must sync everything.
80 * 3) Efficient side-exits need to quickly deltize state snapshots.
81 * 4) Syncing is limited to constants and registers.
82 * 5) Once a value is tracked, there is no reason to "forget" it until #1.
84 * With these in mind, we want to make sure that the compiler doesn't degrade
85 * badly as functions get larger.
87 * If the FE is NULL, a new one is allocated, initialized, and stored. They
88 * are allocated from a pool such that (fe - pool) can be used to compute
89 * the slot's Address.
91 * We keep a side vector of all tracked FrameEntry * to quickly generate
92 * memory stores and clear the tracker.
94 * It is still possible to get really bad behavior with a very large script
95 * that doesn't have branches or calls. That's okay, having this code in
96 * minimizes damage and lets us introduce a hard cut-off point.
98 class FrameState
100 friend class ImmutableSync;
102 typedef JSC::MacroAssembler::RegisterID RegisterID;
103 typedef JSC::MacroAssembler::FPRegisterID FPRegisterID;
104 typedef JSC::MacroAssembler::Address Address;
105 typedef JSC::MacroAssembler::Jump Jump;
106 typedef JSC::MacroAssembler::Imm32 Imm32;
108 static const uint32 InvalidIndex = 0xFFFFFFFF;
110 struct Tracker {
111 Tracker()
112 : entries(NULL), nentries(0)
115 void add(FrameEntry *fe) {
116 entries[nentries++] = fe;
119 void reset() {
120 nentries = 0;
123 FrameEntry * operator [](uint32 n) const {
124 JS_ASSERT(n < nentries);
125 return entries[n];
128 FrameEntry **entries;
129 uint32 nentries;
132 struct RegisterState {
133 RegisterState()
136 RegisterState(FrameEntry *fe, RematInfo::RematType type, bool weak)
137 : fe(fe), type(type), weak(weak)
140 /* FrameEntry owning this register, or NULL if not owned by a frame. */
141 FrameEntry *fe;
143 /* Hack - simplifies register allocation for pairs. */
144 FrameEntry *save;
146 /* Part of the FrameEntry that owns the FE. */
147 RematInfo::RematType type;
149 /* Weak means it is easily spillable. */
150 bool weak;
153 public:
154 FrameState(JSContext *cx, JSScript *script, Assembler &masm);
155 ~FrameState();
156 bool init(uint32 nargs);
159 * Pushes a synced slot.
161 inline void pushSynced();
164 * Pushes a slot that has a known, synced type and payload.
166 inline void pushSyncedType(JSValueMask32 tag);
169 * Pushes a slot that has a known, synced type and payload.
171 inline void pushSynced(JSValueMask32 tag, RegisterID reg);
174 * Pushes a constant value.
176 inline void push(const Value &v);
179 * Loads a value from memory and pushes it.
181 inline void push(Address address);
184 * Pushes a known type and allocated payload onto the operation stack.
186 inline void pushTypedPayload(JSValueMask32 tag, RegisterID payload);
189 * Pushes a type register and data register pair.
191 inline void pushRegs(RegisterID type, RegisterID data);
194 * Pushes a known type and allocated payload onto the operation stack.
195 * This must be used when the type is known, but cannot be propagated
196 * because it is not known to be correct at a slow-path merge point.
198 inline void pushUntypedPayload(JSValueMask32 tag, RegisterID payload);
201 * Pops a value off the operation stack, freeing any of its resources.
203 inline void pop();
206 * Pops a number of values off the operation stack, freeing any of their
207 * resources.
209 inline void popn(uint32 n);
212 * Temporarily increase and decrease local variable depth.
214 inline void enterBlock(uint32 n);
215 inline void leaveBlock(uint32 n);
218 * Pushes a copy of a local variable.
220 void pushLocal(uint32 n);
223 * Allocates a temporary register for a FrameEntry's type. The register
224 * can be spilled or clobbered by the frame. The compiler may only operate
225 * on it temporarily, and must take care not to clobber it.
227 inline RegisterID tempRegForType(FrameEntry *fe);
230 * Returns a register that is guaranteed to contain the frame entry's
231 * data payload. The compiler may not modify the contents of the register.
232 * The compiler should NOT explicitly free it.
234 inline RegisterID tempRegForData(FrameEntry *fe);
237 * Same as above, except register must match identically.
239 inline RegisterID tempRegForData(FrameEntry *fe, RegisterID reg);
242 * Returns a register that contains the constant value of the
243 * frame entry's data payload.
245 inline RegisterID tempRegForConstant(FrameEntry *fe);
248 * Allocates a register for a FrameEntry's data, such that the compiler
249 * can modify it in-place.
251 * The caller guarantees the FrameEntry will not be observed again. This
252 * allows the compiler to avoid spilling. Only call this if the FE is
253 * going to be popped before stubcc joins/guards or the end of the current
254 * opcode.
256 RegisterID ownRegForData(FrameEntry *fe);
259 * Allocates a register for a FrameEntry's type, such that the compiler
260 * can modify it in-place.
262 * The caller guarantees the FrameEntry will not be observed again. This
263 * allows the compiler to avoid spilling. Only call this if the FE is
264 * going to be popped before stubcc joins/guards or the end of the current
265 * opcode.
267 RegisterID ownRegForType(FrameEntry *fe);
270 * Allocates a register for a FrameEntry's data, such that the compiler
271 * can modify it in-place. The actual FE is not modified.
273 RegisterID copyDataIntoReg(FrameEntry *fe);
276 * Types don't always have to be in registers, sometimes the compiler
277 * can use addresses and avoid spilling. If this FrameEntry has a synced
278 * address and no register, this returns true.
280 inline bool shouldAvoidTypeRemat(FrameEntry *fe);
283 * Payloads don't always have to be in registers, sometimes the compiler
284 * can use addresses and avoid spilling. If this FrameEntry has a synced
285 * address and no register, this returns true.
287 inline bool shouldAvoidDataRemat(FrameEntry *fe);
290 * Frees a temporary register. If this register is being tracked, then it
291 * is not spilled; the backing data becomes invalidated!
293 inline void freeReg(RegisterID reg);
296 * Allocates a register. If none are free, one may be spilled from the
297 * tracker. If there are none available for spilling in the tracker,
298 * then this is considered a compiler bug and an assert will fire.
300 inline RegisterID allocReg();
303 * Allocates a register, except using a mask.
305 inline RegisterID allocReg(uint32 mask);
308 * Allocates a specific register, evicting it if it's not avaliable.
310 void takeReg(RegisterID reg);
313 * Returns a FrameEntry * for a slot on the operation stack.
315 inline FrameEntry *peek(int32 depth);
318 * Fully stores a FrameEntry at an arbitrary address. popHint specifies
319 * how hard the register allocator should try to keep the FE in registers.
321 void storeTo(FrameEntry *fe, Address address, bool popHint);
324 * Stores the top stack slot back to a local variable.
326 void storeLocal(uint32 n, bool popGuaranteed = false);
329 * Restores state from a slow path.
331 void merge(Assembler &masm, uint32 ivD) const;
334 * Writes unsynced stores to an arbitrary buffer.
336 void sync(Assembler &masm) const;
339 * Syncs all outstanding stores to memory and possibly kills regs in the
340 * process.
342 void syncAndKill(uint32 mask);
345 * Syncs the entire frame for a call.
347 void syncForCall(uint32 argc);
350 * Clear all tracker entries, syncing all outstanding stores in the process.
351 * The stack depth is in case some merge points' edges did not immediately
352 * precede the current instruction.
354 inline void forgetEverything(uint32 newStackDepth);
357 * Same as above, except the stack depth is not changed. This is used for
358 * branching opcodes.
360 void forgetEverything();
363 * Throw away the entire frame state, without syncing anything.
365 void throwaway();
368 * Mark an existing slot with a type.
370 inline void learnType(FrameEntry *fe, JSValueMask32 tag);
373 * Forget a type, syncing in the process.
375 inline void forgetType(FrameEntry *fe);
378 * Helper function. Tests if a slot's type is an integer. Condition should
379 * be Equal or NotEqual.
381 inline Jump testInt32(Assembler::Condition cond, FrameEntry *fe);
384 * Helper function. Tests if a slot's type is a double. Condition should
385 * be Equal or Not Equal.
387 inline Jump testDouble(Assembler::Condition cond, FrameEntry *fe);
390 * Helper function. Tests if a slot's type is an integer. Condition should
391 * be Equal or NotEqual.
393 inline Jump testBoolean(Assembler::Condition cond, FrameEntry *fe);
396 * Helper function. Tests if a slot's type is a non-funobj. Condition should
397 * be Equal or NotEqual.
399 inline Jump testNonFunObj(Assembler::Condition cond, FrameEntry *fe);
402 * Helper function. Tests if a slot's type is a funobj. Condition should
403 * be Equal or NotEqual.
405 inline Jump testFunObj(Assembler::Condition cond, FrameEntry *fe);
408 * Helper function. Tests if a slot's type is primitve. Condition should
409 * be Equal or NotEqual.
411 inline Jump testPrimitive(Assembler::Condition cond, FrameEntry *fe);
414 * Marks a register such that it cannot be spilled by the register
415 * allocator. Any pinned registers must be unpinned at the end of the op.
416 * Note: This function should only be used on registers tied to FEs.
418 inline void pinReg(RegisterID reg);
421 * Unpins a previously pinned register.
423 inline void unpinReg(RegisterID reg);
426 * Dups the top item on the stack.
428 inline void dup();
431 * Dups the top 2 items on the stack.
433 inline void dup2();
436 * Returns the current stack depth of the frame.
438 uint32 stackDepth() const { return sp - spBase; }
439 uint32 frameDepth() const { return stackDepth() + script->nfixed; }
440 inline FrameEntry *tosFe() const;
442 #ifdef DEBUG
443 void assertValidRegisterState() const;
444 #endif
446 Address addressOf(const FrameEntry *fe) const;
448 inline StateRemat dataRematInfo(const FrameEntry *fe) const;
451 * This is similar to freeReg(ownRegForData(fe)) - except no movement takes place.
452 * The fe is simply invalidated as if it were popped. This can be used to free
453 * registers in the working area of the stack. Obviously, this can only be called
454 * in infallible code that will pop these entries soon after.
456 inline void eviscerate(FrameEntry *fe);
459 * Moves the top of the stack down N slots, popping each item above it.
460 * Caller guarantees the slots below have been observed and eviscerated.
462 void shimmy(uint32 n);
465 * Stores the top item on the stack to a stack slot, count down from the
466 * current stack depth. For example, to move the top (-1) to -3, you would
467 * call shift(-2).
469 void shift(int32 n);
471 inline void addEscaping(uint32 local);
473 private:
474 inline RegisterID allocReg(FrameEntry *fe, RematInfo::RematType type, bool weak);
475 inline void forgetReg(RegisterID reg);
476 RegisterID evictSomeReg(uint32 mask);
477 void evictReg(RegisterID reg);
478 inline FrameEntry *rawPush();
479 inline FrameEntry *addToTracker(uint32 index);
480 inline void syncType(const FrameEntry *fe, Address to, Assembler &masm) const;
481 inline void syncData(const FrameEntry *fe, Address to, Assembler &masm) const;
482 inline FrameEntry *getLocal(uint32 slot);
483 inline void forgetAllRegs(FrameEntry *fe);
484 inline void swapInTracker(FrameEntry *lhs, FrameEntry *rhs);
485 inline uint32 localIndex(uint32 n);
486 void pushCopyOf(uint32 index);
487 void syncFancy(Assembler &masm, Registers avail, uint32 resumeAt) const;
490 * "Uncopies" the backing store of a FrameEntry that has been copied. The
491 * original FrameEntry is not invalidated; this is the responsibility of
492 * the caller. The caller can check isCopied() to see if the registers
493 * were moved to a copy.
495 void uncopy(FrameEntry *original);
497 FrameEntry *entryFor(uint32 index) const {
498 JS_ASSERT(base[index]);
499 return &entries[index];
502 void moveOwnership(RegisterID reg, FrameEntry *newFe) {
503 regstate[reg].fe = newFe;
506 RegisterID evictSomeReg() {
507 return evictSomeReg(Registers::AvailRegs);
510 uint32 indexOf(int32 depth) {
511 return uint32((sp + depth) - base);
514 uint32 indexOfFe(FrameEntry *fe) {
515 return uint32(fe - entries);
518 private:
519 JSContext *cx;
520 JSScript *script;
521 uint32 nargs;
522 Assembler &masm;
524 /* All allocated registers. */
525 Registers freeRegs;
527 /* Cache of FrameEntry objects. */
528 FrameEntry *entries;
530 /* Base pointer of the FrameEntry vector. */
531 FrameEntry **base;
533 /* Base pointer for arguments. */
534 FrameEntry **args;
536 /* Base pointer for local variables. */
537 FrameEntry **locals;
539 /* Base pointer for the stack. */
540 FrameEntry **spBase;
542 /* Dynamic stack pointer. */
543 FrameEntry **sp;
545 /* Vector of tracked slot indexes. */
546 Tracker tracker;
549 * Register ownership state. This can't be used alone; to find whether an
550 * entry is active, you must check the allocated registers.
552 RegisterState regstate[Assembler::TotalRegisters];
554 mutable ImmutableSync reifier;
556 uint32 *escaping;
557 bool eval;
560 } /* namespace mjit */
561 } /* namespace js */
563 #endif /* jsjaeger_framestate_h__ */