|  | /* | 
|  | * 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<DataFormat>(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 |