/* -*- 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 * * Contributor(s): * David Anderson * * 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__ */