summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/jit/CallFrameShuffler.h
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2016-04-10 09:28:39 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2016-04-10 09:28:39 +0000
commit32761a6cee1d0dee366b885b7b9c777e67885688 (patch)
treed6bec92bebfb216f4126356e55518842c2f476a1 /Source/JavaScriptCore/jit/CallFrameShuffler.h
parenta4e969f4965059196ca948db781e52f7cfebf19e (diff)
downloadWebKitGtk-tarball-32761a6cee1d0dee366b885b7b9c777e67885688.tar.gz
webkitgtk-2.4.11webkitgtk-2.4.11
Diffstat (limited to 'Source/JavaScriptCore/jit/CallFrameShuffler.h')
-rw-r--r--Source/JavaScriptCore/jit/CallFrameShuffler.h804
1 files changed, 0 insertions, 804 deletions
diff --git a/Source/JavaScriptCore/jit/CallFrameShuffler.h b/Source/JavaScriptCore/jit/CallFrameShuffler.h
deleted file mode 100644
index d5e6f4253..000000000
--- a/Source/JavaScriptCore/jit/CallFrameShuffler.h
+++ /dev/null
@@ -1,804 +0,0 @@
-/*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef CallFrameShuffler_h
-#define CallFrameShuffler_h
-
-#if ENABLE(JIT)
-
-#include "CachedRecovery.h"
-#include "CallFrameShuffleData.h"
-#include "MacroAssembler.h"
-#include "RegisterSet.h"
-#include "StackAlignment.h"
-#include <wtf/Vector.h>
-
-namespace JSC {
-
-class CallFrameShuffler {
- WTF_MAKE_FAST_ALLOCATED;
-public:
- CallFrameShuffler(CCallHelpers&, const CallFrameShuffleData&);
-
- void dump(PrintStream&) const;
-
- // Any register that has been locked or acquired must be released
- // before calling prepareForTailCall() or prepareForSlowPath().
- void lockGPR(GPRReg gpr)
- {
- ASSERT(!m_lockedRegisters.get(gpr));
- m_lockedRegisters.set(gpr);
- if (verbose)
- dataLog(" * Locking ", gpr, "\n");
- }
-
- GPRReg acquireGPR()
- {
- ensureGPR();
- GPRReg gpr { getFreeGPR() };
- ASSERT(!m_registers[gpr]);
- lockGPR(gpr);
- return gpr;
- }
-
- void releaseGPR(GPRReg gpr)
- {
- if (verbose) {
- if (m_lockedRegisters.get(gpr))
- dataLog(" * Releasing ", gpr, "\n");
- else
- dataLog(" * ", gpr, " was not locked\n");
- }
- m_lockedRegisters.clear(gpr);
- }
-
- void restoreGPR(GPRReg gpr)
- {
- if (!m_newRegisters[gpr])
- return;
-
- ensureGPR();
-#if USE(JSVALUE32_64)
- GPRReg tempGPR { getFreeGPR() };
- lockGPR(tempGPR);
- ensureGPR();
- releaseGPR(tempGPR);
-#endif
- emitDisplace(*m_newRegisters[gpr]);
- }
-
- // You can only take a snapshot if the recovery has not started
- // yet. The only operations that are valid before taking a
- // snapshot are lockGPR(), acquireGPR() and releaseGPR().
- //
- // Locking status is *NOT* preserved by the snapshot: it only
- // contains information about where the
- // arguments/callee/callee-save registers are by taking into
- // account any spilling that acquireGPR() could have done.
- CallFrameShuffleData snapshot() const
- {
- ASSERT(isUndecided());
-
- CallFrameShuffleData data;
- data.numLocals = numLocals();
- data.callee = getNew(VirtualRegister { JSStack::Callee })->recovery();
- data.args.resize(argCount());
- for (size_t i = 0; i < argCount(); ++i)
- data.args[i] = getNew(virtualRegisterForArgument(i))->recovery();
- for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) {
- CachedRecovery* cachedRecovery { m_newRegisters[reg] };
- if (!cachedRecovery)
- continue;
-
-#if USE(JSVALUE64)
- data.registers[reg] = cachedRecovery->recovery();
-#else
- RELEASE_ASSERT_NOT_REACHED();
-#endif
- }
- return data;
- }
-
- // Ask the shuffler to put the callee into some registers once the
- // shuffling is done. You should call this before any of the
- // prepare() methods, and must not take a snapshot afterwards, as
- // this would crash 32bits platforms.
- void setCalleeJSValueRegs(JSValueRegs jsValueRegs)
- {
- ASSERT(isUndecided());
- ASSERT(!getNew(jsValueRegs));
- CachedRecovery* cachedRecovery { getNew(VirtualRegister(JSStack::Callee)) };
- ASSERT(cachedRecovery);
- addNew(jsValueRegs, cachedRecovery->recovery());
- }
-
- // Ask the suhffler to assume the callee has already be checked to
- // be a cell. This is a no-op on 64bit platforms, but allows to
- // free up a GPR on 32bit platforms.
- // You obviously must have ensured that this is the case before
- // running any of the prepare methods.
- void assumeCalleeIsCell()
- {
-#if USE(JSVALUE32_64)
- CachedRecovery& calleeCachedRecovery = *getNew(VirtualRegister(JSStack::Callee));
- switch (calleeCachedRecovery.recovery().technique()) {
- case InPair:
- updateRecovery(
- calleeCachedRecovery,
- ValueRecovery::inGPR(
- calleeCachedRecovery.recovery().payloadGPR(),
- DataFormatCell));
- break;
- case DisplacedInJSStack:
- updateRecovery(
- calleeCachedRecovery,
- ValueRecovery::displacedInJSStack(
- calleeCachedRecovery.recovery().virtualRegister(),
- DataFormatCell));
- break;
- case InFPR:
- case UnboxedCellInGPR:
- case CellDisplacedInJSStack:
- break;
- case Constant:
- ASSERT(calleeCachedRecovery.recovery().constant().isCell());
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
-#endif
- }
-
- // This will emit code to build the new frame over the old one.
- void prepareForTailCall();
-
- // This will emit code to build the new frame as if performing a
- // regular call. However, the callee save registers will be
- // restored, and any locals (not the header or arguments) of the
- // current frame can be overwritten.
- //
- // A frame built using prepareForSlowPath() should be used either
- // to throw an exception in, or destroyed using
- // CCallHelpers::prepareForTailCallSlow() followed by a tail call.
- void prepareForSlowPath();
-
-private:
- static const bool verbose = false;
-
- CCallHelpers& m_jit;
-
- void prepareAny();
-
- void spill(CachedRecovery&);
-
- // "box" is arguably a bad name here. The meaning is that after
- // calling emitBox(), your ensure that subsequently calling
- // emitStore() will be able to store the value without additional
- // transformation. In particular, this is a no-op for constants,
- // and is a complete no-op on 32bits since any unboxed value can
- // still be stored by storing the payload and a statically known
- // tag.
- void emitBox(CachedRecovery&);
-
- bool canBox(CachedRecovery& cachedRecovery)
- {
- if (cachedRecovery.boxingRequiresGPR() && getFreeGPR() == InvalidGPRReg)
- return false;
-
- if (cachedRecovery.boxingRequiresFPR() && getFreeFPR() == InvalidFPRReg)
- return false;
-
- return true;
- }
-
- void ensureBox(CachedRecovery& cachedRecovery)
- {
- if (canBox(cachedRecovery))
- return;
-
- if (cachedRecovery.boxingRequiresGPR())
- ensureGPR();
-
- if (cachedRecovery.boxingRequiresFPR())
- ensureFPR();
- }
-
- void emitLoad(CachedRecovery&);
-
- bool canLoad(CachedRecovery&);
-
- void ensureLoad(CachedRecovery& cachedRecovery)
- {
- if (canLoad(cachedRecovery))
- return;
-
- ASSERT(cachedRecovery.loadsIntoGPR() || cachedRecovery.loadsIntoFPR());
-
- if (cachedRecovery.loadsIntoFPR()) {
- if (cachedRecovery.loadsIntoGPR())
- ensureRegister();
- else
- ensureFPR();
- } else
- ensureGPR();
- }
-
- bool canLoadAndBox(CachedRecovery& cachedRecovery)
- {
- // We don't have interfering loads & boxes
- ASSERT(!cachedRecovery.loadsIntoFPR() || !cachedRecovery.boxingRequiresFPR());
- ASSERT(!cachedRecovery.loadsIntoGPR() || !cachedRecovery.boxingRequiresGPR());
-
- return canLoad(cachedRecovery) && canBox(cachedRecovery);
- }
-
- DataFormat emitStore(CachedRecovery&, MacroAssembler::Address);
-
- void emitDisplace(CachedRecovery&);
-
- void emitDeltaCheck();
-
- Bag<CachedRecovery> m_cachedRecoveries;
-
- void updateRecovery(CachedRecovery& cachedRecovery, ValueRecovery recovery)
- {
- clearCachedRecovery(cachedRecovery.recovery());
- cachedRecovery.setRecovery(recovery);
- setCachedRecovery(recovery, &cachedRecovery);
- }
-
- CachedRecovery* getCachedRecovery(ValueRecovery);
-
- CachedRecovery* setCachedRecovery(ValueRecovery, CachedRecovery*);
-
- void clearCachedRecovery(ValueRecovery recovery)
- {
- if (!recovery.isConstant())
- setCachedRecovery(recovery, nullptr);
- }
-
- CachedRecovery* addCachedRecovery(ValueRecovery recovery)
- {
- if (recovery.isConstant())
- return m_cachedRecoveries.add(recovery);
- CachedRecovery* cachedRecovery = getCachedRecovery(recovery);
- if (!cachedRecovery)
- return setCachedRecovery(recovery, m_cachedRecoveries.add(recovery));
- return cachedRecovery;
- }
-
- // This is the current recoveries present in the old frame's
- // slots. A null CachedRecovery means we can trash the current
- // value as we don't care about it.
- Vector<CachedRecovery*> m_oldFrame;
-
- int numLocals() const
- {
- return m_oldFrame.size() - JSStack::CallerFrameAndPCSize;
- }
-
- CachedRecovery* getOld(VirtualRegister reg) const
- {
- return m_oldFrame[JSStack::CallerFrameAndPCSize - reg.offset() - 1];
- }
-
- void setOld(VirtualRegister reg, CachedRecovery* cachedRecovery)
- {
- m_oldFrame[JSStack::CallerFrameAndPCSize - reg.offset() - 1] = cachedRecovery;
- }
-
- VirtualRegister firstOld() const
- {
- return VirtualRegister { static_cast<int>(-numLocals()) };
- }
-
- VirtualRegister lastOld() const
- {
- return VirtualRegister { JSStack::CallerFrameAndPCSize - 1 };
- }
-
- bool isValidOld(VirtualRegister reg) const
- {
- return reg >= firstOld() && reg <= lastOld();
- }
-
- bool m_didExtendFrame { false };
-
- void extendFrameIfNeeded();
-
- // This stores, for each slot in the new frame, information about
- // the recovery for the value that should eventually go into that
- // slot.
- //
- // Once the slot has been written, the corresponding entry in
- // m_newFrame will be empty.
- Vector<CachedRecovery*> m_newFrame;
-
- size_t argCount() const
- {
- return m_newFrame.size() - JSStack::CallFrameHeaderSize;
- }
-
- CachedRecovery* getNew(VirtualRegister newRegister) const
- {
- return m_newFrame[newRegister.offset()];
- }
-
- void setNew(VirtualRegister newRegister, CachedRecovery* cachedRecovery)
- {
- m_newFrame[newRegister.offset()] = cachedRecovery;
- }
-
- void addNew(VirtualRegister newRegister, ValueRecovery recovery)
- {
- CachedRecovery* cachedRecovery = addCachedRecovery(recovery);
- cachedRecovery->addTarget(newRegister);
- setNew(newRegister, cachedRecovery);
- }
-
- VirtualRegister firstNew() const
- {
- return VirtualRegister { 0 };
- }
-
- VirtualRegister lastNew() const
- {
- return VirtualRegister { static_cast<int>(m_newFrame.size()) - 1 };
- }
-
- bool isValidNew(VirtualRegister reg) const
- {
- return reg >= firstNew() && reg <= lastNew();
- }
-
-
- int m_alignedOldFrameSize;
- int m_alignedNewFrameSize;
-
- // This is the distance, in slots, between the base of the new
- // frame and the base of the old frame. It could be negative when
- // preparing for a tail call to a function with smaller argument
- // count.
- //
- // We will overwrite this appropriately for slow path calls, but
- // we initialize it as if doing a fast path for the spills we
- // could do while undecided (typically while calling acquireGPR()
- // for a polymorphic call).
- int m_frameDelta;
-
- VirtualRegister newAsOld(VirtualRegister reg) const
- {
- return reg - m_frameDelta;
- }
-
- // This stores the set of locked registers, i.e. registers for
- // which we have an implicit requirement that they are not changed.
- //
- // This will usually contains the link register on architectures
- // that have one, any scratch register used by the macro assembler
- // (e.g. r11 on X86_64), as well as any register that we use for
- // addressing (see m_oldFrameBase and m_newFrameBase).
- //
- // We also use this to lock registers temporarily, for instance to
- // ensure that we have at least 2 available registers for loading
- // a pair on 32bits.
- mutable RegisterSet m_lockedRegisters;
-
- // This stores the current recoveries present in registers. A null
- // CachedRecovery means we can trash the current value as we don't
- // care about it.
- RegisterMap<CachedRecovery*> m_registers;
-
-#if USE(JSVALUE64)
- mutable GPRReg m_tagTypeNumber;
-
- bool tryAcquireTagTypeNumber();
-#endif
-
- // This stores, for each register, information about the recovery
- // for the value that should eventually go into that register. The
- // only registers that have a target recovery will be callee-save
- // registers, as well as possibly one JSValueRegs for holding the
- // callee.
- //
- // Once the correct value has been put into the registers, and
- // contrary to what we do with m_newFrame, we keep the entry in
- // m_newRegisters to simplify spilling.
- RegisterMap<CachedRecovery*> m_newRegisters;
-
- template<typename CheckFunctor>
- Reg getFreeRegister(const CheckFunctor& check) const
- {
- Reg nonTemp { };
- for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) {
- if (m_lockedRegisters.get(reg))
- continue;
-
- if (!check(reg))
- continue;
-
- if (!m_registers[reg]) {
- if (!m_newRegisters[reg])
- return reg;
- if (!nonTemp)
- nonTemp = reg;
- }
- }
-
-#if USE(JSVALUE64)
- if (!nonTemp && m_tagTypeNumber != InvalidGPRReg && check(Reg { m_tagTypeNumber })) {
- ASSERT(m_lockedRegisters.get(m_tagTypeNumber));
- m_lockedRegisters.clear(m_tagTypeNumber);
- nonTemp = Reg { m_tagTypeNumber };
- m_tagTypeNumber = InvalidGPRReg;
- }
-#endif
- return nonTemp;
- }
-
- GPRReg getFreeTempGPR() const
- {
- Reg freeTempGPR { getFreeRegister([this] (Reg reg) { return reg.isGPR() && !m_newRegisters[reg]; }) };
- if (!freeTempGPR)
- return InvalidGPRReg;
- return freeTempGPR.gpr();
- }
-
- GPRReg getFreeGPR() const
- {
- Reg freeGPR { getFreeRegister([] (Reg reg) { return reg.isGPR(); }) };
- if (!freeGPR)
- return InvalidGPRReg;
- return freeGPR.gpr();
- }
-
- FPRReg getFreeFPR() const
- {
- Reg freeFPR { getFreeRegister([] (Reg reg) { return reg.isFPR(); }) };
- if (!freeFPR)
- return InvalidFPRReg;
- return freeFPR.fpr();
- }
-
- bool hasFreeRegister() const
- {
- return static_cast<bool>(getFreeRegister([] (Reg) { return true; }));
- }
-
- // This frees up a register satisfying the check functor (this
- // functor could theoretically have any kind of logic, but it must
- // ensure that it will only return true for registers - spill
- // assumes and asserts that it is passed a cachedRecovery stored in a
- // register).
- template<typename CheckFunctor>
- void ensureRegister(const CheckFunctor& check)
- {
- // If we can spill a callee-save, that's best, because it will
- // free up a register that would otherwise been taken for the
- // longest amount of time.
- //
- // We could try to bias towards those that are not in their
- // target registers yet, but the gain is probably super
- // small. Unless you have a huge number of argument (at least
- // around twice the number of available registers on your
- // architecture), no spilling is going to take place anyways.
- for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) {
- if (m_lockedRegisters.get(reg))
- continue;
-
- CachedRecovery* cachedRecovery { m_newRegisters[reg] };
- if (!cachedRecovery)
- continue;
-
- if (check(*cachedRecovery)) {
- if (verbose)
- dataLog(" ", cachedRecovery->recovery(), " looks like a good spill candidate\n");
- spill(*cachedRecovery);
- return;
- }
- }
-
- // We use the cachedRecovery associated with the first new slot we
- // can, because that is the one for which a write will be
- // possible the latest, i.e. that is the one that we would
- // have had to retain in registers for the longest.
- for (VirtualRegister reg = firstNew(); reg <= lastNew(); reg += 1) {
- CachedRecovery* cachedRecovery { getNew(reg) };
- if (!cachedRecovery)
- continue;
-
- if (check(*cachedRecovery)) {
- spill(*cachedRecovery);
- return;
- }
- }
-
- RELEASE_ASSERT_NOT_REACHED();
- }
-
- void ensureRegister()
- {
- if (hasFreeRegister())
- return;
-
- if (verbose)
- dataLog(" Finding a register to spill\n");
- ensureRegister(
- [this] (const CachedRecovery& cachedRecovery) {
- if (cachedRecovery.recovery().isInGPR())
- return !m_lockedRegisters.get(cachedRecovery.recovery().gpr());
- if (cachedRecovery.recovery().isInFPR())
- return !m_lockedRegisters.get(cachedRecovery.recovery().fpr());
-#if USE(JSVALUE32_64)
- if (cachedRecovery.recovery().technique() == InPair) {
- return !m_lockedRegisters.get(cachedRecovery.recovery().tagGPR())
- && !m_lockedRegisters.get(cachedRecovery.recovery().payloadGPR());
- }
-#endif
- return false;
- });
- }
-
- void ensureTempGPR()
- {
- if (getFreeTempGPR() != InvalidGPRReg)
- return;
-
- if (verbose)
- dataLog(" Finding a temp GPR to spill\n");
- ensureRegister(
- [this] (const CachedRecovery& cachedRecovery) {
- if (cachedRecovery.recovery().isInGPR()) {
- return !m_lockedRegisters.get(cachedRecovery.recovery().gpr())
- && !m_newRegisters[cachedRecovery.recovery().gpr()];
- }
-#if USE(JSVALUE32_64)
- if (cachedRecovery.recovery().technique() == InPair) {
- return !m_lockedRegisters.get(cachedRecovery.recovery().tagGPR())
- && !m_lockedRegisters.get(cachedRecovery.recovery().payloadGPR())
- && !m_newRegisters[cachedRecovery.recovery().tagGPR()]
- && !m_newRegisters[cachedRecovery.recovery().payloadGPR()];
- }
-#endif
- return false;
- });
- }
-
- void ensureGPR()
- {
- if (getFreeGPR() != InvalidGPRReg)
- return;
-
- if (verbose)
- dataLog(" Finding a GPR to spill\n");
- ensureRegister(
- [this] (const CachedRecovery& cachedRecovery) {
- if (cachedRecovery.recovery().isInGPR())
- return !m_lockedRegisters.get(cachedRecovery.recovery().gpr());
-#if USE(JSVALUE32_64)
- if (cachedRecovery.recovery().technique() == InPair) {
- return !m_lockedRegisters.get(cachedRecovery.recovery().tagGPR())
- && !m_lockedRegisters.get(cachedRecovery.recovery().payloadGPR());
- }
-#endif
- return false;
- });
- }
-
- void ensureFPR()
- {
- if (getFreeFPR() != InvalidFPRReg)
- return;
-
- if (verbose)
- dataLog(" Finding an FPR to spill\n");
- ensureRegister(
- [this] (const CachedRecovery& cachedRecovery) {
- if (cachedRecovery.recovery().isInFPR())
- return !m_lockedRegisters.get(cachedRecovery.recovery().fpr());
- return false;
- });
- }
-
- CachedRecovery* getNew(JSValueRegs jsValueRegs) const
- {
-#if USE(JSVALUE64)
- return m_newRegisters[jsValueRegs.gpr()];
-#else
- ASSERT(
- jsValueRegs.tagGPR() == InvalidGPRReg || jsValueRegs.payloadGPR() == InvalidGPRReg
- || m_newRegisters[jsValueRegs.payloadGPR()] == m_newRegisters[jsValueRegs.tagGPR()]);
- if (jsValueRegs.payloadGPR() == InvalidGPRReg)
- return m_newRegisters[jsValueRegs.tagGPR()];
- return m_newRegisters[jsValueRegs.payloadGPR()];
-#endif
- }
-
- void addNew(JSValueRegs jsValueRegs, ValueRecovery recovery)
- {
- ASSERT(jsValueRegs && !getNew(jsValueRegs));
- CachedRecovery* cachedRecovery = addCachedRecovery(recovery);
-#if USE(JSVALUE64)
- if (cachedRecovery->wantedJSValueRegs())
- m_newRegisters[cachedRecovery->wantedJSValueRegs().gpr()] = nullptr;
- m_newRegisters[jsValueRegs.gpr()] = cachedRecovery;
-#else
- if (JSValueRegs oldRegs { cachedRecovery->wantedJSValueRegs() }) {
- if (oldRegs.payloadGPR())
- m_newRegisters[oldRegs.payloadGPR()] = nullptr;
- if (oldRegs.tagGPR())
- m_newRegisters[oldRegs.tagGPR()] = nullptr;
- }
- if (jsValueRegs.payloadGPR() != InvalidGPRReg)
- m_newRegisters[jsValueRegs.payloadGPR()] = cachedRecovery;
- if (jsValueRegs.tagGPR() != InvalidGPRReg)
- m_newRegisters[jsValueRegs.tagGPR()] = cachedRecovery;
-#endif
- ASSERT(!cachedRecovery->wantedJSValueRegs());
- cachedRecovery->setWantedJSValueRegs(jsValueRegs);
- }
-
- void addNew(FPRReg fpr, ValueRecovery recovery)
- {
- ASSERT(fpr != InvalidFPRReg && !m_newRegisters[fpr]);
- CachedRecovery* cachedRecovery = addCachedRecovery(recovery);
- m_newRegisters[fpr] = cachedRecovery;
- ASSERT(cachedRecovery->wantedFPR() == InvalidFPRReg);
- cachedRecovery->setWantedFPR(fpr);
- }
-
- // m_oldFrameBase is the register relative to which we access
- // slots in the old call frame, with an additional offset of
- // m_oldFrameOffset.
- //
- // - For an actual tail call, m_oldFrameBase is the stack
- // pointer, and m_oldFrameOffset is the number of locals of the
- // tail caller's frame. We use such stack pointer-based
- // addressing because it allows us to load the tail caller's
- // caller's frame pointer in the frame pointer register
- // immediately instead of awkwardly keeping it around on the
- // stack.
- //
- // - For a slow path call, m_oldFrameBase is just the frame
- // pointer, and m_oldFrameOffset is 0.
- GPRReg m_oldFrameBase { MacroAssembler::framePointerRegister };
- int m_oldFrameOffset { 0 };
-
- MacroAssembler::Address addressForOld(VirtualRegister reg) const
- {
- return MacroAssembler::Address(m_oldFrameBase,
- (m_oldFrameOffset + reg.offset()) * sizeof(Register));
- }
-
- // m_newFrameBase is the register relative to which we access
- // slots in the new call frame, and we always make it point to
- // wherever the stack pointer will be right before making the
- // actual call/jump. The actual base of the new frame is at offset
- // m_newFrameOffset relative to m_newFrameBase.
- //
- // - For an actual tail call, m_newFrameBase is computed
- // dynamically, and m_newFrameOffset varies between 0 and -2
- // depending on the architecture's calling convention (see
- // prepareForTailCall).
- //
- // - For a slow path call, m_newFrameBase is the actual stack
- // pointer, and m_newFrameOffset is - CallerFrameAndPCSize,
- // following the convention for a regular call.
- GPRReg m_newFrameBase { InvalidGPRReg };
- int m_newFrameOffset { 0};
-
- bool isUndecided() const
- {
- return m_newFrameBase == InvalidGPRReg;
- }
-
- bool isSlowPath() const
- {
- return m_newFrameBase == MacroAssembler::stackPointerRegister;
- }
-
- MacroAssembler::Address addressForNew(VirtualRegister reg) const
- {
- return MacroAssembler::Address(m_newFrameBase,
- (m_newFrameOffset + reg.offset()) * sizeof(Register));
- }
-
- // We use a concept of "danger zone". The danger zone consists of
- // all the writes in the new frame that could overlap with reads
- // in the old frame.
- //
- // Because we could have a higher actual number of arguments than
- // parameters, when preparing a tail call, we need to assume that
- // writing to a slot on the new frame could overlap not only with
- // the corresponding slot in the old frame, but also with any slot
- // above it. Thus, the danger zone consists of all writes between
- // the first write and what I call the "danger frontier": the
- // highest slot in the old frame we still care about. Thus, the
- // danger zone contains all the slots between the first slot of
- // the new frame and the danger frontier. Because the danger
- // frontier is related to the new frame, it is stored as a virtual
- // register *in the new frame*.
- VirtualRegister m_dangerFrontier;
-
- VirtualRegister dangerFrontier() const
- {
- ASSERT(!isUndecided());
-
- return m_dangerFrontier;
- }
-
- bool isDangerNew(VirtualRegister reg) const
- {
- ASSERT(!isUndecided() && isValidNew(reg));
- return reg <= dangerFrontier();
- }
-
- void updateDangerFrontier()
- {
- ASSERT(!isUndecided());
-
- m_dangerFrontier = firstNew() - 1;
- for (VirtualRegister reg = lastNew(); reg >= firstNew(); reg -= 1) {
- if (!getNew(reg) || !isValidOld(newAsOld(reg)) || !getOld(newAsOld(reg)))
- continue;
-
- m_dangerFrontier = reg;
- if (verbose)
- dataLog(" Danger frontier now at NEW ", m_dangerFrontier, "\n");
- break;
- }
- if (verbose)
- dataLog(" All clear! Danger zone is empty.\n");
- }
-
- // A safe write is a write that never writes into the danger zone.
- bool hasOnlySafeWrites(CachedRecovery& cachedRecovery) const
- {
- for (VirtualRegister target : cachedRecovery.targets()) {
- if (isDangerNew(target))
- return false;
- }
- return true;
- }
-
- // You must ensure that there is no dangerous writes before
- // calling this function.
- bool tryWrites(CachedRecovery&);
-
- // This function tries to ensure that there is no longer any
- // possible safe write, i.e. all remaining writes are either to
- // the danger zone or callee save restorations.
- //
- // It returns false if it was unable to perform some safe writes
- // due to high register pressure.
- bool performSafeWrites();
-};
-
-} // namespace JSC
-
-#endif // ENABLE(JIT)
-
-#endif // CallFrameShuffler_h