/* * Copyright (C) 2011, 2013 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 DFGGenerationInfo_h #define DFGGenerationInfo_h #if ENABLE(DFG_JIT) #include "DFGJITCompiler.h" #include "DFGMinifiedID.h" #include "DFGVariableEvent.h" #include "DFGVariableEventStream.h" #include "DataFormat.h" namespace JSC { namespace DFG { // === GenerationInfo === // // This class is used to track the current status of live values during code generation. // Can provide information as to whether a value is in machine registers, and if so which, // whether a value has been spilled to the RegisterFile, and if so may be able to provide // details of the format in memory (all values are spilled in a boxed form, but we may be // able to track the type of box), and tracks how many outstanding uses of a value remain, // so that we know when the value is dead and the machine registers associated with it // may be released. class GenerationInfo { public: GenerationInfo() : m_node(0) , m_useCount(0) , m_registerFormat(DataFormatNone) , m_spillFormat(DataFormatNone) , m_canFill(false) , m_bornForOSR(false) , m_isConstant(false) { } void initConstant(Node* node, uint32_t useCount) { m_node = node; m_useCount = useCount; m_registerFormat = DataFormatNone; m_spillFormat = DataFormatNone; m_canFill = true; m_bornForOSR = false; m_isConstant = true; ASSERT(m_useCount); } void initGPR(Node* node, uint32_t useCount, GPRReg gpr, DataFormat format) { ASSERT(gpr != InvalidGPRReg); m_node = node; m_useCount = useCount; m_registerFormat = format; m_spillFormat = DataFormatNone; m_canFill = false; u.gpr = gpr; m_bornForOSR = false; m_isConstant = false; ASSERT(m_useCount); } void initInt32(Node* node, uint32_t useCount, GPRReg gpr) { initGPR(node, useCount, gpr, DataFormatInt32); } void initInt52(Node* node, uint32_t useCount, GPRReg reg, DataFormat format) { ASSERT(format == DataFormatInt52 || format == DataFormatStrictInt52); initGPR(node, useCount, reg, format); } void initInt52(Node* node, uint32_t useCount, GPRReg reg) { initGPR(node, useCount, reg, DataFormatInt52); } void initStrictInt52(Node* node, uint32_t useCount, GPRReg reg) { initGPR(node, useCount, reg, DataFormatStrictInt52); } #if USE(JSVALUE64) void initJSValue(Node* node, uint32_t useCount, GPRReg gpr, DataFormat format = DataFormatJS) { ASSERT(format & DataFormatJS); initGPR(node, useCount, gpr, format); } #elif USE(JSVALUE32_64) void initJSValue(Node* node, uint32_t useCount, GPRReg tagGPR, GPRReg payloadGPR, DataFormat format = DataFormatJS) { ASSERT(format & DataFormatJS); m_node = node; m_useCount = useCount; m_registerFormat = format; m_spillFormat = DataFormatNone; m_canFill = false; u.v.tagGPR = tagGPR; u.v.payloadGPR = payloadGPR; m_bornForOSR = false; m_isConstant = false; ASSERT(m_useCount); } #endif void initCell(Node* node, uint32_t useCount, GPRReg gpr) { initGPR(node, useCount, gpr, DataFormatCell); } void initBoolean(Node* node, uint32_t useCount, GPRReg gpr) { initGPR(node, useCount, gpr, DataFormatBoolean); } void initDouble(Node* node, uint32_t useCount, FPRReg fpr) { ASSERT(fpr != InvalidFPRReg); m_node = node; m_useCount = useCount; m_registerFormat = DataFormatDouble; m_spillFormat = DataFormatNone; m_canFill = false; u.fpr = fpr; m_bornForOSR = false; m_isConstant = false; ASSERT(m_useCount); } void initStorage(Node* node, uint32_t useCount, GPRReg gpr) { initGPR(node, useCount, gpr, DataFormatStorage); } // Get the node that produced this value. Node* node() { return m_node; } void noticeOSRBirth(VariableEventStream& stream, Node* node, VirtualRegister virtualRegister) { if (m_node != node) return; if (!alive()) return; if (m_bornForOSR) return; m_bornForOSR = true; if (m_isConstant) appendBirth(stream); else if (m_registerFormat != DataFormatNone) appendFill(BirthToFill, stream); else if (m_spillFormat != DataFormatNone) appendSpill(BirthToSpill, stream, virtualRegister); } // Mark the value as having been used (decrement the useCount). // Returns true if this was the last use of the value, and any // associated machine registers may be freed. bool use(VariableEventStream& stream) { ASSERT(m_useCount); bool result = !--m_useCount; if (result && m_bornForOSR) { ASSERT(m_node); stream.appendAndLog(VariableEvent::death(MinifiedID(m_node))); } return result; } // Used to check the operands of operations to see if they are on // their last use; in some cases it may be safe to reuse the same // machine register for the result of the operation. uint32_t useCount() { ASSERT(m_useCount); return m_useCount; } // Get the format of the value in machine registers (or 'none'). DataFormat registerFormat() { return m_registerFormat; } // Get the format of the value as it is spilled in the JSStack (or 'none'). DataFormat spillFormat() { return m_spillFormat; } bool isFormat(DataFormat expectedFormat) { return registerFormat() == expectedFormat || spillFormat() == expectedFormat; } bool isJSFormat(DataFormat expectedFormat) { return JSC::isJSFormat(registerFormat(), expectedFormat) || JSC::isJSFormat(spillFormat(), expectedFormat); } bool isJSInt32() { return isJSFormat(DataFormatJSInt32); } bool isInt52() { return isFormat(DataFormatInt52); } bool isStrictInt52() { return isFormat(DataFormatStrictInt52); } bool isJSDouble() { return isJSFormat(DataFormatJSDouble); } bool isJSCell() { return isJSFormat(DataFormatJSCell); } bool isJSBoolean() { return isJSFormat(DataFormatJSBoolean); } bool isUnknownJS() { return spillFormat() == DataFormatNone ? registerFormat() == DataFormatJS || registerFormat() == DataFormatNone : spillFormat() == DataFormatJS; } // Get the machine resister currently holding the value. #if USE(JSVALUE64) GPRReg gpr() { ASSERT(m_registerFormat && m_registerFormat != DataFormatDouble); return u.gpr; } FPRReg fpr() { ASSERT(m_registerFormat == DataFormatDouble); return u.fpr; } JSValueRegs jsValueRegs() { ASSERT(m_registerFormat & DataFormatJS); return JSValueRegs(u.gpr); } #elif USE(JSVALUE32_64) GPRReg gpr() { ASSERT(!(m_registerFormat & DataFormatJS) && m_registerFormat != DataFormatDouble); return u.gpr; } GPRReg tagGPR() { ASSERT(m_registerFormat & DataFormatJS); return u.v.tagGPR; } GPRReg payloadGPR() { ASSERT(m_registerFormat & DataFormatJS); return u.v.payloadGPR; } FPRReg fpr() { ASSERT(m_registerFormat == DataFormatDouble || m_registerFormat == DataFormatJSDouble); return u.fpr; } JSValueRegs jsValueRegs() { ASSERT(m_registerFormat & DataFormatJS); return JSValueRegs(u.v.tagGPR, u.v.payloadGPR); } #endif // Check whether a value needs spilling in order to free up any associated machine registers. bool needsSpill() { // This should only be called on values that are currently in a register. ASSERT(m_registerFormat != DataFormatNone); // Constants do not need spilling, nor do values that have already been // spilled to the JSStack. return !m_canFill; } // Called when a VirtualRegister is being spilled to the JSStack for the first time. void spill(VariableEventStream& stream, VirtualRegister virtualRegister, DataFormat spillFormat) { // We shouldn't be spill values that don't need spilling. ASSERT(!m_canFill); ASSERT(m_spillFormat == DataFormatNone); // We should only be spilling values that are currently in machine registers. ASSERT(m_registerFormat != DataFormatNone); m_registerFormat = DataFormatNone; m_spillFormat = spillFormat; m_canFill = true; if (m_bornForOSR) appendSpill(Spill, stream, virtualRegister); } // Called on values that don't need spilling (constants and values that have // already been spilled), to mark them as no longer being in machine registers. void setSpilled(VariableEventStream& stream, VirtualRegister virtualRegister) { // Should only be called on values that don't need spilling, and are currently in registers. ASSERT(m_canFill && m_registerFormat != DataFormatNone); m_registerFormat = DataFormatNone; if (m_bornForOSR) appendSpill(Spill, stream, virtualRegister); } void killSpilled() { m_spillFormat = DataFormatNone; m_canFill = false; } void fillGPR(VariableEventStream& stream, GPRReg gpr, DataFormat format) { ASSERT(gpr != InvalidGPRReg); m_registerFormat = format; u.gpr = gpr; if (m_bornForOSR) appendFill(Fill, stream); } // Record that this value is filled into machine registers, // tracking which registers, and what format the value has. #if USE(JSVALUE64) void fillJSValue(VariableEventStream& stream, GPRReg gpr, DataFormat format = DataFormatJS) { ASSERT(format & DataFormatJS); fillGPR(stream, gpr, format); } #elif USE(JSVALUE32_64) void fillJSValue(VariableEventStream& stream, GPRReg tagGPR, GPRReg payloadGPR, DataFormat format = DataFormatJS) { ASSERT(format & DataFormatJS); m_registerFormat = format; u.v.tagGPR = tagGPR; // FIXME: for JSValues with known type (boolean, integer, cell etc.) no tagGPR is needed? u.v.payloadGPR = payloadGPR; if (m_bornForOSR) appendFill(Fill, stream); } void fillCell(VariableEventStream& stream, GPRReg gpr) { fillGPR(stream, gpr, DataFormatCell); } #endif void fillInt32(VariableEventStream& stream, GPRReg gpr) { fillGPR(stream, gpr, DataFormatInt32); } void fillInt52(VariableEventStream& stream, GPRReg gpr, DataFormat format) { ASSERT(format == DataFormatInt52 || format == DataFormatStrictInt52); fillGPR(stream, gpr, format); } void fillInt52(VariableEventStream& stream, GPRReg gpr) { fillGPR(stream, gpr, DataFormatInt52); } void fillStrictInt52(VariableEventStream& stream, GPRReg gpr) { fillGPR(stream, gpr, DataFormatStrictInt52); } void fillBoolean(VariableEventStream& stream, GPRReg gpr) { fillGPR(stream, gpr, DataFormatBoolean); } void fillDouble(VariableEventStream& stream, FPRReg fpr) { ASSERT(fpr != InvalidFPRReg); m_registerFormat = DataFormatDouble; u.fpr = fpr; if (m_bornForOSR) appendFill(Fill, stream); } void fillStorage(VariableEventStream& stream, GPRReg gpr) { fillGPR(stream, gpr, DataFormatStorage); } bool alive() { return m_useCount; } ValueRecovery recovery(VirtualRegister spillSlot) const { if (m_isConstant) return ValueRecovery::constant(m_node->constant()->value()); if (m_registerFormat == DataFormatDouble) return ValueRecovery::inFPR(u.fpr, DataFormatDouble); #if USE(JSVALUE32_64) if (m_registerFormat & DataFormatJS) { if (m_registerFormat == DataFormatJS) return ValueRecovery::inPair(u.v.tagGPR, u.v.payloadGPR); return ValueRecovery::inGPR(u.v.payloadGPR, static_cast(m_registerFormat & ~DataFormatJS)); } #endif if (m_registerFormat) return ValueRecovery::inGPR(u.gpr, m_registerFormat); ASSERT(m_spillFormat); return ValueRecovery::displacedInJSStack(spillSlot, m_spillFormat); } private: void appendBirth(VariableEventStream& stream) { stream.appendAndLog(VariableEvent::birth(MinifiedID(m_node))); } void appendFill(VariableEventKind kind, VariableEventStream& stream) { ASSERT(m_bornForOSR); if (m_registerFormat == DataFormatDouble) { stream.appendAndLog(VariableEvent::fillFPR(kind, MinifiedID(m_node), u.fpr)); return; } #if USE(JSVALUE32_64) if (m_registerFormat & DataFormatJS) { stream.appendAndLog(VariableEvent::fillPair(kind, MinifiedID(m_node), u.v.tagGPR, u.v.payloadGPR)); return; } #endif stream.appendAndLog(VariableEvent::fillGPR(kind, MinifiedID(m_node), u.gpr, m_registerFormat)); } void appendSpill(VariableEventKind kind, VariableEventStream& stream, VirtualRegister virtualRegister) { stream.appendAndLog(VariableEvent::spill(kind, MinifiedID(m_node), virtualRegister, m_spillFormat)); } // The node whose result is stored in this virtual register. Node* m_node; uint32_t m_useCount; DataFormat m_registerFormat; DataFormat m_spillFormat; bool m_canFill; bool m_bornForOSR; bool m_isConstant; union { GPRReg gpr; FPRReg fpr; #if USE(JSVALUE32_64) struct { GPRReg tagGPR; GPRReg payloadGPR; } v; #endif } u; }; } } // namespace JSC::DFG #endif #endif