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
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
20 * The Initial Developer of the Original Code is
21 * Brendan Eich <brendan@mozilla.org>
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__
44 #include "methodjit/MachineRegs.h"
45 #include "methodjit/FrameEntry.h"
46 #include "CodeGenIncludes.h"
47 #include "ImmutableSync.h"
53 typedef JSC::MacroAssembler::RegisterID RegisterID
;
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
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 ------------------
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
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.
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;
112 : entries(NULL
), nentries(0)
115 void add(FrameEntry
*fe
) {
116 entries
[nentries
++] = fe
;
123 FrameEntry
* operator [](uint32 n
) const {
124 JS_ASSERT(n
< nentries
);
128 FrameEntry
**entries
;
132 struct 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. */
143 /* Hack - simplifies register allocation for pairs. */
146 /* Part of the FrameEntry that owns the FE. */
147 RematInfo::RematType type
;
149 /* Weak means it is easily spillable. */
154 FrameState(JSContext
*cx
, JSScript
*script
, Assembler
&masm
);
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.
206 * Pops a number of values off the operation stack, freeing any of their
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
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
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
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
360 void forgetEverything();
363 * Throw away the entire frame state, without syncing anything.
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.
431 * Dups the top 2 items on the stack.
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;
443 void assertValidRegisterState() const;
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
471 inline void addEscaping(uint32 local
);
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
);
524 /* All allocated registers. */
527 /* Cache of FrameEntry objects. */
530 /* Base pointer of the FrameEntry vector. */
533 /* Base pointer for arguments. */
536 /* Base pointer for local variables. */
539 /* Base pointer for the stack. */
542 /* Dynamic stack pointer. */
545 /* Vector of tracked slot indexes. */
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
;
560 } /* namespace mjit */
563 #endif /* jsjaeger_framestate_h__ */