summaryrefslogtreecommitdiff
path: root/js/src/methodjit/FrameState-inl.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/methodjit/FrameState-inl.h')
-rw-r--r--js/src/methodjit/FrameState-inl.h1164
1 files changed, 1164 insertions, 0 deletions
diff --git a/js/src/methodjit/FrameState-inl.h b/js/src/methodjit/FrameState-inl.h
new file mode 100644
index 0000000..9dfeb82
--- /dev/null
+++ b/js/src/methodjit/FrameState-inl.h
@@ -0,0 +1,1164 @@
+/* -*- 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_inl_h__ && defined JS_METHODJIT
+#define jsjaeger_framestate_inl_h__
+
+namespace js {
+namespace mjit {
+
+inline void
+FrameState::addToTracker(FrameEntry *fe)
+{
+ JS_ASSERT(!fe->isTracked());
+ fe->track(tracker.nentries);
+ tracker.add(fe);
+ JS_ASSERT(tracker.nentries <= feLimit());
+}
+
+inline FrameEntry *
+FrameState::peek(int32 depth)
+{
+ JS_ASSERT(depth < 0);
+ JS_ASSERT(sp + depth >= spBase);
+ FrameEntry *fe = &sp[depth];
+ if (!fe->isTracked()) {
+ addToTracker(fe);
+ fe->resetSynced();
+ }
+ return fe;
+}
+
+inline void
+FrameState::popn(uint32 n)
+{
+ for (uint32 i = 0; i < n; i++)
+ pop();
+}
+
+inline bool
+FrameState::haveSameBacking(FrameEntry *lhs, FrameEntry *rhs)
+{
+ if (lhs->isCopy())
+ lhs = lhs->copyOf();
+ if (rhs->isCopy())
+ rhs = rhs->copyOf();
+ return lhs == rhs;
+}
+
+inline JSC::MacroAssembler::RegisterID
+FrameState::allocReg()
+{
+ RegisterID reg;
+ if (!freeRegs.empty()) {
+ reg = freeRegs.takeAnyReg();
+ } else {
+ reg = evictSomeReg();
+ regstate[reg].forget();
+ }
+
+ return reg;
+}
+
+inline JSC::MacroAssembler::RegisterID
+FrameState::allocReg(uint32 mask)
+{
+ RegisterID reg;
+ if (freeRegs.hasRegInMask(mask)) {
+ reg = freeRegs.takeRegInMask(mask);
+ } else {
+ reg = evictSomeReg(mask);
+ regstate[reg].forget();
+ }
+
+ return reg;
+}
+
+inline JSC::MacroAssembler::RegisterID
+FrameState::allocReg(FrameEntry *fe, RematInfo::RematType type)
+{
+ RegisterID reg;
+ if (!freeRegs.empty()) {
+ reg = freeRegs.takeAnyReg();
+ } else {
+ reg = evictSomeReg();
+ regstate[reg].forget();
+ }
+
+ regstate[reg].associate(fe, type);
+
+ return reg;
+}
+
+inline void
+FrameState::convertInt32ToDouble(Assembler &masm, FrameEntry *fe, FPRegisterID fpreg) const
+{
+ JS_ASSERT(!fe->isConstant());
+
+ if (fe->isCopy())
+ fe = fe->copyOf();
+
+ if (fe->data.inRegister())
+ masm.convertInt32ToDouble(fe->data.reg(), fpreg);
+ else
+ masm.convertInt32ToDouble(addressOf(fe), fpreg);
+}
+
+inline bool
+FrameState::peekTypeInRegister(FrameEntry *fe) const
+{
+ if (fe->isCopy())
+ fe = fe->copyOf();
+ return fe->type.inRegister();
+}
+
+inline void
+FrameState::pop()
+{
+ JS_ASSERT(sp > spBase);
+
+ FrameEntry *fe = --sp;
+ if (!fe->isTracked())
+ return;
+
+ forgetAllRegs(fe);
+}
+
+inline void
+FrameState::freeReg(RegisterID reg)
+{
+ JS_ASSERT(!regstate[reg].usedBy());
+
+ freeRegs.putReg(reg);
+}
+
+inline void
+FrameState::forgetReg(RegisterID reg)
+{
+ /*
+ * Important: Do not touch the fe here. We can peephole optimize away
+ * loads and stores by re-using the contents of old FEs.
+ */
+ JS_ASSERT_IF(regstate[reg].fe(), !regstate[reg].fe()->isCopy());
+
+ if (!regstate[reg].isPinned()) {
+ regstate[reg].forget();
+ freeRegs.putReg(reg);
+ }
+}
+
+inline void
+FrameState::syncAndForgetEverything(uint32 newStackDepth)
+{
+ syncAndForgetEverything();
+ sp = spBase + newStackDepth;
+}
+
+inline FrameEntry *
+FrameState::rawPush()
+{
+ JS_ASSERT(unsigned(sp - entries) < feLimit());
+
+ if (!sp->isTracked())
+ addToTracker(sp);
+
+ return sp++;
+}
+
+inline void
+FrameState::push(const Value &v)
+{
+ FrameEntry *fe = rawPush();
+ fe->setConstant(Jsvalify(v));
+}
+
+inline void
+FrameState::pushSynced()
+{
+ if (sp->isTracked())
+ sp->resetSynced();
+ sp++;
+}
+
+inline void
+FrameState::pushSyncedType(JSValueType type)
+{
+ FrameEntry *fe = rawPush();
+
+ fe->resetSynced();
+ fe->setType(type);
+}
+
+inline void
+FrameState::pushSynced(JSValueType type, RegisterID reg)
+{
+ FrameEntry *fe = rawPush();
+
+ fe->resetUnsynced();
+ fe->type.sync();
+ fe->data.sync();
+ fe->setType(type);
+ fe->data.setRegister(reg);
+ regstate[reg].associate(fe, RematInfo::DATA);
+}
+
+inline void
+FrameState::push(Address address)
+{
+#ifdef JS_PUNBOX64
+ // It's okay if either of these clobbers address.base, since we guarantee
+ // eviction will not physically clobber. It's also safe, on x64, for
+ // loadValueAsComponents() to take either type or data regs as address.base.
+ RegisterID typeReg = allocReg();
+ RegisterID dataReg = allocReg();
+ masm.loadValueAsComponents(address, typeReg, dataReg);
+#elif JS_NUNBOX32
+ // Prevent us from clobbering this reg.
+ bool free = freeRegs.hasReg(address.base);
+ if (free)
+ freeRegs.takeReg(address.base);
+
+ RegisterID typeReg = allocReg();
+
+ masm.loadTypeTag(address, typeReg);
+
+ // Allow re-use of the base register. This could avoid a spill, and
+ // is safe because the following allocReg() won't actually emit any
+ // writes to the register.
+ if (free)
+ freeRegs.putReg(address.base);
+
+ RegisterID dataReg = allocReg();
+ masm.loadPayload(address, dataReg);
+#endif
+
+ pushRegs(typeReg, dataReg);
+}
+
+inline void
+FrameState::pushRegs(RegisterID type, RegisterID data)
+{
+ JS_ASSERT(!freeRegs.hasReg(type) && !freeRegs.hasReg(data));
+
+ FrameEntry *fe = rawPush();
+
+ fe->resetUnsynced();
+ fe->type.setRegister(type);
+ fe->data.setRegister(data);
+ regstate[type].associate(fe, RematInfo::TYPE);
+ regstate[data].associate(fe, RematInfo::DATA);
+}
+
+inline void
+FrameState::pushTypedPayload(JSValueType type, RegisterID payload)
+{
+ JS_ASSERT(!freeRegs.hasReg(payload));
+
+ FrameEntry *fe = rawPush();
+
+ fe->resetUnsynced();
+ fe->setType(type);
+ fe->data.setRegister(payload);
+ regstate[payload].associate(fe, RematInfo::DATA);
+}
+
+inline void
+FrameState::pushNumber(MaybeRegisterID payload, bool asInt32)
+{
+ JS_ASSERT_IF(payload.isSet(), !freeRegs.hasReg(payload.reg()));
+
+ FrameEntry *fe = rawPush();
+ fe->clear();
+
+ JS_ASSERT(!fe->isNumber);
+
+ if (asInt32) {
+ if (!fe->type.synced())
+ masm.storeTypeTag(ImmType(JSVAL_TYPE_INT32), addressOf(fe));
+ fe->type.setMemory();
+ } else {
+ fe->type.setMemory();
+ }
+
+ fe->isNumber = true;
+ if (payload.isSet()) {
+ fe->data.unsync();
+ fe->data.setRegister(payload.reg());
+ regstate[payload.reg()].associate(fe, RematInfo::DATA);
+ } else {
+ fe->data.setMemory();
+ }
+}
+
+inline void
+FrameState::pushInt32(RegisterID payload)
+{
+ FrameEntry *fe = rawPush();
+ fe->clear();
+ JS_ASSERT(!fe->isNumber);
+
+ masm.storeTypeTag(ImmType(JSVAL_TYPE_INT32), addressOf(fe));
+ fe->type.setMemory();
+
+ fe->isNumber = true;
+ fe->data.unsync();
+ fe->data.setRegister(payload);
+ regstate[payload].associate(fe, RematInfo::DATA);
+}
+
+inline void
+FrameState::pushInitializerObject(RegisterID payload, bool array, JSObject *baseobj)
+{
+ pushTypedPayload(JSVAL_TYPE_OBJECT, payload);
+
+ FrameEntry *fe = peek(-1);
+ fe->initArray = array;
+ fe->initObject = baseobj;
+}
+
+inline void
+FrameState::pushUntypedPayload(JSValueType type, RegisterID payload)
+{
+ JS_ASSERT(!freeRegs.hasReg(payload));
+
+ FrameEntry *fe = rawPush();
+
+ fe->clear();
+
+ masm.storeTypeTag(ImmType(type), addressOf(fe));
+
+ /* The forceful type sync will assert otherwise. */
+#ifdef DEBUG
+ fe->type.unsync();
+#endif
+ fe->type.setMemory();
+ fe->data.unsync();
+ fe->setNotCopied();
+ fe->setCopyOf(NULL);
+ fe->data.setRegister(payload);
+ regstate[payload].associate(fe, RematInfo::DATA);
+}
+
+inline void
+FrameState::pushUntypedValue(const Value &v)
+{
+ FrameEntry *fe = rawPush();
+
+ fe->clear();
+
+ masm.storeValue(v, addressOf(fe));
+
+ /* The forceful type sync will assert otherwise. */
+#ifdef DEBUG
+ fe->type.unsync();
+#endif
+ fe->type.setMemory();
+ fe->data.unsync();
+ fe->data.setMemory();
+ fe->setNotCopied();
+ fe->setCopyOf(NULL);
+}
+
+inline JSC::MacroAssembler::RegisterID
+FrameState::tempRegForType(FrameEntry *fe, RegisterID fallback)
+{
+ JS_ASSERT(!regstate[fallback].fe());
+ if (fe->isCopy())
+ fe = fe->copyOf();
+
+ JS_ASSERT(!fe->type.isConstant());
+
+ if (fe->type.inRegister())
+ return fe->type.reg();
+
+ /* :XXX: X86 */
+
+ masm.loadTypeTag(addressOf(fe), fallback);
+ return fallback;
+}
+
+
+inline JSC::MacroAssembler::RegisterID
+FrameState::tempRegForType(FrameEntry *fe)
+{
+ if (fe->isCopy())
+ fe = fe->copyOf();
+
+ JS_ASSERT(!fe->type.isConstant());
+
+ if (fe->type.inRegister())
+ return fe->type.reg();
+
+ /* :XXX: X86 */
+
+ RegisterID reg = allocReg(fe, RematInfo::TYPE);
+ masm.loadTypeTag(addressOf(fe), reg);
+ fe->type.setRegister(reg);
+ return reg;
+}
+
+inline JSC::MacroAssembler::RegisterID
+FrameState::tempRegForData(FrameEntry *fe)
+{
+ JS_ASSERT(!fe->data.isConstant());
+
+ if (fe->isCopy())
+ fe = fe->copyOf();
+
+ if (fe->data.inRegister())
+ return fe->data.reg();
+
+ RegisterID reg = allocReg(fe, RematInfo::DATA);
+ masm.loadPayload(addressOf(fe), reg);
+ fe->data.setRegister(reg);
+ return reg;
+}
+
+inline JSC::MacroAssembler::RegisterID
+FrameState::tempRegInMaskForData(FrameEntry *fe, uint32 mask)
+{
+ JS_ASSERT(!fe->data.isConstant());
+
+ if (fe->isCopy())
+ fe = fe->copyOf();
+
+ RegisterID reg;
+ if (fe->data.inRegister()) {
+ RegisterID old = fe->data.reg();
+ if (Registers::maskReg(old) & mask)
+ return old;
+
+ /* Keep the old register pinned. */
+ regstate[old].forget();
+ reg = allocReg(mask);
+ masm.move(old, reg);
+ freeReg(old);
+ } else {
+ reg = allocReg(mask);
+ masm.loadPayload(addressOf(fe), reg);
+ }
+ regstate[reg].associate(fe, RematInfo::DATA);
+ fe->data.setRegister(reg);
+ return reg;
+}
+
+inline JSC::MacroAssembler::RegisterID
+FrameState::tempRegForData(FrameEntry *fe, RegisterID reg, Assembler &masm) const
+{
+ JS_ASSERT(!fe->data.isConstant());
+
+ if (fe->isCopy())
+ fe = fe->copyOf();
+
+ if (fe->data.inRegister()) {
+ JS_ASSERT(fe->data.reg() != reg);
+ return fe->data.reg();
+ } else {
+ masm.loadPayload(addressOf(fe), reg);
+ return reg;
+ }
+}
+
+inline bool
+FrameState::shouldAvoidTypeRemat(FrameEntry *fe)
+{
+ return fe->type.inMemory();
+}
+
+inline bool
+FrameState::shouldAvoidDataRemat(FrameEntry *fe)
+{
+ return fe->data.inMemory();
+}
+
+inline void
+FrameState::ensureFeSynced(const FrameEntry *fe, Assembler &masm) const
+{
+#if defined JS_PUNBOX64
+ Address to = addressOf(fe);
+ const FrameEntry *backing = fe;
+ if (fe->isCopy())
+ backing = fe->copyOf();
+
+ /* If we can, sync the type and data in one go. */
+ if (!fe->data.synced() && !fe->type.synced()) {
+ if (backing->isConstant())
+ masm.storeValue(backing->getValue(), to);
+ else if (backing->isTypeKnown())
+ masm.storeValueFromComponents(ImmType(backing->getKnownType()), backing->data.reg(), to);
+ else
+ masm.storeValueFromComponents(backing->type.reg(), backing->data.reg(), to);
+ return;
+ }
+#endif
+
+ /*
+ * On x86_64, only one of the following two calls will have output,
+ * and a load will only occur if necessary.
+ */
+ ensureDataSynced(fe, masm);
+ ensureTypeSynced(fe, masm);
+}
+
+inline void
+FrameState::ensureTypeSynced(const FrameEntry *fe, Assembler &masm) const
+{
+ if (fe->type.synced())
+ return;
+
+ Address to = addressOf(fe);
+ const FrameEntry *backing = fe;
+ if (fe->isCopy())
+ backing = fe->copyOf();
+
+#if defined JS_PUNBOX64
+ /* Attempt to store the entire Value, to prevent a load. */
+ if (backing->isConstant()) {
+ masm.storeValue(backing->getValue(), to);
+ return;
+ }
+
+ if (backing->data.inRegister()) {
+ RegisterID dreg = backing->data.reg();
+ if (backing->isTypeKnown())
+ masm.storeValueFromComponents(ImmType(backing->getKnownType()), dreg, to);
+ else
+ masm.storeValueFromComponents(backing->type.reg(), dreg, to);
+ return;
+ }
+#endif
+
+ /* Store a double's type bits, even though !isTypeKnown(). */
+ if (backing->isConstant())
+ masm.storeTypeTag(ImmTag(backing->getKnownTag()), to);
+ else if (backing->isTypeKnown())
+ masm.storeTypeTag(ImmType(backing->getKnownType()), to);
+ else
+ masm.storeTypeTag(backing->type.reg(), to);
+}
+
+inline void
+FrameState::ensureDataSynced(const FrameEntry *fe, Assembler &masm) const
+{
+ if (fe->data.synced())
+ return;
+
+ Address to = addressOf(fe);
+ const FrameEntry *backing = fe;
+ if (fe->isCopy())
+ backing = fe->copyOf();
+
+#if defined JS_PUNBOX64
+ if (backing->isConstant())
+ masm.storeValue(backing->getValue(), to);
+ else if (backing->isTypeKnown())
+ masm.storeValueFromComponents(ImmType(backing->getKnownType()), backing->data.reg(), to);
+ else if (backing->type.inRegister())
+ masm.storeValueFromComponents(backing->type.reg(), backing->data.reg(), to);
+ else
+ masm.storePayload(backing->data.reg(), to);
+#elif defined JS_NUNBOX32
+ if (backing->isConstant())
+ masm.storePayload(ImmPayload(backing->getPayload()), to);
+ else
+ masm.storePayload(backing->data.reg(), to);
+#endif
+}
+
+inline void
+FrameState::syncFe(FrameEntry *fe)
+{
+ FrameEntry *backing = fe;
+ if (fe->isCopy())
+ backing = fe->copyOf();
+
+ bool needTypeReg = !fe->type.synced() && backing->type.inMemory();
+ bool needDataReg = !fe->data.synced() && backing->data.inMemory();
+
+#if defined JS_NUNBOX32
+ /* Determine an ordering that won't spill known regs. */
+ if (needTypeReg && !needDataReg) {
+ syncData(fe);
+ syncType(fe);
+ } else {
+ syncType(fe);
+ syncData(fe);
+ }
+#elif defined JS_PUNBOX64
+ if (JS_UNLIKELY(needTypeReg && needDataReg)) {
+ /* Memory-to-memory moves can only occur for copies backed by memory. */
+ JS_ASSERT(backing != fe);
+
+ /* Use ValueReg to do a whole-Value mem-to-mem move. */
+ masm.loadValue(addressOf(backing), Registers::ValueReg);
+ masm.storeValue(Registers::ValueReg, addressOf(fe));
+ } else {
+ /* Store in case unpinning is necessary. */
+ MaybeRegisterID pairReg;
+
+ /* Get a register if necessary, without clobbering its pair. */
+ if (needTypeReg) {
+ if (backing->data.inRegister() && !regstate[backing->data.reg()].isPinned()) {
+ pairReg = backing->data.reg();
+ pinReg(backing->data.reg());
+ }
+ tempRegForType(backing);
+ } else if (needDataReg) {
+ if (backing->type.inRegister() && !regstate[backing->type.reg()].isPinned()) {
+ pairReg = backing->type.reg();
+ pinReg(backing->type.reg());
+ }
+ tempRegForData(backing);
+ }
+
+ ensureFeSynced(fe, masm);
+
+ if (pairReg.isSet())
+ unpinReg(pairReg.reg());
+ }
+
+ if (!fe->type.synced())
+ fe->type.sync();
+ if (!fe->data.synced())
+ fe->data.sync();
+#endif
+}
+
+inline void
+FrameState::syncType(FrameEntry *fe)
+{
+ FrameEntry *backing = fe;
+ if (fe->isCopy())
+ backing = fe->copyOf();
+
+ if (!fe->type.synced() && backing->type.inMemory())
+ tempRegForType(backing);
+
+ ensureTypeSynced(fe, masm);
+
+ if (!fe->type.synced())
+ fe->type.sync();
+}
+
+inline void
+FrameState::syncData(FrameEntry *fe)
+{
+ FrameEntry *backing = fe;
+ if (fe->isCopy())
+ backing = fe->copyOf();
+
+ if (!fe->data.synced() && backing->data.inMemory())
+ tempRegForData(backing);
+
+ ensureDataSynced(fe, masm);
+
+ if (!fe->data.synced())
+ fe->data.sync();
+}
+
+inline void
+FrameState::forgetType(FrameEntry *fe)
+{
+ /*
+ * The type may have been forgotten with an intervening storeLocal in the
+ * presence of eval or closed variables. For defense in depth and to make
+ * callers' lives simpler, bail out if the type is not known.
+ */
+ if (!fe->isTypeKnown())
+ return;
+
+ /*
+ * Likewise, storeLocal() may have set this FE, with a known type,
+ * to be a copy of another FE, which has an unknown type.
+ * Just forget the type, since the backing is used in all cases.
+ */
+ if (fe->isCopy()) {
+ fe->type.invalidate();
+ return;
+ }
+
+ ensureTypeSynced(fe, masm);
+ fe->type.setMemory();
+}
+
+inline void
+FrameState::learnType(FrameEntry *fe, JSValueType type)
+{
+ if (fe->type.inRegister())
+ forgetReg(fe->type.reg());
+#ifdef DEBUG
+ fe->isNumber = false;
+#endif
+ fe->setType(type);
+}
+
+inline JSC::MacroAssembler::Address
+FrameState::addressOf(const FrameEntry *fe) const
+{
+ int32 frameOffset = 0;
+ if (fe >= locals)
+ frameOffset = JSStackFrame::offsetOfFixed(uint32(fe - locals));
+ else if (fe >= args)
+ frameOffset = JSStackFrame::offsetOfFormalArg(fun, uint32(fe - args));
+ else if (fe == this_)
+ frameOffset = JSStackFrame::offsetOfThis(fun);
+ else if (fe == callee_)
+ frameOffset = JSStackFrame::offsetOfCallee(fun);
+ JS_ASSERT(frameOffset);
+ return Address(JSFrameReg, frameOffset);
+}
+
+inline JSC::MacroAssembler::Address
+FrameState::addressForDataRemat(const FrameEntry *fe) const
+{
+ if (fe->isCopy() && !fe->data.synced())
+ fe = fe->copyOf();
+ JS_ASSERT(fe->data.synced());
+ return addressOf(fe);
+}
+
+inline JSC::MacroAssembler::Jump
+FrameState::testNull(Assembler::Condition cond, FrameEntry *fe)
+{
+ JS_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+ if (shouldAvoidTypeRemat(fe))
+ return masm.testNull(cond, addressOf(fe));
+ return masm.testNull(cond, tempRegForType(fe));
+}
+
+inline JSC::MacroAssembler::Jump
+FrameState::testUndefined(Assembler::Condition cond, FrameEntry *fe)
+{
+ JS_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+ if (shouldAvoidTypeRemat(fe))
+ return masm.testUndefined(cond, addressOf(fe));
+ return masm.testUndefined(cond, tempRegForType(fe));
+}
+
+inline JSC::MacroAssembler::Jump
+FrameState::testInt32(Assembler::Condition cond, FrameEntry *fe)
+{
+ JS_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+ if (shouldAvoidTypeRemat(fe))
+ return masm.testInt32(cond, addressOf(fe));
+ return masm.testInt32(cond, tempRegForType(fe));
+}
+
+inline JSC::MacroAssembler::Jump
+FrameState::testPrimitive(Assembler::Condition cond, FrameEntry *fe)
+{
+ JS_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+ if (shouldAvoidTypeRemat(fe))
+ return masm.testPrimitive(cond, addressOf(fe));
+ return masm.testPrimitive(cond, tempRegForType(fe));
+}
+
+inline JSC::MacroAssembler::Jump
+FrameState::testObject(Assembler::Condition cond, FrameEntry *fe)
+{
+ JS_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+ if (shouldAvoidTypeRemat(fe))
+ return masm.testObject(cond, addressOf(fe));
+ return masm.testObject(cond, tempRegForType(fe));
+}
+
+inline JSC::MacroAssembler::Jump
+FrameState::testDouble(Assembler::Condition cond, FrameEntry *fe)
+{
+ JS_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+ if (shouldAvoidTypeRemat(fe))
+ return masm.testDouble(cond, addressOf(fe));
+ return masm.testDouble(cond, tempRegForType(fe));
+}
+
+inline JSC::MacroAssembler::Jump
+FrameState::testBoolean(Assembler::Condition cond, FrameEntry *fe)
+{
+ JS_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+ if (shouldAvoidTypeRemat(fe))
+ return masm.testBoolean(cond, addressOf(fe));
+ return masm.testBoolean(cond, tempRegForType(fe));
+}
+
+inline JSC::MacroAssembler::Jump
+FrameState::testString(Assembler::Condition cond, FrameEntry *fe)
+{
+ JS_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+ if (shouldAvoidTypeRemat(fe))
+ return masm.testString(cond, addressOf(fe));
+ return masm.testString(cond, tempRegForType(fe));
+}
+
+inline FrameEntry *
+FrameState::getOrTrack(uint32 index)
+{
+ FrameEntry *fe = &entries[index];
+ if (!fe->isTracked()) {
+ addToTracker(fe);
+ fe->resetSynced();
+ }
+ return fe;
+}
+
+inline FrameEntry *
+FrameState::getLocal(uint32 slot)
+{
+ JS_ASSERT(slot < script->nslots);
+ return getOrTrack(uint32(&locals[slot] - entries));
+}
+
+inline FrameEntry *
+FrameState::getArg(uint32 slot)
+{
+ JS_ASSERT(slot < nargs);
+ return getOrTrack(uint32(&args[slot] - entries));
+}
+
+inline FrameEntry *
+FrameState::getThis()
+{
+ return getOrTrack(uint32(this_ - entries));
+}
+
+inline FrameEntry *
+FrameState::getCallee()
+{
+ // Callee can only be used in function code, and it's always an object.
+ JS_ASSERT(fun);
+ if (!callee_->isTracked()) {
+ addToTracker(callee_);
+ callee_->resetSynced();
+ callee_->setType(JSVAL_TYPE_OBJECT);
+ }
+ return callee_;
+}
+
+inline void
+FrameState::pinReg(RegisterID reg)
+{
+ regstate[reg].pin();
+}
+
+inline void
+FrameState::unpinReg(RegisterID reg)
+{
+ regstate[reg].unpin();
+}
+
+inline void
+FrameState::unpinKilledReg(RegisterID reg)
+{
+ regstate[reg].unpinUnsafe();
+ freeRegs.putReg(reg);
+}
+
+inline void
+FrameState::forgetAllRegs(FrameEntry *fe)
+{
+ if (fe->type.inRegister())
+ forgetReg(fe->type.reg());
+ if (fe->data.inRegister())
+ forgetReg(fe->data.reg());
+}
+
+inline void
+FrameState::swapInTracker(FrameEntry *lhs, FrameEntry *rhs)
+{
+ uint32 li = lhs->trackerIndex();
+ uint32 ri = rhs->trackerIndex();
+ JS_ASSERT(tracker[li] == lhs);
+ JS_ASSERT(tracker[ri] == rhs);
+ tracker.entries[ri] = lhs;
+ tracker.entries[li] = rhs;
+ lhs->index_ = ri;
+ rhs->index_ = li;
+}
+
+inline void
+FrameState::dup()
+{
+ dupAt(-1);
+}
+
+inline void
+FrameState::dup2()
+{
+ FrameEntry *lhs = peek(-2);
+ FrameEntry *rhs = peek(-1);
+ pushCopyOf(indexOfFe(lhs));
+ pushCopyOf(indexOfFe(rhs));
+}
+
+inline void
+FrameState::dupAt(int32 n)
+{
+ JS_ASSERT(n < 0);
+ FrameEntry *fe = peek(n);
+ pushCopyOf(indexOfFe(fe));
+}
+
+inline void
+FrameState::pushLocal(uint32 n)
+{
+ FrameEntry *fe = getLocal(n);
+ if (!isClosedVar(n)) {
+ pushCopyOf(indexOfFe(fe));
+ } else {
+#ifdef DEBUG
+ /*
+ * We really want to assert on local variables, but in the presence of
+ * SETLOCAL equivocation of stack slots, and let expressions, just
+ * weakly assert on the fixed local vars.
+ */
+ FrameEntry *fe = &locals[n];
+ if (fe->isTracked() && n < script->nfixed) {
+ JS_ASSERT(fe->type.inMemory());
+ JS_ASSERT(fe->data.inMemory());
+ }
+#endif
+ push(addressOf(fe));
+ }
+}
+
+inline void
+FrameState::pushArg(uint32 n)
+{
+ FrameEntry *fe = getArg(n);
+ if (!isClosedArg(n)) {
+ pushCopyOf(indexOfFe(fe));
+ } else {
+#ifdef DEBUG
+ FrameEntry *fe = &args[n];
+ if (fe->isTracked()) {
+ JS_ASSERT(fe->type.inMemory());
+ JS_ASSERT(fe->data.inMemory());
+ }
+#endif
+ push(addressOf(fe));
+ }
+}
+
+inline void
+FrameState::pushCallee()
+{
+ FrameEntry *fe = getCallee();
+ pushCopyOf(indexOfFe(fe));
+}
+
+inline void
+FrameState::pushThis()
+{
+ FrameEntry *fe = getThis();
+ pushCopyOf(indexOfFe(fe));
+}
+
+void
+FrameState::learnThisIsObject()
+{
+ // This is safe, albeit hacky. This is only called from the compiler,
+ // and only on the first use of |this| inside a basic block. Thus,
+ // there are no copies of |this| anywhere.
+ learnType(this_, JSVAL_TYPE_OBJECT);
+}
+
+inline void
+FrameState::leaveBlock(uint32 n)
+{
+ popn(n);
+}
+
+inline void
+FrameState::enterBlock(uint32 n)
+{
+ /* expect that tracker has 0 entries, for now. */
+ JS_ASSERT(!tracker.nentries);
+ JS_ASSERT(uint32(sp + n - locals) <= script->nslots);
+
+ if (!eval)
+ memset(&closedVars[uint32(sp - locals)], 0, n * sizeof(*closedVars));
+ sp += n;
+}
+
+inline void
+FrameState::eviscerate(FrameEntry *fe)
+{
+ forgetAllRegs(fe);
+ fe->type.invalidate();
+ fe->data.invalidate();
+ fe->setNotCopied();
+ fe->setCopyOf(NULL);
+}
+
+inline void
+FrameState::setClosedVar(uint32 slot)
+{
+ if (!eval)
+ closedVars[slot] = true;
+}
+
+inline void
+FrameState::setClosedArg(uint32 slot)
+{
+ if (!eval && !usesArguments)
+ closedArgs[slot] = true;
+}
+
+inline StateRemat
+FrameState::dataRematInfo(const FrameEntry *fe) const
+{
+ if (fe->isCopy())
+ fe = fe->copyOf();
+
+ if (fe->data.inRegister())
+ return StateRemat::FromRegister(fe->data.reg());
+
+ JS_ASSERT(fe->data.synced());
+ return StateRemat::FromAddress(addressOf(fe));
+}
+
+inline void
+FrameState::giveOwnRegs(FrameEntry *fe)
+{
+ JS_ASSERT(!fe->isConstant());
+ JS_ASSERT(fe == peek(-1));
+
+ if (!fe->isCopy())
+ return;
+
+ RegisterID data = copyDataIntoReg(fe);
+ if (fe->isTypeKnown()) {
+ JSValueType type = fe->getKnownType();
+ pop();
+ pushTypedPayload(type, data);
+ } else {
+ RegisterID type = copyTypeIntoReg(fe);
+ pop();
+ pushRegs(type, data);
+ }
+}
+
+inline void
+FrameState::loadDouble(RegisterID t, RegisterID d, FrameEntry *fe, FPRegisterID fpReg,
+ Assembler &masm) const
+{
+#ifdef JS_CPU_X86
+ masm.fastLoadDouble(d, t, fpReg);
+#else
+ loadDouble(fe, fpReg, masm);
+#endif
+}
+
+inline bool
+FrameState::tryFastDoubleLoad(FrameEntry *fe, FPRegisterID fpReg, Assembler &masm) const
+{
+#ifdef JS_CPU_X86
+ if (fe->type.inRegister() && fe->data.inRegister()) {
+ masm.fastLoadDouble(fe->data.reg(), fe->type.reg(), fpReg);
+ return true;
+ }
+#endif
+ return false;
+}
+
+inline void
+FrameState::loadDouble(FrameEntry *fe, FPRegisterID fpReg, Assembler &masm) const
+{
+ if (fe->isCopy()) {
+ FrameEntry *backing = fe->copyOf();
+ if (tryFastDoubleLoad(fe, fpReg, masm))
+ return;
+ if (backing->isCachedNumber() || (backing->type.synced() && backing->data.synced())) {
+ masm.loadDouble(addressOf(backing), fpReg);
+ return;
+ }
+ fe = backing;
+ }
+
+ if (tryFastDoubleLoad(fe, fpReg, masm))
+ return;
+
+ if ((fe->type.synced() && fe->data.synced()) || fe->isCachedNumber()) {
+ masm.loadDouble(addressOf(fe), fpReg);
+ return;
+ }
+
+ ensureFeSynced(fe, masm);
+ masm.loadDouble(addressOf(fe), fpReg);
+}
+
+inline bool
+FrameState::isClosedVar(uint32 slot)
+{
+ return eval || closedVars[slot];
+}
+
+inline bool
+FrameState::isClosedArg(uint32 slot)
+{
+ return eval || usesArguments || closedArgs[slot];
+}
+
+class PinRegAcrossSyncAndKill
+{
+ typedef JSC::MacroAssembler::RegisterID RegisterID;
+ FrameState &frame;
+ MaybeRegisterID maybeReg;
+ public:
+ PinRegAcrossSyncAndKill(FrameState &frame, RegisterID reg)
+ : frame(frame), maybeReg(reg)
+ {
+ frame.pinReg(reg);
+ }
+ PinRegAcrossSyncAndKill(FrameState &frame, MaybeRegisterID maybeReg)
+ : frame(frame), maybeReg(maybeReg)
+ {
+ if (maybeReg.isSet())
+ frame.pinReg(maybeReg.reg());
+ }
+ ~PinRegAcrossSyncAndKill() {
+ if (maybeReg.isSet())
+ frame.unpinKilledReg(maybeReg.reg());
+ }
+};
+
+} /* namespace mjit */
+} /* namespace js */
+
+#endif /* include */
+