| /* |
| * Copyright (C) 2011 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 "DFGJITCodeGenerator.h" |
| |
| #if ENABLE(DFG_JIT) |
| |
| #include "DFGNonSpeculativeJIT.h" |
| #include "DFGSpeculativeJIT.h" |
| #include "LinkBuffer.h" |
| |
| namespace JSC { namespace DFG { |
| |
| GPRReg JITCodeGenerator::fillInteger(NodeIndex nodeIndex, DataFormat& returnFormat) |
| { |
| Node& node = m_jit.graph()[nodeIndex]; |
| VirtualRegister virtualRegister = node.virtualRegister; |
| GenerationInfo& info = m_generationInfo[virtualRegister]; |
| |
| if (info.registerFormat() == DataFormatNone) { |
| GPRReg gpr = allocate(); |
| JITCompiler::RegisterID reg = JITCompiler::gprToRegisterID(gpr); |
| |
| if (node.isConstant()) { |
| m_gprs.retain(gpr, virtualRegister, SpillOrderConstant); |
| if (isInt32Constant(nodeIndex)) { |
| m_jit.move(MacroAssembler::Imm32(valueOfInt32Constant(nodeIndex)), reg); |
| info.fillInteger(gpr); |
| returnFormat = DataFormatInteger; |
| return gpr; |
| } |
| if (isDoubleConstant(nodeIndex)) { |
| JSValue jsValue = jsNumber(valueOfDoubleConstant(nodeIndex)); |
| m_jit.move(MacroAssembler::ImmPtr(JSValue::encode(jsValue)), reg); |
| } else { |
| ASSERT(isJSConstant(nodeIndex)); |
| JSValue jsValue = valueOfJSConstant(nodeIndex); |
| m_jit.move(MacroAssembler::ImmPtr(JSValue::encode(jsValue)), reg); |
| } |
| } else { |
| ASSERT(info.spillFormat() == DataFormatJS || info.spillFormat() == DataFormatJSInteger); |
| m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); |
| m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), reg); |
| } |
| |
| // Since we statically know that we're filling an integer, and values |
| // in the RegisterFile are boxed, this must be DataFormatJSInteger. |
| // We will check this with a jitAssert below. |
| info.fillJSValue(gpr, DataFormatJSInteger); |
| unlock(gpr); |
| } |
| |
| switch (info.registerFormat()) { |
| case DataFormatNone: |
| // Should have filled, above. |
| case DataFormatJSDouble: |
| case DataFormatDouble: |
| case DataFormatJS: |
| case DataFormatCell: |
| case DataFormatJSCell: |
| // Should only be calling this function if we know this operand to be integer. |
| ASSERT_NOT_REACHED(); |
| |
| case DataFormatJSInteger: { |
| GPRReg gpr = info.gpr(); |
| m_gprs.lock(gpr); |
| m_jit.jitAssertIsJSInt32(gpr); |
| returnFormat = DataFormatJSInteger; |
| return gpr; |
| } |
| |
| case DataFormatInteger: { |
| GPRReg gpr = info.gpr(); |
| m_gprs.lock(gpr); |
| m_jit.jitAssertIsInt32(gpr); |
| returnFormat = DataFormatInteger; |
| return gpr; |
| } |
| } |
| |
| ASSERT_NOT_REACHED(); |
| return InvalidGPRReg; |
| } |
| |
| FPRReg JITCodeGenerator::fillDouble(NodeIndex nodeIndex) |
| { |
| Node& node = m_jit.graph()[nodeIndex]; |
| VirtualRegister virtualRegister = node.virtualRegister; |
| GenerationInfo& info = m_generationInfo[virtualRegister]; |
| |
| if (info.registerFormat() == DataFormatNone) { |
| GPRReg gpr = allocate(); |
| JITCompiler::RegisterID reg = JITCompiler::gprToRegisterID(gpr); |
| |
| if (node.isConstant()) { |
| if (isInt32Constant(nodeIndex)) { |
| // FIXME: should not be reachable? |
| m_jit.move(MacroAssembler::Imm32(valueOfInt32Constant(nodeIndex)), reg); |
| m_gprs.retain(gpr, virtualRegister, SpillOrderConstant); |
| info.fillInteger(gpr); |
| unlock(gpr); |
| } else if (isDoubleConstant(nodeIndex)) { |
| FPRReg fpr = fprAllocate(); |
| m_jit.move(MacroAssembler::ImmPtr(reinterpret_cast<void*>(reinterpretDoubleToIntptr(valueOfDoubleConstant(nodeIndex)))), reg); |
| m_jit.movePtrToDouble(reg, JITCompiler::fprToRegisterID(fpr)); |
| unlock(gpr); |
| |
| m_fprs.retain(fpr, virtualRegister, SpillOrderDouble); |
| info.fillDouble(fpr); |
| return fpr; |
| } else { |
| // FIXME: should not be reachable? |
| ASSERT(isJSConstant(nodeIndex)); |
| JSValue jsValue = valueOfJSConstant(nodeIndex); |
| m_jit.move(MacroAssembler::ImmPtr(JSValue::encode(jsValue)), reg); |
| m_gprs.retain(gpr, virtualRegister, SpillOrderConstant); |
| info.fillJSValue(gpr, DataFormatJS); |
| unlock(gpr); |
| } |
| } else { |
| DataFormat spillFormat = info.spillFormat(); |
| ASSERT(spillFormat & DataFormatJS); |
| m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); |
| m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), reg); |
| info.fillJSValue(gpr, m_isSpeculative ? spillFormat : DataFormatJS); |
| unlock(gpr); |
| } |
| } |
| |
| switch (info.registerFormat()) { |
| case DataFormatNone: |
| // Should have filled, above. |
| case DataFormatCell: |
| case DataFormatJSCell: |
| // Should only be calling this function if we know this operand to be numeric. |
| ASSERT_NOT_REACHED(); |
| |
| case DataFormatJS: { |
| GPRReg jsValueGpr = info.gpr(); |
| m_gprs.lock(jsValueGpr); |
| FPRReg fpr = fprAllocate(); |
| GPRReg tempGpr = allocate(); // FIXME: can we skip this allocation on the last use of the virtual register? |
| |
| JITCompiler::RegisterID jsValueReg = JITCompiler::gprToRegisterID(jsValueGpr); |
| JITCompiler::FPRegisterID fpReg = JITCompiler::fprToRegisterID(fpr); |
| JITCompiler::RegisterID tempReg = JITCompiler::gprToRegisterID(tempGpr); |
| |
| JITCompiler::Jump isInteger = m_jit.branchPtr(MacroAssembler::AboveOrEqual, jsValueReg, JITCompiler::tagTypeNumberRegister); |
| |
| m_jit.jitAssertIsJSDouble(jsValueGpr); |
| |
| // First, if we get here we have a double encoded as a JSValue |
| m_jit.move(jsValueReg, tempReg); |
| m_jit.addPtr(JITCompiler::tagTypeNumberRegister, tempReg); |
| m_jit.movePtrToDouble(tempReg, fpReg); |
| JITCompiler::Jump hasUnboxedDouble = m_jit.jump(); |
| |
| // Finally, handle integers. |
| isInteger.link(&m_jit); |
| m_jit.convertInt32ToDouble(jsValueReg, fpReg); |
| hasUnboxedDouble.link(&m_jit); |
| |
| m_gprs.release(jsValueGpr); |
| m_gprs.unlock(jsValueGpr); |
| m_gprs.unlock(tempGpr); |
| m_fprs.retain(fpr, virtualRegister, SpillOrderDouble); |
| info.fillDouble(fpr); |
| return fpr; |
| } |
| |
| case DataFormatJSInteger: |
| case DataFormatInteger: { |
| FPRReg fpr = fprAllocate(); |
| GPRReg gpr = info.gpr(); |
| m_gprs.lock(gpr); |
| JITCompiler::RegisterID reg = JITCompiler::gprToRegisterID(gpr); |
| JITCompiler::FPRegisterID fpReg = JITCompiler::fprToRegisterID(fpr); |
| |
| m_jit.convertInt32ToDouble(reg, fpReg); |
| |
| m_gprs.release(gpr); |
| m_gprs.unlock(gpr); |
| m_fprs.retain(fpr, virtualRegister, SpillOrderDouble); |
| info.fillDouble(fpr); |
| return fpr; |
| } |
| |
| // Unbox the double |
| case DataFormatJSDouble: { |
| GPRReg gpr = info.gpr(); |
| FPRReg fpr = unboxDouble(gpr); |
| |
| m_gprs.release(gpr); |
| m_fprs.retain(fpr, virtualRegister, SpillOrderDouble); |
| |
| info.fillDouble(fpr); |
| return fpr; |
| } |
| |
| case DataFormatDouble: { |
| FPRReg fpr = info.fpr(); |
| m_fprs.lock(fpr); |
| return fpr; |
| } |
| } |
| |
| ASSERT_NOT_REACHED(); |
| return InvalidFPRReg; |
| } |
| |
| GPRReg JITCodeGenerator::fillJSValue(NodeIndex nodeIndex) |
| { |
| Node& node = m_jit.graph()[nodeIndex]; |
| VirtualRegister virtualRegister = node.virtualRegister; |
| GenerationInfo& info = m_generationInfo[virtualRegister]; |
| |
| switch (info.registerFormat()) { |
| case DataFormatNone: { |
| GPRReg gpr = allocate(); |
| JITCompiler::RegisterID reg = JITCompiler::gprToRegisterID(gpr); |
| |
| if (node.isConstant()) { |
| if (isInt32Constant(nodeIndex)) { |
| info.fillJSValue(gpr, DataFormatJSInteger); |
| JSValue jsValue = jsNumber(valueOfInt32Constant(nodeIndex)); |
| m_jit.move(MacroAssembler::ImmPtr(JSValue::encode(jsValue)), reg); |
| } else if (isDoubleConstant(nodeIndex)) { |
| info.fillJSValue(gpr, DataFormatJSDouble); |
| JSValue jsValue(JSValue::EncodeAsDouble, valueOfDoubleConstant(nodeIndex)); |
| m_jit.move(MacroAssembler::ImmPtr(JSValue::encode(jsValue)), reg); |
| } else { |
| ASSERT(isJSConstant(nodeIndex)); |
| JSValue jsValue = valueOfJSConstant(nodeIndex); |
| m_jit.move(MacroAssembler::ImmPtr(JSValue::encode(jsValue)), reg); |
| info.fillJSValue(gpr, DataFormatJS); |
| } |
| |
| m_gprs.retain(gpr, virtualRegister, SpillOrderConstant); |
| } else { |
| DataFormat spillFormat = info.spillFormat(); |
| ASSERT(spillFormat & DataFormatJS); |
| m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); |
| m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), reg); |
| info.fillJSValue(gpr, m_isSpeculative ? spillFormat : DataFormatJS); |
| } |
| return gpr; |
| } |
| |
| case DataFormatInteger: { |
| GPRReg gpr = info.gpr(); |
| // If the register has already been locked we need to take a copy. |
| // If not, we'll zero extend in place, so mark on the info that this is now type DataFormatInteger, not DataFormatJSInteger. |
| if (m_gprs.isLocked(gpr)) { |
| GPRReg result = allocate(); |
| m_jit.orPtr(JITCompiler::tagTypeNumberRegister, JITCompiler::gprToRegisterID(gpr), JITCompiler::gprToRegisterID(result)); |
| return result; |
| } |
| m_gprs.lock(gpr); |
| m_jit.orPtr(JITCompiler::tagTypeNumberRegister, JITCompiler::gprToRegisterID(gpr)); |
| info.fillJSValue(gpr, DataFormatJSInteger); |
| return gpr; |
| } |
| |
| case DataFormatDouble: { |
| FPRReg fpr = info.fpr(); |
| GPRReg gpr = boxDouble(fpr); |
| |
| // Update all info |
| info.fillJSValue(gpr, DataFormatJSDouble); |
| m_fprs.release(fpr); |
| m_gprs.retain(gpr, virtualRegister, SpillOrderJS); |
| |
| return gpr; |
| } |
| |
| case DataFormatCell: |
| // No retag required on JSVALUE64! |
| case DataFormatJS: |
| case DataFormatJSInteger: |
| case DataFormatJSDouble: |
| case DataFormatJSCell: { |
| GPRReg gpr = info.gpr(); |
| m_gprs.lock(gpr); |
| return gpr; |
| } |
| } |
| |
| ASSERT_NOT_REACHED(); |
| return InvalidGPRReg; |
| } |
| |
| void JITCodeGenerator::useChildren(Node& node) |
| { |
| NodeIndex child1 = node.child1; |
| if (child1 == NoNode) { |
| ASSERT(node.child2 == NoNode && node.child3 == NoNode); |
| return; |
| } |
| use(child1); |
| |
| NodeIndex child2 = node.child2; |
| if (child2 == NoNode) { |
| ASSERT(node.child3 == NoNode); |
| return; |
| } |
| use(child2); |
| |
| NodeIndex child3 = node.child3; |
| if (child3 == NoNode) |
| return; |
| use(child3); |
| } |
| |
| #ifndef NDEBUG |
| static const char* dataFormatString(DataFormat format) |
| { |
| // These values correspond to the DataFormat enum. |
| const char* strings[] = { |
| "[ ]", |
| "[ i]", |
| "[ d]", |
| "[ c]", |
| "Err!", |
| "Err!", |
| "Err!", |
| "Err!", |
| "[J ]", |
| "[Ji]", |
| "[Jd]", |
| "[Jc]", |
| "Err!", |
| "Err!", |
| "Err!", |
| "Err!", |
| }; |
| return strings[format]; |
| } |
| |
| void JITCodeGenerator::dump(const char* label) |
| { |
| if (label) |
| fprintf(stderr, "<%s>\n", label); |
| |
| fprintf(stderr, " gprs:\n"); |
| m_gprs.dump(); |
| fprintf(stderr, " fprs:\n"); |
| m_fprs.dump(); |
| fprintf(stderr, " VirtualRegisters:\n"); |
| for (unsigned i = 0; i < m_generationInfo.size(); ++i) { |
| GenerationInfo& info = m_generationInfo[i]; |
| if (info.alive()) |
| fprintf(stderr, " % 3d:%s%s\n", i, dataFormatString(info.registerFormat()), dataFormatString(info.spillFormat())); |
| else |
| fprintf(stderr, " % 3d:[__][__]\n", i); |
| } |
| if (label) |
| fprintf(stderr, "</%s>\n", label); |
| } |
| #endif |
| |
| |
| #if DFG_CONSISTENCY_CHECK |
| void JITCodeGenerator::checkConsistency() |
| { |
| VirtualRegister grpContents[numberOfGPRs]; |
| VirtualRegister frpContents[numberOfFPRs]; |
| |
| for (unsigned i = 0; i < numberOfGPRs; ++i) |
| grpContents[i] = InvalidVirtualRegister; |
| for (unsigned i = 0; i < numberOfFPRs; ++i) |
| frpContents[i] = InvalidVirtualRegister; |
| for (unsigned i = 0; i < m_generationInfo.size(); ++i) { |
| GenerationInfo& info = m_generationInfo[i]; |
| if (!info.alive()) |
| continue; |
| switch (info.registerFormat()) { |
| case DataFormatNone: |
| break; |
| case DataFormatInteger: |
| case DataFormatCell: |
| case DataFormatJS: |
| case DataFormatJSInteger: |
| case DataFormatJSDouble: |
| case DataFormatJSCell: { |
| GPRReg gpr = info.gpr(); |
| ASSERT(gpr != InvalidGPRReg); |
| grpContents[gpr] = (VirtualRegister)i; |
| break; |
| } |
| case DataFormatDouble: { |
| FPRReg fpr = info.fpr(); |
| ASSERT(fpr != InvalidFPRReg); |
| frpContents[fpr] = (VirtualRegister)i; |
| break; |
| } |
| } |
| } |
| |
| for (GPRReg i = gpr0; i < numberOfGPRs; next(i)) { |
| if (m_gprs.isLocked(i) || m_gprs.name(i) != grpContents[i]) { |
| dump(); |
| CRASH(); |
| } |
| } |
| for (FPRReg i = fpr0; i < numberOfFPRs; next(i)) { |
| if (m_fprs.isLocked(i) || m_fprs.name(i) != frpContents[i]) { |
| dump(); |
| CRASH(); |
| } |
| } |
| } |
| #endif |
| |
| GPRTemporary::GPRTemporary(JITCodeGenerator* jit) |
| : m_jit(jit) |
| , m_gpr(InvalidGPRReg) |
| { |
| m_gpr = m_jit->allocate(); |
| } |
| |
| GPRTemporary::GPRTemporary(JITCodeGenerator* jit, SpeculateIntegerOperand& op1) |
| : m_jit(jit) |
| , m_gpr(InvalidGPRReg) |
| { |
| // locking into a register may free for reuse! |
| op1.gpr(); |
| if (m_jit->canReuse(op1.index())) |
| m_gpr = m_jit->reuse(op1.gpr()); |
| else |
| m_gpr = m_jit->allocate(); |
| } |
| |
| GPRTemporary::GPRTemporary(JITCodeGenerator* jit, SpeculateIntegerOperand& op1, SpeculateIntegerOperand& op2) |
| : m_jit(jit) |
| , m_gpr(InvalidGPRReg) |
| { |
| // locking into a register may free for reuse! |
| op1.gpr(); |
| op2.gpr(); |
| if (m_jit->canReuse(op1.index())) |
| m_gpr = m_jit->reuse(op1.gpr()); |
| else if (m_jit->canReuse(op2.index())) |
| m_gpr = m_jit->reuse(op2.gpr()); |
| else |
| m_gpr = m_jit->allocate(); |
| } |
| |
| GPRTemporary::GPRTemporary(JITCodeGenerator* jit, IntegerOperand& op1) |
| : m_jit(jit) |
| , m_gpr(InvalidGPRReg) |
| { |
| // locking into a register may free for reuse! |
| op1.gpr(); |
| if (m_jit->canReuse(op1.index())) |
| m_gpr = m_jit->reuse(op1.gpr()); |
| else |
| m_gpr = m_jit->allocate(); |
| } |
| |
| GPRTemporary::GPRTemporary(JITCodeGenerator* jit, IntegerOperand& op1, IntegerOperand& op2) |
| : m_jit(jit) |
| , m_gpr(InvalidGPRReg) |
| { |
| // locking into a register may free for reuse! |
| op1.gpr(); |
| op2.gpr(); |
| if (m_jit->canReuse(op1.index())) |
| m_gpr = m_jit->reuse(op1.gpr()); |
| else if (m_jit->canReuse(op2.index())) |
| m_gpr = m_jit->reuse(op2.gpr()); |
| else |
| m_gpr = m_jit->allocate(); |
| } |
| |
| GPRTemporary::GPRTemporary(JITCodeGenerator* jit, SpeculateCellOperand& op1) |
| : m_jit(jit) |
| , m_gpr(InvalidGPRReg) |
| { |
| // locking into a register may free for reuse! |
| op1.gpr(); |
| if (m_jit->canReuse(op1.index())) |
| m_gpr = m_jit->reuse(op1.gpr()); |
| else |
| m_gpr = m_jit->allocate(); |
| } |
| |
| GPRTemporary::GPRTemporary(JITCodeGenerator* jit, JSValueOperand& op1) |
| : m_jit(jit) |
| , m_gpr(InvalidGPRReg) |
| { |
| // locking into a register may free for reuse! |
| op1.gpr(); |
| if (m_jit->canReuse(op1.index())) |
| m_gpr = m_jit->reuse(op1.gpr()); |
| else |
| m_gpr = m_jit->allocate(); |
| } |
| |
| FPRTemporary::FPRTemporary(JITCodeGenerator* jit) |
| : m_jit(jit) |
| , m_fpr(InvalidFPRReg) |
| { |
| m_fpr = m_jit->fprAllocate(); |
| } |
| |
| FPRTemporary::FPRTemporary(JITCodeGenerator* jit, DoubleOperand& op1) |
| : m_jit(jit) |
| , m_fpr(InvalidFPRReg) |
| { |
| // locking into a register may free for reuse! |
| op1.fpr(); |
| if (m_jit->canReuse(op1.index())) |
| m_fpr = m_jit->reuse(op1.fpr()); |
| else |
| m_fpr = m_jit->fprAllocate(); |
| } |
| |
| FPRTemporary::FPRTemporary(JITCodeGenerator* jit, DoubleOperand& op1, DoubleOperand& op2) |
| : m_jit(jit) |
| , m_fpr(InvalidFPRReg) |
| { |
| // locking into a register may free for reuse! |
| op1.fpr(); |
| op2.fpr(); |
| if (m_jit->canReuse(op1.index())) |
| m_fpr = m_jit->reuse(op1.fpr()); |
| else if (m_jit->canReuse(op2.index())) |
| m_fpr = m_jit->reuse(op2.fpr()); |
| else |
| m_fpr = m_jit->fprAllocate(); |
| } |
| |
| } } // namespace JSC::DFG |
| |
| #endif |