diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
commit | 32761a6cee1d0dee366b885b7b9c777e67885688 (patch) | |
tree | d6bec92bebfb216f4126356e55518842c2f476a1 /Source/JavaScriptCore/jit/CallFrameShuffler.cpp | |
parent | a4e969f4965059196ca948db781e52f7cfebf19e (diff) | |
download | WebKitGtk-tarball-32761a6cee1d0dee366b885b7b9c777e67885688.tar.gz |
webkitgtk-2.4.11webkitgtk-2.4.11
Diffstat (limited to 'Source/JavaScriptCore/jit/CallFrameShuffler.cpp')
-rw-r--r-- | Source/JavaScriptCore/jit/CallFrameShuffler.cpp | 774 |
1 files changed, 0 insertions, 774 deletions
diff --git a/Source/JavaScriptCore/jit/CallFrameShuffler.cpp b/Source/JavaScriptCore/jit/CallFrameShuffler.cpp deleted file mode 100644 index 45af55dd6..000000000 --- a/Source/JavaScriptCore/jit/CallFrameShuffler.cpp +++ /dev/null @@ -1,774 +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. - */ - -#include "config.h" -#include "CallFrameShuffler.h" - -#if ENABLE(JIT) - -#include "CachedRecovery.h" -#include "CCallHelpers.h" -#include "CodeBlock.h" - -namespace JSC { - -CallFrameShuffler::CallFrameShuffler(CCallHelpers& jit, const CallFrameShuffleData& data) - : m_jit(jit) - , m_oldFrame(data.numLocals + JSStack::CallerFrameAndPCSize, nullptr) - , m_newFrame(data.args.size() + JSStack::CallFrameHeaderSize, nullptr) - , m_alignedOldFrameSize(JSStack::CallFrameHeaderSize - + roundArgumentCountToAlignFrame(jit.codeBlock()->numParameters())) - , m_alignedNewFrameSize(JSStack::CallFrameHeaderSize - + roundArgumentCountToAlignFrame(data.args.size())) - , m_frameDelta(m_alignedNewFrameSize - m_alignedOldFrameSize) - , m_lockedRegisters(RegisterSet::allRegisters()) -{ - // We are allowed all the usual registers... - for (unsigned i = GPRInfo::numberOfRegisters; i--; ) - m_lockedRegisters.clear(GPRInfo::toRegister(i)); - for (unsigned i = FPRInfo::numberOfRegisters; i--; ) - m_lockedRegisters.clear(FPRInfo::toRegister(i)); - // ... as well as the runtime registers. - m_lockedRegisters.exclude(RegisterSet::vmCalleeSaveRegisters()); - - ASSERT(!data.callee.isInJSStack() || data.callee.virtualRegister().isLocal()); - addNew(VirtualRegister(JSStack::Callee), data.callee); - - for (size_t i = 0; i < data.args.size(); ++i) { - ASSERT(!data.args[i].isInJSStack() || data.args[i].virtualRegister().isLocal()); - addNew(virtualRegisterForArgument(i), data.args[i]); - } - -#if USE(JSVALUE64) - for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) { - if (!data.registers[reg].isSet()) - continue; - - if (reg.isGPR()) - addNew(JSValueRegs(reg.gpr()), data.registers[reg]); - else - addNew(reg.fpr(), data.registers[reg]); - } - - m_tagTypeNumber = data.tagTypeNumber; - if (m_tagTypeNumber != InvalidGPRReg) - lockGPR(m_tagTypeNumber); -#endif -} - -void CallFrameShuffler::dump(PrintStream& out) const -{ - static const char* delimiter = " +-------------------------------+ "; - static const char* dangerDelimiter = " X-------------------------------X "; - static const char* dangerBoundsDelimiter = " XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX "; - static const char* emptySpace = " "; - out.print(" "); - out.print(" Old frame "); - out.print(" New frame "); - out.print("\n"); - int totalSize = m_alignedOldFrameSize + std::max(numLocals(), m_alignedNewFrameSize) + 3; - for (int i = 0; i < totalSize; ++i) { - VirtualRegister old { m_alignedOldFrameSize - i - 1 }; - VirtualRegister newReg { old + m_frameDelta }; - - if (!isValidOld(old) && old != firstOld() - 1 - && !isValidNew(newReg) && newReg != firstNew() - 1) - continue; - - out.print(" "); - if (dangerFrontier() >= firstNew() - && (newReg == dangerFrontier() || newReg == firstNew() - 1)) - out.print(dangerBoundsDelimiter); - else if (isValidOld(old)) - out.print(isValidNew(newReg) && isDangerNew(newReg) ? dangerDelimiter : delimiter); - else if (old == firstOld() - 1) - out.print(delimiter); - else - out.print(emptySpace); - if (dangerFrontier() >= firstNew() - && (newReg == dangerFrontier() || newReg == firstNew() - 1)) - out.print(dangerBoundsDelimiter); - else if (isValidNew(newReg) || newReg == firstNew() - 1) - out.print(isDangerNew(newReg) ? dangerDelimiter : delimiter); - else - out.print(emptySpace); - out.print("\n"); - if (old == firstOld()) - out.print(" sp --> "); - else if (!old.offset()) - out.print(" fp --> "); - else - out.print(" "); - if (isValidOld(old)) { - if (getOld(old)) { - auto str = toCString(old); - if (isValidNew(newReg) && isDangerNew(newReg)) - out.printf(" X %18s X ", str.data()); - else - out.printf(" | %18s | ", str.data()); - } else if (isValidNew(newReg) && isDangerNew(newReg)) - out.printf(" X%30s X ", ""); - else - out.printf(" |%30s | ", ""); - } else - out.print(emptySpace); - if (isValidNew(newReg)) { - const char d = isDangerNew(newReg) ? 'X' : '|'; - auto str = toCString(newReg); - if (getNew(newReg)) { - if (getNew(newReg)->recovery().isConstant()) - out.printf(" %c%8s <- constant %c ", d, str.data(), d); - else { - auto recoveryStr = toCString(getNew(newReg)->recovery()); - out.printf(" %c%8s <- %18s %c ", d, str.data(), - recoveryStr.data(), d); - } - } else if (newReg == VirtualRegister { JSStack::ArgumentCount }) - out.printf(" %c%8s <- %18zu %c ", d, str.data(), argCount(), d); - else - out.printf(" %c%30s %c ", d, "", d); - } else - out.print(emptySpace); - if (newReg == firstNew() - m_newFrameOffset && !isSlowPath()) - out.print(" <-- new sp before jump (current ", m_newFrameBase, ") "); - if (newReg == firstNew()) - out.print(" <-- new fp after prologue"); - out.print("\n"); - } - out.print(" "); - out.print(" Live registers "); - out.print(" Wanted registers "); - out.print("\n"); - for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) { - CachedRecovery* oldCachedRecovery { m_registers[reg] }; - CachedRecovery* newCachedRecovery { m_newRegisters[reg] }; - if (!oldCachedRecovery && !newCachedRecovery) - continue; - out.print(" "); - if (oldCachedRecovery) { - auto str = toCString(reg); - out.printf(" %8s ", str.data()); - } else - out.print(emptySpace); -#if USE(JSVALUE32_64) - if (newCachedRecovery) { - JSValueRegs wantedJSValueRegs { newCachedRecovery->wantedJSValueRegs() }; - if (reg.isFPR()) - out.print(reg, " <- ", newCachedRecovery->recovery()); - else { - if (reg.gpr() == wantedJSValueRegs.tagGPR()) - out.print(reg.gpr(), " <- tag(", newCachedRecovery->recovery(), ")"); - else - out.print(reg.gpr(), " <- payload(", newCachedRecovery->recovery(), ")"); - } - } -#else - if (newCachedRecovery) - out.print(" ", reg, " <- ", newCachedRecovery->recovery()); -#endif - out.print("\n"); - } - out.print(" Locked registers: "); - bool firstLocked { true }; - for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) { - if (m_lockedRegisters.get(reg)) { - out.print(firstLocked ? "" : ", ", reg); - firstLocked = false; - } - } - out.print("\n"); - - if (isSlowPath()) - out.print(" Using fp-relative addressing for slow path call\n"); - else - out.print(" Using sp-relative addressing for jump (using ", m_newFrameBase, " as new sp)\n"); - if (m_oldFrameOffset) - out.print(" Old frame offset is ", m_oldFrameOffset, "\n"); - if (m_newFrameOffset) - out.print(" New frame offset is ", m_newFrameOffset, "\n"); -#if USE(JSVALUE64) - if (m_tagTypeNumber != InvalidGPRReg) - out.print(" TagTypeNumber is currently in ", m_tagTypeNumber, "\n"); -#endif -} - -CachedRecovery* CallFrameShuffler::getCachedRecovery(ValueRecovery recovery) -{ - ASSERT(!recovery.isConstant()); - if (recovery.isInGPR()) - return m_registers[recovery.gpr()]; - if (recovery.isInFPR()) - return m_registers[recovery.fpr()]; -#if USE(JSVALUE32_64) - if (recovery.technique() == InPair) { - ASSERT(m_registers[recovery.tagGPR()] == m_registers[recovery.payloadGPR()]); - return m_registers[recovery.payloadGPR()]; - } -#endif - ASSERT(recovery.isInJSStack()); - return getOld(recovery.virtualRegister()); -} - -CachedRecovery* CallFrameShuffler::setCachedRecovery(ValueRecovery recovery, CachedRecovery* cachedRecovery) -{ - ASSERT(!recovery.isConstant()); - if (recovery.isInGPR()) - return m_registers[recovery.gpr()] = cachedRecovery; - if (recovery.isInFPR()) - return m_registers[recovery.fpr()] = cachedRecovery; -#if USE(JSVALUE32_64) - if (recovery.technique() == InPair) { - m_registers[recovery.tagGPR()] = cachedRecovery; - return m_registers[recovery.payloadGPR()] = cachedRecovery; - } -#endif - ASSERT(recovery.isInJSStack()); - setOld(recovery.virtualRegister(), cachedRecovery); - return cachedRecovery; -} - -void CallFrameShuffler::spill(CachedRecovery& cachedRecovery) -{ - ASSERT(!isSlowPath()); - ASSERT(cachedRecovery.recovery().isInRegisters()); - - VirtualRegister spillSlot { 0 }; - for (VirtualRegister slot = firstOld(); slot <= lastOld(); slot += 1) { - if (slot >= newAsOld(firstNew())) - break; - - if (getOld(slot)) - continue; - - spillSlot = slot; - break; - } - // We must have enough slots to be able to fit the whole callee's - // frame for the slow path - unless we are in the FTL. In that - // case, we are allowed to extend the frame *once*, since we are - // guaranteed to have enough available space for that. - if (spillSlot >= newAsOld(firstNew()) || !spillSlot.isLocal()) { - RELEASE_ASSERT(!m_didExtendFrame); - extendFrameIfNeeded(); - spill(cachedRecovery); - return; - } - - if (verbose) - dataLog(" * Spilling ", cachedRecovery.recovery(), " into ", spillSlot, "\n"); - auto format = emitStore(cachedRecovery, addressForOld(spillSlot)); - ASSERT(format != DataFormatNone); - updateRecovery(cachedRecovery, ValueRecovery::displacedInJSStack(spillSlot, format)); -} - -void CallFrameShuffler::emitDeltaCheck() -{ - if (ASSERT_DISABLED) - return; - - GPRReg scratchGPR { getFreeGPR() }; - if (scratchGPR != InvalidGPRReg) { - if (verbose) - dataLog(" Using ", scratchGPR, " for the fp-sp delta check\n"); - m_jit.move(MacroAssembler::stackPointerRegister, scratchGPR); - m_jit.subPtr(GPRInfo::callFrameRegister, scratchGPR); - MacroAssembler::Jump ok = m_jit.branch32( - MacroAssembler::Equal, scratchGPR, - MacroAssembler::TrustedImm32(-numLocals() * sizeof(Register))); - m_jit.abortWithReason(JITUnexpectedCallFrameSize); - ok.link(&m_jit); - } else if (verbose) - dataLog(" Skipping the fp-sp delta check since there is too much pressure"); -} - -void CallFrameShuffler::extendFrameIfNeeded() -{ - ASSERT(!m_didExtendFrame); - - VirtualRegister firstRead { firstOld() }; - for (; firstRead <= virtualRegisterForLocal(0); firstRead += 1) { - if (getOld(firstRead)) - break; - } - size_t availableSize = static_cast<size_t>(firstRead.offset() - firstOld().offset()); - size_t wantedSize = m_newFrame.size() + m_newFrameOffset; - - if (availableSize < wantedSize) { - size_t delta = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), wantedSize - availableSize); - m_oldFrame.grow(m_oldFrame.size() + delta); - for (size_t i = 0; i < delta; ++i) - m_oldFrame[m_oldFrame.size() - i - 1] = nullptr; - m_jit.subPtr(MacroAssembler::TrustedImm32(delta * sizeof(Register)), MacroAssembler::stackPointerRegister); - - if (isSlowPath()) - m_frameDelta = numLocals() + JSStack::CallerFrameAndPCSize; - else - m_oldFrameOffset = numLocals(); - - if (verbose) - dataLogF(" Not enough space - extending the old frame %zu slot\n", delta); - } - - m_didExtendFrame = true; -} - -void CallFrameShuffler::prepareForSlowPath() -{ - ASSERT(isUndecided()); - emitDeltaCheck(); - - m_frameDelta = numLocals() + JSStack::CallerFrameAndPCSize; - m_newFrameBase = MacroAssembler::stackPointerRegister; - m_newFrameOffset = -JSStack::CallerFrameAndPCSize; - - if (verbose) - dataLog("\n\nPreparing frame for slow path call:\n"); - - // When coming from the FTL, we need to extend the frame. In other - // cases, we may end up extending the frame if we previously - // spilled things (e.g. in polymorphic cache). - extendFrameIfNeeded(); - - if (verbose) - dataLog(*this); - - prepareAny(); - - if (verbose) - dataLog("Ready for slow path call!\n"); -} - -void CallFrameShuffler::prepareForTailCall() -{ - ASSERT(isUndecided()); - emitDeltaCheck(); - - // We'll use sp-based indexing so that we can load the - // caller's frame pointer into the fpr immediately - m_oldFrameBase = MacroAssembler::stackPointerRegister; - m_oldFrameOffset = numLocals(); - m_newFrameBase = acquireGPR(); -#if CPU(X86) - // We load the frame pointer manually, but we need to ask the - // algorithm to move the return PC for us (it'd probably - // require a write to the danger zone). Since it'd be awkward - // to ask for half a value move, we ask that the whole thing - // be moved for us. - addNew(VirtualRegister { 0 }, - ValueRecovery::displacedInJSStack(VirtualRegister(0), DataFormatJS)); - - // sp will point to head0 and we will move it up half a slot - // manually - m_newFrameOffset = 0; -#elif CPU(ARM) || CPU(SH4) || CPU(MIPS) - // We load the the frame pointer and link register - // manually. We could ask the algorithm to load them for us, - // and it would allow us to use the link register as an extra - // temporary - but it'd mean that the frame pointer can also - // be used as an extra temporary, so we keep the link register - // locked instead. - - // sp will point to head1 since the callee's prologue pushes - // the call frame and link register. - m_newFrameOffset = -1; -#elif CPU(ARM64) - // We load the frame pointer and link register manually. We - // could ask the algorithm to load the link register for us - // (which would allow for its use as an extra temporary), but - // since its not in GPRInfo, we can't do it. - - // sp will point to head2 since the callee's prologue pushes the - // call frame and link register - m_newFrameOffset = -2; -#elif CPU(X86_64) - // We load the frame pointer manually, but we ask the - // algorithm to move the return PC for us (it'd probably - // require a write in the danger zone) - addNew(VirtualRegister { 1 }, - ValueRecovery::displacedInJSStack(VirtualRegister(1), DataFormatJS)); - - // sp will point to head1 since the callee's prologue pushes - // the call frame register - m_newFrameOffset = -1; -#else - UNREACHABLE_FOR_PLATFORM(); -#endif - - if (verbose) - dataLog(" Emitting code for computing the new frame base\n"); - - // We compute the new frame base by first computing the top of the - // old frame (taking into account an argument count higher than - // the number of parameters), then substracting to it the aligned - // new frame size (adjusted). - m_jit.load32(MacroAssembler::Address(GPRInfo::callFrameRegister, JSStack::ArgumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset), m_newFrameBase); - MacroAssembler::Jump argumentCountOK = - m_jit.branch32(MacroAssembler::BelowOrEqual, m_newFrameBase, - MacroAssembler::TrustedImm32(m_jit.codeBlock()->numParameters())); - m_jit.add32(MacroAssembler::TrustedImm32(stackAlignmentRegisters() - 1 + JSStack::CallFrameHeaderSize), m_newFrameBase); - m_jit.and32(MacroAssembler::TrustedImm32(-stackAlignmentRegisters()), m_newFrameBase); - m_jit.mul32(MacroAssembler::TrustedImm32(sizeof(Register)), m_newFrameBase, m_newFrameBase); - MacroAssembler::Jump done = m_jit.jump(); - argumentCountOK.link(&m_jit); - m_jit.move( - MacroAssembler::TrustedImm32(m_alignedOldFrameSize * sizeof(Register)), - m_newFrameBase); - done.link(&m_jit); - - m_jit.addPtr(GPRInfo::callFrameRegister, m_newFrameBase); - m_jit.subPtr( - MacroAssembler::TrustedImm32( - (m_alignedNewFrameSize + m_newFrameOffset) * sizeof(Register)), - m_newFrameBase); - - // We load the link register manually for architectures that have one -#if CPU(ARM) || CPU(SH4) || CPU(ARM64) - m_jit.loadPtr(MacroAssembler::Address(MacroAssembler::framePointerRegister, sizeof(void*)), - MacroAssembler::linkRegister); -#elif CPU(MIPS) - m_jit.loadPtr(MacroAssembler::Address(MacroAssembler::framePointerRegister, sizeof(void*)), - MacroAssembler::returnAddressRegister); -#endif - - // We want the frame pointer to always point to a valid frame, and - // we are going to trash the current one. Let's make it point to - // our caller's frame, since that's what we want to end up with. - m_jit.loadPtr(MacroAssembler::Address(MacroAssembler::framePointerRegister), - MacroAssembler::framePointerRegister); - - if (verbose) - dataLog("Preparing frame for tail call:\n", *this); - - prepareAny(); - -#if CPU(X86) - if (verbose) - dataLog(" Simulating pop of the call frame register\n"); - m_jit.addPtr(MacroAssembler::TrustedImm32(sizeof(void*)), MacroAssembler::stackPointerRegister); -#endif - - if (verbose) - dataLog("Ready for tail call!\n"); -} - -bool CallFrameShuffler::tryWrites(CachedRecovery& cachedRecovery) -{ - ASSERT(m_newFrameBase != InvalidGPRReg); - - // If the value is already set up correctly, we don't have - // anything to do. - if (isSlowPath() && cachedRecovery.recovery().isInJSStack() - && cachedRecovery.targets().size() == 1 - && newAsOld(cachedRecovery.targets()[0]) == cachedRecovery.recovery().virtualRegister()) { - cachedRecovery.clearTargets(); - if (!cachedRecovery.wantedJSValueRegs() && cachedRecovery.wantedFPR() == InvalidFPRReg) - clearCachedRecovery(cachedRecovery.recovery()); - return true; - } - - if (!canLoadAndBox(cachedRecovery)) - return false; - - emitLoad(cachedRecovery); - emitBox(cachedRecovery); - ASSERT(cachedRecovery.recovery().isInRegisters() - || cachedRecovery.recovery().isConstant()); - - if (verbose) - dataLog(" * Storing ", cachedRecovery.recovery()); - for (size_t i = 0; i < cachedRecovery.targets().size(); ++i) { - VirtualRegister target { cachedRecovery.targets()[i] }; - ASSERT(!isDangerNew(target)); - if (verbose) - dataLog(!i ? " into " : ", and ", "NEW ", target); - emitStore(cachedRecovery, addressForNew(target)); - setNew(target, nullptr); - } - if (verbose) - dataLog("\n"); - cachedRecovery.clearTargets(); - if (!cachedRecovery.wantedJSValueRegs() && cachedRecovery.wantedFPR() == InvalidFPRReg) - clearCachedRecovery(cachedRecovery.recovery()); - - return true; -} - -bool CallFrameShuffler::performSafeWrites() -{ - VirtualRegister firstSafe; - VirtualRegister end { lastNew() + 1 }; - Vector<VirtualRegister> failures; - - // For all cachedRecoveries that writes to the safe zone, if it - // doesn't also write to the danger zone, we try to perform - // the writes. This may free up danger slots, so we iterate - // again until it doesn't happen anymore. - // - // Note that even though we have a while block, we look at - // each slot of the new call frame at most once since in each - // iteration beyond the first, we only load up the portion of - // the new call frame that was dangerous and became safe due - // to the previous iteration. - do { - firstSafe = dangerFrontier() + 1; - if (verbose) - dataLog(" Trying safe writes (between NEW ", firstSafe, " and NEW ", end - 1, ")\n"); - bool didProgress = false; - for (VirtualRegister reg = firstSafe; reg < end; reg += 1) { - CachedRecovery* cachedRecovery = getNew(reg); - if (!cachedRecovery) { - if (verbose) - dataLog(" + ", reg, " is OK.\n"); - continue; - } - if (!hasOnlySafeWrites(*cachedRecovery)) { - if (verbose) { - dataLog(" - ", cachedRecovery->recovery(), " writes to NEW ", reg, - " but also has dangerous writes.\n"); - } - continue; - } - if (cachedRecovery->wantedJSValueRegs()) { - if (verbose) { - dataLog(" - ", cachedRecovery->recovery(), " writes to NEW ", reg, - " but is also needed in registers.\n"); - } - continue; - } - if (cachedRecovery->wantedFPR() != InvalidFPRReg) { - if (verbose) { - dataLog(" - ", cachedRecovery->recovery(), " writes to NEW ", reg, - " but is also needed in an FPR.\n"); - } - continue; - } - if (!tryWrites(*cachedRecovery)) { - if (verbose) - dataLog(" - Unable to write to NEW ", reg, " from ", cachedRecovery->recovery(), "\n"); - failures.append(reg); - } - didProgress = true; - } - end = firstSafe; - - // If we have cachedRecoveries that failed to write, it is - // because they are on the stack and we didn't have enough - // registers available at the time to load them into. If - // we have a free register, we should try again because it - // could free up some danger slots. - if (didProgress && hasFreeRegister()) { - Vector<VirtualRegister> stillFailing; - for (VirtualRegister failed : failures) { - CachedRecovery* cachedRecovery = getNew(failed); - // It could have been handled later if it had - // several targets - if (!cachedRecovery) - continue; - - ASSERT(hasOnlySafeWrites(*cachedRecovery) - && !cachedRecovery->wantedJSValueRegs() - && cachedRecovery->wantedFPR() == InvalidFPRReg); - if (!tryWrites(*cachedRecovery)) - stillFailing.append(failed); - } - failures = WTFMove(stillFailing); - } - if (verbose && firstSafe != dangerFrontier() + 1) - dataLog(" We freed up danger slots!\n"); - } while (firstSafe != dangerFrontier() + 1); - - return failures.isEmpty(); -} - -void CallFrameShuffler::prepareAny() -{ - ASSERT(!isUndecided()); - - updateDangerFrontier(); - - // First, we try to store any value that goes above the danger - // frontier. This will never use more registers since we are only - // loading+storing if we ensure that any register used for the load - // will be freed up after the stores (i.e., all stores are above - // the danger frontier, and there is no wanted register). - performSafeWrites(); - - // At this point, we couldn't have more available registers than - // we have withouth spilling: all values currently in registers - // either require a write to the danger zone, or have a wanted - // register, which means that in any case they will have to go - // through registers again. - - // We now slowly free up the danger zone by first loading the old - // value on the danger frontier, spilling as many registers as - // needed to do so and ensuring that the corresponding slot in the - // new frame is now ready to be written. Then, we store the old - // value to its target location if possible (we could have failed - // to load it previously due to high pressure). Finally, we write - // to any of the newly safe slots that we can, which could free up - // registers (hence why we do it eagerly). - for (VirtualRegister reg = dangerFrontier(); reg >= firstNew(); reg -= 1) { - if (reg == dangerFrontier()) { - if (verbose) - dataLog(" Next slot (NEW ", reg, ") is the danger frontier\n"); - CachedRecovery* cachedRecovery { getOld(newAsOld(dangerFrontier())) }; - ASSERT(cachedRecovery); - ensureLoad(*cachedRecovery); - emitLoad(*cachedRecovery); - ensureBox(*cachedRecovery); - emitBox(*cachedRecovery); - if (hasOnlySafeWrites(*cachedRecovery)) - tryWrites(*cachedRecovery); - } else if (verbose) - dataLog(" Next slot is NEW ", reg, "\n"); - - ASSERT(!isDangerNew(reg)); - CachedRecovery* cachedRecovery = getNew(reg); - // This could be one of the header slots we don't care about. - if (!cachedRecovery) { - if (verbose) - dataLog(" + ", reg, " is OK\n"); - continue; - } - - if (canLoadAndBox(*cachedRecovery) && hasOnlySafeWrites(*cachedRecovery) - && !cachedRecovery->wantedJSValueRegs() - && cachedRecovery->wantedFPR() == InvalidFPRReg) { - emitLoad(*cachedRecovery); - emitBox(*cachedRecovery); - bool writesOK = tryWrites(*cachedRecovery); - ASSERT_UNUSED(writesOK, writesOK); - } else if (verbose) - dataLog(" - ", cachedRecovery->recovery(), " can't be handled just yet.\n"); - } - ASSERT(dangerFrontier() < firstNew()); - - // Now, the danger zone is empty, but we still have a couple of - // things to do: - // - // 1) There could be remaining safe writes that failed earlier due - // to high register pressure and had nothing to do with the - // danger zone whatsoever. - // - // 2) Some wanted registers could have to be loaded (this could - // happen either when making a call to a new function with a - // lower number of arguments - since above here, we only load - // wanted registers when they are at the danger frontier -, or - // if a wanted register got spilled). - // - // 3) Some wanted registers could have been loaded in the wrong - // registers - // - // 4) We have to take care of some bookkeeping - namely, storing - // the argument count and updating the stack pointer. - - // At this point, we must have enough registers available for - // handling 1). None of the loads can fail because we have been - // eagerly freeing up registers in all the previous phases - so - // the only values that are in registers at this point must have - // wanted registers. - if (verbose) - dataLog(" Danger zone is clear, performing remaining writes.\n"); - for (VirtualRegister reg = firstNew(); reg <= lastNew(); reg += 1) { - CachedRecovery* cachedRecovery { getNew(reg) }; - if (!cachedRecovery) - continue; - - emitLoad(*cachedRecovery); - emitBox(*cachedRecovery); - bool writesOK = tryWrites(*cachedRecovery); - ASSERT_UNUSED(writesOK, writesOK); - } - -#if USE(JSVALUE64) - if (m_tagTypeNumber != InvalidGPRReg && m_newRegisters[m_tagTypeNumber]) - releaseGPR(m_tagTypeNumber); -#endif - - // Handle 2) by loading all registers. We don't have to do any - // writes, since they have been taken care of above. - if (verbose) - dataLog(" Loading wanted registers into registers\n"); - for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) { - CachedRecovery* cachedRecovery { m_newRegisters[reg] }; - if (!cachedRecovery) - continue; - - emitLoad(*cachedRecovery); - emitBox(*cachedRecovery); - ASSERT(cachedRecovery->targets().isEmpty()); - } - -#if USE(JSVALUE64) - if (m_tagTypeNumber != InvalidGPRReg) - releaseGPR(m_tagTypeNumber); -#endif - - // At this point, we have read everything we cared about from the - // stack, and written everything we had to to the stack. - if (verbose) - dataLog(" Callee frame is fully set up\n"); - if (!ASSERT_DISABLED) { - for (VirtualRegister reg = firstNew(); reg <= lastNew(); reg += 1) - ASSERT_UNUSED(reg, !getNew(reg)); - - for (CachedRecovery* cachedRecovery : m_cachedRecoveries) { - ASSERT_UNUSED(cachedRecovery, cachedRecovery->targets().isEmpty()); - ASSERT(!cachedRecovery->recovery().isInJSStack()); - } - } - - // We need to handle 4) first because it implies releasing - // m_newFrameBase, which could be a wanted register. - if (verbose) - dataLog(" * Storing the argument count into ", VirtualRegister { JSStack::ArgumentCount }, "\n"); - m_jit.store32(MacroAssembler::TrustedImm32(0), - addressForNew(VirtualRegister { JSStack::ArgumentCount }).withOffset(TagOffset)); - m_jit.store32(MacroAssembler::TrustedImm32(argCount()), - addressForNew(VirtualRegister { JSStack::ArgumentCount }).withOffset(PayloadOffset)); - - if (!isSlowPath()) { - ASSERT(m_newFrameBase != MacroAssembler::stackPointerRegister); - if (verbose) - dataLog(" Releasing the new frame base pointer\n"); - m_jit.move(m_newFrameBase, MacroAssembler::stackPointerRegister); - releaseGPR(m_newFrameBase); - } - - // Finally we handle 3) - if (verbose) - dataLog(" Ensuring wanted registers are in the right register\n"); - for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) { - CachedRecovery* cachedRecovery { m_newRegisters[reg] }; - if (!cachedRecovery) - continue; - - emitDisplace(*cachedRecovery); - } -} - -} // namespace JSC - -#endif // ENABLE(JIT) |