uox3/spidermonkey/methodjit/FrameState.h
2025-03-19 21:25:58 +10:00

906 lines
29 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
* May 28, 2008.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>
*
* Contributor(s):
* David Anderson <danderson@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#if !defined jsjaeger_framestate_h__ && defined JS_METHODJIT
#define jsjaeger_framestate_h__
#include "jsapi.h"
#include "methodjit/MachineRegs.h"
#include "methodjit/FrameEntry.h"
#include "CodeGenIncludes.h"
#include "ImmutableSync.h"
#include "jscompartment.h"
namespace js {
namespace mjit {
struct Uses {
explicit Uses(uint32 nuses)
: nuses(nuses)
{ }
uint32 nuses;
};
struct Changes {
explicit Changes(uint32 nchanges)
: nchanges(nchanges)
{ }
uint32 nchanges;
};
/*
* The FrameState keeps track of values on the frame during compilation.
* The compiler can query FrameState for information about arguments, locals,
* and stack slots (all hereby referred to as "slots"). Slot information can
* be requested in constant time. For each slot there is a FrameEntry *. If
* this is non-NULL, it contains valid information and can be returned.
*
* The register allocator keeps track of registers as being in one of two
* states. These are:
*
* 1) Unowned. Some code in the compiler is working on a register.
* 2) Owned. The FrameState owns the register, and may spill it at any time.
*
* ------------------ Implementation Details ------------------
*
* Observations:
*
* 1) We totally blow away known information quite often; branches, merge points.
* 2) Every time we need a slow call, we must sync everything.
* 3) Efficient side-exits need to quickly deltize state snapshots.
* 4) Syncing is limited to constants and registers.
* 5) Once a value is tracked, there is no reason to "forget" it until #1.
*
* With these in mind, we want to make sure that the compiler doesn't degrade
* badly as functions get larger.
*
* If the FE is NULL, a new one is allocated, initialized, and stored. They
* are allocated from a pool such that (fe - pool) can be used to compute
* the slot's Address.
*
* We keep a side vector of all tracked FrameEntry * to quickly generate
* memory stores and clear the tracker.
*
* It is still possible to get really bad behavior with a very large script
* that doesn't have branches or calls. That's okay, having this code in
* minimizes damage and lets us introduce a hard cut-off point.
*/
class FrameState
{
friend class ImmutableSync;
typedef JSC::MacroAssembler::RegisterID RegisterID;
typedef JSC::MacroAssembler::FPRegisterID FPRegisterID;
typedef JSC::MacroAssembler::Address Address;
typedef JSC::MacroAssembler::Jump Jump;
typedef JSC::MacroAssembler::Imm32 Imm32;
static const uint32 InvalidIndex = 0xFFFFFFFF;
struct Tracker {
Tracker()
: entries(NULL), nentries(0)
{ }
void add(FrameEntry *fe) {
entries[nentries++] = fe;
}
void reset() {
nentries = 0;
}
FrameEntry * operator [](uint32 n) const {
JS_ASSERT(n < nentries);
return entries[n];
}
FrameEntry **entries;
uint32 nentries;
};
/*
* Some RegisterState invariants.
*
* If |fe| is non-NULL, |save| is NULL.
* If |save| is non-NULL, |fe| is NULL.
* That is, both |fe| and |save| cannot be non-NULL.
*
* If either |fe| or |save| is non-NULL, the register is not in freeRegs.
* If both |fe| and |save| are NULL, the register is either in freeRegs,
* or owned by the compiler.
*/
struct RegisterState {
RegisterState() : fe_(NULL), save_(NULL)
{ }
RegisterState(FrameEntry *fe, RematInfo::RematType type)
: fe_(fe), save_(NULL), type_(type)
{
JS_ASSERT(!save_);
}
bool isPinned() const {
assertConsistency();
return !!save_;
}
void assertConsistency() const {
JS_ASSERT_IF(fe_, !save_);
JS_ASSERT_IF(save_, !fe_);
}
FrameEntry *fe() const {
assertConsistency();
return fe_;
}
RematInfo::RematType type() const {
assertConsistency();
return type_;
}
FrameEntry *usedBy() const {
if (fe_)
return fe_;
return save_;
}
void associate(FrameEntry *fe, RematInfo::RematType type) {
JS_ASSERT(!fe_);
JS_ASSERT(!save_);
fe_ = fe;
type_ = type;
JS_ASSERT(!save_);
}
/* Change ownership. */
void reassociate(FrameEntry *fe) {
assertConsistency();
JS_ASSERT(fe);
fe_ = fe;
}
/* Unassociate this register from the FE. */
void forget() {
JS_ASSERT(fe_);
fe_ = NULL;
JS_ASSERT(!save_);
}
void pin() {
JS_ASSERT(fe_ != NULL);
assertConsistency();
save_ = fe_;
fe_ = NULL;
}
void unpin() {
JS_ASSERT(save_ != NULL);
assertConsistency();
fe_ = save_;
save_ = NULL;
}
void unpinUnsafe() {
assertConsistency();
save_ = NULL;
}
private:
/* FrameEntry owning this register, or NULL if not owned by a frame. */
FrameEntry *fe_;
/* Hack - simplifies register allocation for pairs. */
FrameEntry *save_;
/* Part of the FrameEntry that owns the FE. */
RematInfo::RematType type_;
};
FrameState *thisFromCtor() { return this; }
public:
FrameState(JSContext *cx, JSScript *script, JSFunction *fun, Assembler &masm);
~FrameState();
bool init();
/*
* Pushes a synced slot.
*/
inline void pushSynced();
/*
* Pushes a slot that has a known, synced type and payload.
*/
inline void pushSyncedType(JSValueType type);
/*
* Pushes a slot that has a known, synced type and payload.
*/
inline void pushSynced(JSValueType type, RegisterID reg);
/*
* Pushes a constant value.
*/
inline void push(const Value &v);
/*
* Loads a value from memory and pushes it.
*/
inline void push(Address address);
/*
* Pushes a known type and allocated payload onto the operation stack.
*/
inline void pushTypedPayload(JSValueType type, RegisterID payload);
/*
* Pushes a type register and data register pair.
*/
inline void pushRegs(RegisterID type, RegisterID data);
/*
* Pushes a known type and allocated payload onto the operation stack.
* This must be used when the type is known, but cannot be propagated
* because it is not known to be correct at a slow-path merge point.
*
* The caller guarantees that the tag was a fast-path check; that is,
* the value it replaces on the stack had the same tag if the fast-path
* was taken.
*/
inline void pushUntypedPayload(JSValueType type, RegisterID payload);
/*
* Pushes a number onto the operation stack.
*
* If asInt32 is set to true, then the FS will attempt to optimize
* syncing the type as int32. Only use this parameter when the fast-path
* guaranteed that the stack slot was guarded to be an int32 originally.
*
* For example, checking LHS and RHS as ints guarantees that if the LHS
* was synced, then popping both and pushing a maybe-int32 does not need
* to be synced.
*/
inline void pushNumber(MaybeRegisterID payload, bool asInt32 = false);
/*
* Pushes an int32 onto the operation stack. This is a specialized version
* of pushNumber. The caller must guarantee that (a) an int32 is to be
* pushed on the inline path, and (b) if any slow path pushes a double,
* the slow path also stores the double to memory.
*/
inline void pushInt32(RegisterID payload);
/*
* Pushes an initializer with specified payload, storing whether it is an array
* or object whose contents can be initialized in fast paths.
*/
inline void pushInitializerObject(RegisterID payload, bool array, JSObject *baseobj);
/*
* Pops a value off the operation stack, freeing any of its resources.
*/
inline void pop();
/*
* Pops a number of values off the operation stack, freeing any of their
* resources.
*/
inline void popn(uint32 n);
/*
* Returns true iff lhs and rhs are copies of the same FrameEntry.
*/
inline bool haveSameBacking(FrameEntry *lhs, FrameEntry *rhs);
/*
* Temporarily increase and decrease local variable depth.
*/
inline void enterBlock(uint32 n);
inline void leaveBlock(uint32 n);
// Pushes a copy of a slot (formal argument, local variable, or stack slot)
// onto the operation stack.
void pushLocal(uint32 n);
void pushArg(uint32 n);
void pushCallee();
void pushThis();
inline void learnThisIsObject();
/*
* Allocates a temporary register for a FrameEntry's type. The register
* can be spilled or clobbered by the frame. The compiler may only operate
* on it temporarily, and must take care not to clobber it.
*/
inline RegisterID tempRegForType(FrameEntry *fe);
/*
* Try to use a register already allocated for fe's type, but if one
* is not already available, use fallback.
*
* Note: this does NOT change fe's type-register remat info. It's supposed
* to be a super lightweight/transparent operation.
*/
inline RegisterID tempRegForType(FrameEntry *fe, RegisterID fallback);
/*
* Returns a register that is guaranteed to contain the frame entry's
* data payload. The compiler may not modify the contents of the register.
* The compiler should NOT explicitly free it.
*/
inline RegisterID tempRegForData(FrameEntry *fe);
/*
* Same as above, except register must match identically.
*/
inline RegisterID tempRegInMaskForData(FrameEntry *fe, uint32 mask);
/*
* Same as above, except loads into reg (using masm) if the entry does not
* already have a register, and does not change the frame state in doing so.
*/
inline RegisterID tempRegForData(FrameEntry *fe, RegisterID reg, Assembler &masm) const;
/*
* Convert an integer to a double without applying
* additional Register pressure.
*/
inline void convertInt32ToDouble(Assembler &masm, FrameEntry *fe,
FPRegisterID fpreg) const;
/*
* Dive into a FrameEntry and check whether it's in a register.
*/
inline bool peekTypeInRegister(FrameEntry *fe) const;
/*
* Allocates a register for a FrameEntry's data, such that the compiler
* can modify it in-place.
*
* The caller guarantees the FrameEntry will not be observed again. This
* allows the compiler to avoid spilling. Only call this if the FE is
* going to be popped before stubcc joins/guards or the end of the current
* opcode.
*/
RegisterID ownRegForData(FrameEntry *fe);
/*
* Allocates a register for a FrameEntry's type, such that the compiler
* can modify it in-place.
*
* The caller guarantees the FrameEntry will not be observed again. This
* allows the compiler to avoid spilling. Only call this if the FE is
* going to be popped before stubcc joins/guards or the end of the current
* opcode.
*/
RegisterID ownRegForType(FrameEntry *fe);
/*
* Allocates a register for a FrameEntry's data, such that the compiler
* can modify it in-place. The actual FE is not modified.
*/
RegisterID copyDataIntoReg(FrameEntry *fe);
void copyDataIntoReg(FrameEntry *fe, RegisterID exact);
RegisterID copyDataIntoReg(Assembler &masm, FrameEntry *fe);
/*
* Allocates a FPRegister for a FrameEntry, such that the compiler
* can modify it in-place. The FrameState is not modified.
*/
FPRegisterID copyEntryIntoFPReg(FrameEntry *fe, FPRegisterID fpreg);
FPRegisterID copyEntryIntoFPReg(Assembler &masm, FrameEntry *fe,
FPRegisterID fpreg);
/*
* Allocates a register for a FrameEntry's type, such that the compiler
* can modify it in-place. The actual FE is not modified.
*/
RegisterID copyTypeIntoReg(FrameEntry *fe);
/*
* Returns a register that contains the constant Int32 value of the
* frame entry's data payload.
* Since the register is not bound to a FrameEntry,
* it MUST be explicitly freed with freeReg().
*/
RegisterID copyInt32ConstantIntoReg(FrameEntry *fe);
RegisterID copyInt32ConstantIntoReg(Assembler &masm, FrameEntry *fe);
/*
* Gets registers for the components of fe where needed,
* pins them and stores into vr.
*/
void pinEntry(FrameEntry *fe, ValueRemat &vr);
/* Unpins registers from a call to pinEntry. */
void unpinEntry(const ValueRemat &vr);
/* Syncs fe to memory, given its state as constructed by a call to pinEntry. */
void ensureValueSynced(Assembler &masm, FrameEntry *fe, const ValueRemat &vr);
struct BinaryAlloc {
MaybeRegisterID lhsType;
MaybeRegisterID lhsData;
MaybeRegisterID rhsType;
MaybeRegisterID rhsData;
MaybeRegisterID extraFree;
RegisterID result; // mutable result reg
bool resultHasRhs; // whether the result has the RHS instead of the LHS
bool lhsNeedsRemat; // whether LHS needs memory remat
bool rhsNeedsRemat; // whether RHS needs memory remat
};
/*
* Ensures that the two given FrameEntries have registers for both their
* type and data. The register allocations are returned in a struct.
*
* One mutable register is allocated as well, holding the LHS payload. If
* this would cause a spill that could be avoided by using a mutable RHS,
* and the operation is commutative, then the resultHasRhs is set to true.
*/
void allocForBinary(FrameEntry *lhs, FrameEntry *rhs, JSOp op, BinaryAlloc &alloc,
bool resultNeeded = true);
/* Ensures that an FE has both type and data remat'd in registers. */
void ensureFullRegs(FrameEntry *fe, MaybeRegisterID *typeReg, MaybeRegisterID *dataReg);
/*
* Similar to allocForBinary, except works when the LHS and RHS have the
* same backing FE. Only a reduced subset of BinaryAlloc is used:
* lhsType
* lhsData
* result
* lhsNeedsRemat
*/
void allocForSameBinary(FrameEntry *fe, JSOp op, BinaryAlloc &alloc);
/* Loads an FE into an fp reg. */
inline void loadDouble(FrameEntry *fe, FPRegisterID fpReg, Assembler &masm) const;
/*
* Slightly more specialized version when more precise register
* information is known.
*/
inline void loadDouble(RegisterID type, RegisterID data, FrameEntry *fe, FPRegisterID fpReg,
Assembler &masm) const;
/*
* Types don't always have to be in registers, sometimes the compiler
* can use addresses and avoid spilling. If this FrameEntry has a synced
* address and no register, this returns true.
*/
inline bool shouldAvoidTypeRemat(FrameEntry *fe);
/*
* Payloads don't always have to be in registers, sometimes the compiler
* can use addresses and avoid spilling. If this FrameEntry has a synced
* address and no register, this returns true.
*/
inline bool shouldAvoidDataRemat(FrameEntry *fe);
/*
* Frees a temporary register. If this register is being tracked, then it
* is not spilled; the backing data becomes invalidated!
*/
inline void freeReg(RegisterID reg);
/*
* Allocates a register. If none are free, one may be spilled from the
* tracker. If there are none available for spilling in the tracker,
* then this is considered a compiler bug and an assert will fire.
*/
inline RegisterID allocReg();
/*
* Allocates a register, except using a mask.
*/
inline RegisterID allocReg(uint32 mask);
/*
* Allocates a specific register, evicting it if it's not avaliable.
*/
void takeReg(RegisterID reg);
/*
* Returns a FrameEntry * for a slot on the operation stack.
*/
inline FrameEntry *peek(int32 depth);
/*
* Fully stores a FrameEntry at an arbitrary address. popHint specifies
* how hard the register allocator should try to keep the FE in registers.
*/
void storeTo(FrameEntry *fe, Address address, bool popHint = false);
/*
* Fully stores a FrameEntry into two arbitrary registers. tempReg may be
* used as a temporary.
*/
void loadForReturn(FrameEntry *fe, RegisterID typeReg, RegisterID dataReg, RegisterID tempReg);
void loadThisForReturn(RegisterID typeReg, RegisterID dataReg, RegisterID tempReg);
/*
* Stores the top stack slot back to a slot.
*/
void storeLocal(uint32 n, bool popGuaranteed = false, bool typeChange = true);
void storeArg(uint32 n, bool popGuaranteed = false);
void storeTop(FrameEntry *target, bool popGuaranteed = false, bool typeChange = true);
void finishStore(FrameEntry *fe, bool closed);
/*
* Restores state from a slow path.
*/
void merge(Assembler &masm, Changes changes) const;
/*
* Writes unsynced stores to an arbitrary buffer.
*/
void sync(Assembler &masm, Uses uses) const;
/*
* Syncs all outstanding stores to memory and possibly kills regs in the
* process. The top [ignored..uses-1] frame entries will be synced.
*/
void syncAndKill(Registers kill, Uses uses, Uses ignored);
void syncAndKill(Registers kill, Uses uses) { syncAndKill(kill, uses, Uses(0)); }
/* Syncs and kills everything. */
void syncAndKillEverything() {
syncAndKill(Registers(Registers::AvailRegs), Uses(frameSlots()));
}
/*
* Clear all tracker entries, syncing all outstanding stores in the process.
* The stack depth is in case some merge points' edges did not immediately
* precede the current instruction.
*/
inline void syncAndForgetEverything(uint32 newStackDepth);
/*
* Same as above, except the stack depth is not changed. This is used for
* branching opcodes.
*/
void syncAndForgetEverything();
/*
* Throw away the entire frame state, without syncing anything.
* This can only be called after a syncAndKill() against all registers.
*/
void forgetEverything();
/*
* Discard the entire framestate forcefully.
*/
void discardFrame();
/*
* Mark an existing slot with a type.
*/
inline void learnType(FrameEntry *fe, JSValueType type);
/*
* Forget a type, syncing in the process.
*/
inline void forgetType(FrameEntry *fe);
/*
* Discards a FrameEntry, tricking the FS into thinking it's synced.
*/
void discardFe(FrameEntry *fe);
/*
* Helper function. Tests if a slot's type is null. Condition must
* be Equal or NotEqual.
*/
inline Jump testNull(Assembler::Condition cond, FrameEntry *fe);
/*
* Helper function. Tests if a slot's type is undefined. Condition must
* be Equal or NotEqual.
*/
inline Jump testUndefined(Assembler::Condition cond, FrameEntry *fe);
/*
* Helper function. Tests if a slot's type is an integer. Condition must
* be Equal or NotEqual.
*/
inline Jump testInt32(Assembler::Condition cond, FrameEntry *fe);
/*
* Helper function. Tests if a slot's type is a double. Condition must
* be Equal or Not Equal.
*/
inline Jump testDouble(Assembler::Condition cond, FrameEntry *fe);
/*
* Helper function. Tests if a slot's type is a boolean. Condition must
* be Equal or NotEqual.
*/
inline Jump testBoolean(Assembler::Condition cond, FrameEntry *fe);
/*
* Helper function. Tests if a slot's type is a string. Condition must
* be Equal or NotEqual.
*/
inline Jump testString(Assembler::Condition cond, FrameEntry *fe);
/*
* Helper function. Tests if a slot's type is a non-funobj. Condition must
* be Equal or NotEqual.
*/
inline Jump testObject(Assembler::Condition cond, FrameEntry *fe);
/*
* Helper function. Tests if a slot's type is primitive. Condition must
* be Equal or NotEqual.
*/
inline Jump testPrimitive(Assembler::Condition cond, FrameEntry *fe);
/*
* Marks a register such that it cannot be spilled by the register
* allocator. Any pinned registers must be unpinned at the end of the op,
* no matter what. In addition, pinReg() can only be used on registers
* which are associated with FrameEntries.
*/
inline void pinReg(RegisterID reg);
/*
* Unpins a previously pinned register.
*/
inline void unpinReg(RegisterID reg);
/*
* Same as unpinReg(), but does not restore the FrameEntry.
*/
inline void unpinKilledReg(RegisterID reg);
/* Pins a data or type register if one exists. */
MaybeRegisterID maybePinData(FrameEntry *fe);
MaybeRegisterID maybePinType(FrameEntry *fe);
void maybeUnpinReg(MaybeRegisterID reg);
/*
* Dups the top item on the stack.
*/
inline void dup();
/*
* Dups the top 2 items on the stack.
*/
inline void dup2();
/*
* Dups an item n-deep in the stack. n must be < 0
*/
inline void dupAt(int32 n);
/*
* If the frameentry is a copy, give it its own registers.
* This may only be called on the topmost fe.
*/
inline void giveOwnRegs(FrameEntry *fe);
uint32 stackDepth() const { return sp - spBase; }
// Returns the number of entries in the frame, that is:
// 2 for callee, this +
// nargs +
// nfixed +
// currently pushed stack slots
uint32 frameSlots() const { return uint32(sp - entries); }
// Returns the number of local variables and active stack slots.
uint32 localSlots() const { return uint32(sp - locals); }
#ifdef DEBUG
void assertValidRegisterState() const;
#endif
// Return an address, relative to the JSStackFrame, that represents where
// this FrameEntry is stored in memory. Note that this is its canonical
// address, not its backing store. There is no guarantee that the memory
// is coherent.
Address addressOf(const FrameEntry *fe) const;
// Returns an address, relative to the JSStackFrame, that represents where
// this FrameEntry is backed in memory. This is not necessarily its
// canonical address, but the address for which the payload has been synced
// to memory. The caller guarantees that the payload has been synced.
Address addressForDataRemat(const FrameEntry *fe) const;
inline StateRemat dataRematInfo(const FrameEntry *fe) const;
/*
* This is similar to freeReg(ownRegForData(fe)) - except no movement takes place.
* The fe is simply invalidated as if it were popped. This can be used to free
* registers in the working area of the stack. Obviously, this can only be called
* in infallible code that will pop these entries soon after.
*/
inline void eviscerate(FrameEntry *fe);
/*
* Moves the top of the stack down N slots, popping each item above it.
* Caller guarantees the slots below have been observed and eviscerated.
*/
void shimmy(uint32 n);
/*
* Stores the top item on the stack to a stack slot, count down from the
* current stack depth. For example, to move the top (-1) to -3, you would
* call shift(-2).
*/
void shift(int32 n);
// Notifies the frame that a local variable or argument slot is closed over.
inline void setClosedVar(uint32 slot);
inline void setClosedArg(uint32 slot);
inline void setInTryBlock(bool inTryBlock) {
this->inTryBlock = inTryBlock;
}
inline uint32 regsInUse() const { return Registers::AvailRegs & ~freeRegs.freeMask; }
private:
inline RegisterID allocReg(FrameEntry *fe, RematInfo::RematType type);
inline void forgetReg(RegisterID reg);
RegisterID evictSomeReg(uint32 mask);
void evictReg(RegisterID reg);
inline FrameEntry *rawPush();
inline void addToTracker(FrameEntry *fe);
/* Guarantee sync, but do not set any sync flag. */
inline void ensureFeSynced(const FrameEntry *fe, Assembler &masm) const;
inline void ensureTypeSynced(const FrameEntry *fe, Assembler &masm) const;
inline void ensureDataSynced(const FrameEntry *fe, Assembler &masm) const;
/* Guarantee sync, even if register allocation is required, and set sync. */
inline void syncFe(FrameEntry *fe);
inline void syncType(FrameEntry *fe);
inline void syncData(FrameEntry *fe);
inline FrameEntry *getOrTrack(uint32 index);
inline FrameEntry *getLocal(uint32 slot);
inline FrameEntry *getArg(uint32 slot);
inline FrameEntry *getCallee();
inline FrameEntry *getThis();
inline void forgetAllRegs(FrameEntry *fe);
inline void swapInTracker(FrameEntry *lhs, FrameEntry *rhs);
void pushCopyOf(uint32 index);
#if defined JS_NUNBOX32
void syncFancy(Assembler &masm, Registers avail, FrameEntry *resumeAt,
FrameEntry *bottom) const;
#endif
inline bool tryFastDoubleLoad(FrameEntry *fe, FPRegisterID fpReg, Assembler &masm) const;
void resetInternalState();
/*
* "Uncopies" the backing store of a FrameEntry that has been copied. The
* original FrameEntry is not invalidated; this is the responsibility of
* the caller. The caller can check isCopied() to see if the registers
* were moved to a copy.
*
* Later addition: uncopy() returns the first copy found.
*/
FrameEntry *uncopy(FrameEntry *original);
FrameEntry *walkTrackerForUncopy(FrameEntry *original);
FrameEntry *walkFrameForUncopy(FrameEntry *original);
/*
* All registers in the FE are forgotten. If it is copied, it is uncopied
* beforehand.
*/
void forgetEntry(FrameEntry *fe);
FrameEntry *entryFor(uint32 index) const {
JS_ASSERT(entries[index].isTracked());
return &entries[index];
}
RegisterID evictSomeReg() { return evictSomeReg(Registers::AvailRegs); }
uint32 indexOf(int32 depth) const {
JS_ASSERT(uint32((sp + depth) - entries) < feLimit());
return uint32((sp + depth) - entries);
}
uint32 indexOfFe(FrameEntry *fe) const {
JS_ASSERT(uint32(fe - entries) < feLimit());
return uint32(fe - entries);
}
uint32 feLimit() const { return script->nslots + nargs + 2; }
inline bool isClosedVar(uint32 slot);
inline bool isClosedArg(uint32 slot);
private:
JSContext *cx;
JSScript *script;
JSFunction *fun;
uint32 nargs;
Assembler &masm;
/* All allocated registers. */
Registers freeRegs;
/* Cache of FrameEntry objects. */
FrameEntry *entries;
FrameEntry *callee_;
FrameEntry *this_;
/* Base pointer for arguments. */
FrameEntry *args;
/* Base pointer for local variables. */
FrameEntry *locals;
/* Base pointer for the stack. */
FrameEntry *spBase;
/* Dynamic stack pointer. */
FrameEntry *sp;
/* Vector of tracked slot indexes. */
Tracker tracker;
/*
* Register ownership state. This can't be used alone; to find whether an
* entry is active, you must check the allocated registers.
*/
RegisterState regstate[Assembler::TotalRegisters];
#if defined JS_NUNBOX32
mutable ImmutableSync reifier;
#endif
JSPackedBool *closedVars;
JSPackedBool *closedArgs;
bool eval;
bool usesArguments;
bool inTryBlock;
};
class AutoPreserveAcrossSyncAndKill;
} /* namespace mjit */
} /* namespace js */
#endif /* jsjaeger_framestate_h__ */