| /* |
| * 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. |
| */ |
| |
| #pragma once |
| |
| #if ENABLE(DFG_JIT) |
| |
| #include "DFGMinifiedIDInlines.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, 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(nullptr) |
| , 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(VariableEventStreamBuilder& 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(VariableEventStreamBuilder& 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(VariableEventStreamBuilder& 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(VariableEventStreamBuilder& 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(VariableEventStreamBuilder& 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(VariableEventStreamBuilder& stream, GPRReg gpr, DataFormat format = DataFormatJS) |
| { |
| ASSERT(format & DataFormatJS); |
| fillGPR(stream, gpr, format); |
| } |
| #elif USE(JSVALUE32_64) |
| void fillJSValue(VariableEventStreamBuilder& 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(VariableEventStreamBuilder& stream, GPRReg gpr) |
| { |
| fillGPR(stream, gpr, DataFormatCell); |
| } |
| #endif |
| void fillInt32(VariableEventStreamBuilder& stream, GPRReg gpr) |
| { |
| fillGPR(stream, gpr, DataFormatInt32); |
| } |
| void fillInt52(VariableEventStreamBuilder& stream, GPRReg gpr, DataFormat format) |
| { |
| ASSERT(format == DataFormatInt52 || format == DataFormatStrictInt52); |
| fillGPR(stream, gpr, format); |
| } |
| void fillInt52(VariableEventStreamBuilder& stream, GPRReg gpr) |
| { |
| fillGPR(stream, gpr, DataFormatInt52); |
| } |
| void fillStrictInt52(VariableEventStreamBuilder& stream, GPRReg gpr) |
| { |
| fillGPR(stream, gpr, DataFormatStrictInt52); |
| } |
| void fillBoolean(VariableEventStreamBuilder& stream, GPRReg gpr) |
| { |
| fillGPR(stream, gpr, DataFormatBoolean); |
| } |
| void fillDouble(VariableEventStreamBuilder& stream, FPRReg fpr) |
| { |
| ASSERT(fpr != InvalidFPRReg); |
| m_registerFormat = DataFormatDouble; |
| u.fpr = fpr; |
| |
| if (m_bornForOSR) |
| appendFill(Fill, stream); |
| } |
| void fillStorage(VariableEventStreamBuilder& 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(VariableEventStreamBuilder& stream) |
| { |
| stream.appendAndLog(VariableEvent::birth(MinifiedID(m_node))); |
| } |
| |
| void appendFill(VariableEventKind kind, VariableEventStreamBuilder& 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, VariableEventStreamBuilder& 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 |