diff options
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp')
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp | 2484 |
1 files changed, 2484 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp new file mode 100644 index 000000000..939fef669 --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -0,0 +1,2484 @@ +/* + * 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 "DFGSpeculativeJIT.h" + +#if ENABLE(DFG_JIT) + +#include "JSByteArray.h" +#include "LinkBuffer.h" + +namespace JSC { namespace DFG { + +// On Windows we need to wrap fmod; on other platforms we can call it directly. +// On ARMv7 we assert that all function pointers have to low bit set (point to thumb code). +#if CALLING_CONVENTION_IS_STDCALL || CPU(ARM_THUMB2) +static double DFG_OPERATION fmodAsDFGOperation(double x, double y) +{ + return fmod(x, y); +} +#else +#define fmodAsDFGOperation fmod +#endif + +void SpeculativeJIT::clearGenerationInfo() +{ + for (unsigned i = 0; i < m_generationInfo.size(); ++i) + m_generationInfo[i] = GenerationInfo(); + m_gprs = RegisterBank<GPRInfo>(); + m_fprs = RegisterBank<FPRInfo>(); +} + +GPRReg SpeculativeJIT::fillStorage(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(); + ASSERT(info.spillFormat() == DataFormatStorage); + m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); + m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), gpr); + info.fillStorage(gpr); + return gpr; + } + + case DataFormatStorage: { + GPRReg gpr = info.gpr(); + m_gprs.lock(gpr); + return gpr; + } + + default: + ASSERT_NOT_REACHED(); + } + + return InvalidGPRReg; +} + +void SpeculativeJIT::useChildren(Node& node) +{ + if (node.op & NodeHasVarArgs) { + for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); childIdx++) + use(m_jit.graph().m_varArgChildren[childIdx]); + } else { + 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); + } +} + +bool SpeculativeJIT::isStrictInt32(NodeIndex nodeIndex) +{ + if (isInt32Constant(nodeIndex)) + return true; + + Node& node = m_jit.graph()[nodeIndex]; + GenerationInfo& info = m_generationInfo[node.virtualRegister()]; + + return info.registerFormat() == DataFormatInteger; +} + +bool SpeculativeJIT::isKnownInteger(NodeIndex nodeIndex) +{ + if (isInt32Constant(nodeIndex)) + return true; + + Node& node = m_jit.graph()[nodeIndex]; + + if (node.hasInt32Result()) + return true; + + GenerationInfo& info = m_generationInfo[node.virtualRegister()]; + + return info.isJSInteger(); +} + +bool SpeculativeJIT::isKnownNumeric(NodeIndex nodeIndex) +{ + if (isInt32Constant(nodeIndex) || isNumberConstant(nodeIndex)) + return true; + + Node& node = m_jit.graph()[nodeIndex]; + + if (node.hasNumberResult()) + return true; + + GenerationInfo& info = m_generationInfo[node.virtualRegister()]; + + return info.isJSInteger() || info.isJSDouble(); +} + +bool SpeculativeJIT::isKnownCell(NodeIndex nodeIndex) +{ + return m_generationInfo[m_jit.graph()[nodeIndex].virtualRegister()].isJSCell(); +} + +bool SpeculativeJIT::isKnownNotCell(NodeIndex nodeIndex) +{ + Node& node = m_jit.graph()[nodeIndex]; + VirtualRegister virtualRegister = node.virtualRegister(); + GenerationInfo& info = m_generationInfo[virtualRegister]; + if (node.hasConstant() && !valueOfJSConstant(nodeIndex).isCell()) + return true; + return !(info.isJSCell() || info.isUnknownJS()); +} + +bool SpeculativeJIT::isKnownNotInteger(NodeIndex nodeIndex) +{ + Node& node = m_jit.graph()[nodeIndex]; + VirtualRegister virtualRegister = node.virtualRegister(); + GenerationInfo& info = m_generationInfo[virtualRegister]; + + return info.isJSDouble() || info.isJSCell() || info.isJSBoolean() + || (node.hasConstant() && !valueOfJSConstant(nodeIndex).isInt32()); +} + +bool SpeculativeJIT::isKnownNotNumber(NodeIndex nodeIndex) +{ + Node& node = m_jit.graph()[nodeIndex]; + VirtualRegister virtualRegister = node.virtualRegister(); + GenerationInfo& info = m_generationInfo[virtualRegister]; + + return (!info.isJSDouble() && !info.isJSInteger() && !info.isUnknownJS()) + || (node.hasConstant() && !isNumberConstant(nodeIndex)); +} + +bool SpeculativeJIT::isKnownBoolean(NodeIndex nodeIndex) +{ + Node& node = m_jit.graph()[nodeIndex]; + if (node.hasBooleanResult()) + return true; + + if (isBooleanConstant(nodeIndex)) + return true; + + VirtualRegister virtualRegister = node.virtualRegister(); + GenerationInfo& info = m_generationInfo[virtualRegister]; + + return info.isJSBoolean(); +} + +bool SpeculativeJIT::isKnownNotBoolean(NodeIndex nodeIndex) +{ + Node& node = m_jit.graph()[nodeIndex]; + VirtualRegister virtualRegister = node.virtualRegister(); + GenerationInfo& info = m_generationInfo[virtualRegister]; + if (node.hasConstant() && !valueOfJSConstant(nodeIndex).isBoolean()) + return true; + return !(info.isJSBoolean() || info.isUnknownJS()); +} + +void SpeculativeJIT::writeBarrier(MacroAssembler& jit, GPRReg owner, GPRReg scratch1, GPRReg scratch2, WriteBarrierUseKind useKind) +{ + UNUSED_PARAM(jit); + UNUSED_PARAM(owner); + UNUSED_PARAM(scratch1); + UNUSED_PARAM(scratch2); + UNUSED_PARAM(useKind); + ASSERT(owner != scratch1); + ASSERT(owner != scratch2); + ASSERT(scratch1 != scratch2); + +#if ENABLE(WRITE_BARRIER_PROFILING) + JITCompiler::emitCount(jit, WriteBarrierCounters::jitCounterFor(useKind)); +#endif + markCellCard(jit, owner, scratch1, scratch2); +} + +void SpeculativeJIT::markCellCard(MacroAssembler& jit, GPRReg owner, GPRReg scratch1, GPRReg scratch2) +{ + UNUSED_PARAM(jit); + UNUSED_PARAM(owner); + UNUSED_PARAM(scratch1); + UNUSED_PARAM(scratch2); + +#if ENABLE(GGC) + jit.move(owner, scratch1); + jit.andPtr(TrustedImm32(static_cast<int32_t>(MarkedBlock::blockMask)), scratch1); + jit.move(owner, scratch2); + // consume additional 8 bits as we're using an approximate filter + jit.rshift32(TrustedImm32(MarkedBlock::atomShift + 8), scratch2); + jit.andPtr(TrustedImm32(MarkedBlock::atomMask >> 8), scratch2); + MacroAssembler::Jump filter = jit.branchTest8(MacroAssembler::Zero, MacroAssembler::BaseIndex(scratch1, scratch2, MacroAssembler::TimesOne, MarkedBlock::offsetOfMarks())); + jit.move(owner, scratch2); + jit.rshift32(TrustedImm32(MarkedBlock::cardShift), scratch2); + jit.andPtr(TrustedImm32(MarkedBlock::cardMask), scratch2); + jit.store8(TrustedImm32(1), MacroAssembler::BaseIndex(scratch1, scratch2, MacroAssembler::TimesOne, MarkedBlock::offsetOfCards())); + filter.link(&jit); +#endif +} + +void SpeculativeJIT::writeBarrier(GPRReg ownerGPR, GPRReg valueGPR, NodeIndex valueIndex, WriteBarrierUseKind useKind, GPRReg scratch1, GPRReg scratch2) +{ + UNUSED_PARAM(ownerGPR); + UNUSED_PARAM(valueGPR); + UNUSED_PARAM(scratch1); + UNUSED_PARAM(scratch2); + UNUSED_PARAM(useKind); + + if (isKnownNotCell(valueIndex)) + return; + +#if ENABLE(WRITE_BARRIER_PROFILING) + JITCompiler::emitCount(m_jit, WriteBarrierCounters::jitCounterFor(useKind)); +#endif + +#if ENABLE(GGC) + GPRTemporary temp1; + GPRTemporary temp2; + if (scratch1 == InvalidGPRReg) { + GPRTemporary scratchGPR(this); + temp1.adopt(scratchGPR); + scratch1 = temp1.gpr(); + } + if (scratch2 == InvalidGPRReg) { + GPRTemporary scratchGPR(this); + temp2.adopt(scratchGPR); + scratch2 = temp2.gpr(); + } + + JITCompiler::Jump rhsNotCell; + bool hadCellCheck = false; + if (!isKnownCell(valueIndex) && !isCellPrediction(m_jit.getPrediction(valueIndex))) { + hadCellCheck = true; + rhsNotCell = m_jit.branchIfNotCell(valueGPR); + } + + markCellCard(m_jit, ownerGPR, scratch1, scratch2); + + if (hadCellCheck) + rhsNotCell.link(&m_jit); +#endif +} + +void SpeculativeJIT::writeBarrier(GPRReg ownerGPR, JSCell* value, WriteBarrierUseKind useKind, GPRReg scratch1, GPRReg scratch2) +{ + UNUSED_PARAM(ownerGPR); + UNUSED_PARAM(value); + UNUSED_PARAM(scratch1); + UNUSED_PARAM(scratch2); + UNUSED_PARAM(useKind); + + if (Heap::isMarked(value)) + return; + +#if ENABLE(WRITE_BARRIER_PROFILING) + JITCompiler::emitCount(m_jit, WriteBarrierCounters::jitCounterFor(useKind)); +#endif + +#if ENABLE(GGC) + GPRTemporary temp1; + GPRTemporary temp2; + if (scratch1 == InvalidGPRReg) { + GPRTemporary scratchGPR(this); + temp1.adopt(scratchGPR); + scratch1 = temp1.gpr(); + } + if (scratch2 == InvalidGPRReg) { + GPRTemporary scratchGPR(this); + temp2.adopt(scratchGPR); + scratch2 = temp2.gpr(); + } + + markCellCard(m_jit, ownerGPR, scratch1, scratch2); +#endif +} + +void SpeculativeJIT::writeBarrier(JSCell* owner, GPRReg valueGPR, NodeIndex valueIndex, WriteBarrierUseKind useKind, GPRReg scratch) +{ + UNUSED_PARAM(owner); + UNUSED_PARAM(valueGPR); + UNUSED_PARAM(scratch); + UNUSED_PARAM(useKind); + + if (isKnownNotCell(valueIndex)) + return; + +#if ENABLE(WRITE_BARRIER_PROFILING) + JITCompiler::emitCount(m_jit, WriteBarrierCounters::jitCounterFor(useKind)); +#endif + +#if ENABLE(GGC) + JITCompiler::Jump rhsNotCell; + bool hadCellCheck = false; + if (!isKnownCell(valueIndex) && !isCellPrediction(m_jit.getPrediction(valueIndex))) { + hadCellCheck = true; + rhsNotCell = m_jit.branchIfNotCell(valueGPR); + } + + GPRTemporary temp; + if (scratch == InvalidGPRReg) { + GPRTemporary scratchGPR(this); + temp.adopt(scratchGPR); + scratch = temp.gpr(); + } + + uint8_t* cardAddress = Heap::addressOfCardFor(owner); + m_jit.move(JITCompiler::TrustedImmPtr(cardAddress), scratch); + m_jit.store8(JITCompiler::TrustedImm32(1), JITCompiler::Address(scratch)); + + if (hadCellCheck) + rhsNotCell.link(&m_jit); +#endif +} + +bool SpeculativeJIT::nonSpeculativeCompare(Node& node, MacroAssembler::RelationalCondition cond, S_DFGOperation_EJJ helperFunction) +{ + NodeIndex branchNodeIndex = detectPeepHoleBranch(); + if (branchNodeIndex != NoNode) { + ASSERT(node.adjustedRefCount() == 1); + + nonSpeculativePeepholeBranch(node, branchNodeIndex, cond, helperFunction); + + m_compileIndex = branchNodeIndex; + + return true; + } + + nonSpeculativeNonPeepholeCompare(node, cond, helperFunction); + + return false; +} + +bool SpeculativeJIT::nonSpeculativeStrictEq(Node& node, bool invert) +{ + if (!invert && (isKnownNumeric(node.child1()) || isKnownNumeric(node.child2()))) + return nonSpeculativeCompare(node, MacroAssembler::Equal, operationCompareStrictEq); + + NodeIndex branchNodeIndex = detectPeepHoleBranch(); + if (branchNodeIndex != NoNode) { + ASSERT(node.adjustedRefCount() == 1); + + nonSpeculativePeepholeStrictEq(node, branchNodeIndex, invert); + + m_compileIndex = branchNodeIndex; + + return true; + } + + nonSpeculativeNonPeepholeStrictEq(node, invert); + + return false; +} + +#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 SpeculativeJIT::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", i, dataFormatString(info.registerFormat()), dataFormatString(info.spillFormat())); + else + fprintf(stderr, " % 3d:[__][__]", i); + if (info.registerFormat() == DataFormatDouble) + fprintf(stderr, ":fpr%d\n", info.fpr()); + else if (info.registerFormat() != DataFormatNone +#if USE(JSVALUE32_64) + && !(info.registerFormat() & DataFormatJS) +#endif + ) { + ASSERT(info.gpr() != InvalidGPRReg); + fprintf(stderr, ":%s\n", GPRInfo::debugName(info.gpr())); + } else + fprintf(stderr, "\n"); + } + if (label) + fprintf(stderr, "</%s>\n", label); +} +#endif + + +#if DFG_ENABLE(CONSISTENCY_CHECK) +void SpeculativeJIT::checkConsistency() +{ + bool failed = false; + + for (gpr_iterator iter = m_gprs.begin(); iter != m_gprs.end(); ++iter) { + if (iter.isLocked()) { + fprintf(stderr, "DFG_CONSISTENCY_CHECK failed: gpr %s is locked.\n", iter.debugName()); + failed = true; + } + } + for (fpr_iterator iter = m_fprs.begin(); iter != m_fprs.end(); ++iter) { + if (iter.isLocked()) { + fprintf(stderr, "DFG_CONSISTENCY_CHECK failed: fpr %s is locked.\n", iter.debugName()); + failed = true; + } + } + + for (unsigned i = 0; i < m_generationInfo.size(); ++i) { + VirtualRegister virtualRegister = (VirtualRegister)i; + GenerationInfo& info = m_generationInfo[virtualRegister]; + if (!info.alive()) + continue; + switch (info.registerFormat()) { + case DataFormatNone: + break; + case DataFormatJS: + case DataFormatJSInteger: + case DataFormatJSDouble: + case DataFormatJSCell: + case DataFormatJSBoolean: +#if USE(JSVALUE32_64) + break; +#endif + case DataFormatInteger: + case DataFormatCell: + case DataFormatBoolean: + case DataFormatStorage: { + GPRReg gpr = info.gpr(); + ASSERT(gpr != InvalidGPRReg); + if (m_gprs.name(gpr) != virtualRegister) { + fprintf(stderr, "DFG_CONSISTENCY_CHECK failed: name mismatch for virtual register %d (gpr %s).\n", virtualRegister, GPRInfo::debugName(gpr)); + failed = true; + } + break; + } + case DataFormatDouble: { + FPRReg fpr = info.fpr(); + ASSERT(fpr != InvalidFPRReg); + if (m_fprs.name(fpr) != virtualRegister) { + fprintf(stderr, "DFG_CONSISTENCY_CHECK failed: name mismatch for virtual register %d (fpr %s).\n", virtualRegister, FPRInfo::debugName(fpr)); + failed = true; + } + break; + } + } + } + + for (gpr_iterator iter = m_gprs.begin(); iter != m_gprs.end(); ++iter) { + VirtualRegister virtualRegister = iter.name(); + if (virtualRegister == InvalidVirtualRegister) + continue; + + GenerationInfo& info = m_generationInfo[virtualRegister]; +#if USE(JSVALUE64) + if (iter.regID() != info.gpr()) { + fprintf(stderr, "DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); + failed = true; + } +#else + if (!(info.registerFormat() & DataFormatJS)) { + if (iter.regID() != info.gpr()) { + fprintf(stderr, "DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); + failed = true; + } + } else { + if (iter.regID() != info.tagGPR() && iter.regID() != info.payloadGPR()) { + fprintf(stderr, "DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); + failed = true; + } + } +#endif + } + + for (fpr_iterator iter = m_fprs.begin(); iter != m_fprs.end(); ++iter) { + VirtualRegister virtualRegister = iter.name(); + if (virtualRegister == InvalidVirtualRegister) + continue; + + GenerationInfo& info = m_generationInfo[virtualRegister]; + if (iter.regID() != info.fpr()) { + fprintf(stderr, "DFG_CONSISTENCY_CHECK failed: name mismatch for fpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); + failed = true; + } + } + + if (failed) { + dump(); + CRASH(); + } +} +#endif + +GPRTemporary::GPRTemporary() + : m_jit(0) + , m_gpr(InvalidGPRReg) +{ +} + +GPRTemporary::GPRTemporary(SpeculativeJIT* jit) + : m_jit(jit) + , m_gpr(InvalidGPRReg) +{ + m_gpr = m_jit->allocate(); +} + +GPRTemporary::GPRTemporary(SpeculativeJIT* jit, GPRReg specific) + : m_jit(jit) + , m_gpr(InvalidGPRReg) +{ + m_gpr = m_jit->allocate(specific); +} + +GPRTemporary::GPRTemporary(SpeculativeJIT* jit, SpeculateIntegerOperand& op1) + : m_jit(jit) + , m_gpr(InvalidGPRReg) +{ + if (m_jit->canReuse(op1.index())) + m_gpr = m_jit->reuse(op1.gpr()); + else + m_gpr = m_jit->allocate(); +} + +GPRTemporary::GPRTemporary(SpeculativeJIT* jit, SpeculateIntegerOperand& op1, SpeculateIntegerOperand& op2) + : m_jit(jit) + , m_gpr(InvalidGPRReg) +{ + 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(SpeculativeJIT* jit, SpeculateStrictInt32Operand& op1) + : m_jit(jit) + , m_gpr(InvalidGPRReg) +{ + if (m_jit->canReuse(op1.index())) + m_gpr = m_jit->reuse(op1.gpr()); + else + m_gpr = m_jit->allocate(); +} + +GPRTemporary::GPRTemporary(SpeculativeJIT* jit, IntegerOperand& op1) + : m_jit(jit) + , m_gpr(InvalidGPRReg) +{ + if (m_jit->canReuse(op1.index())) + m_gpr = m_jit->reuse(op1.gpr()); + else + m_gpr = m_jit->allocate(); +} + +GPRTemporary::GPRTemporary(SpeculativeJIT* jit, IntegerOperand& op1, IntegerOperand& op2) + : m_jit(jit) + , m_gpr(InvalidGPRReg) +{ + 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(SpeculativeJIT* jit, SpeculateCellOperand& op1) + : m_jit(jit) + , m_gpr(InvalidGPRReg) +{ + if (m_jit->canReuse(op1.index())) + m_gpr = m_jit->reuse(op1.gpr()); + else + m_gpr = m_jit->allocate(); +} + +GPRTemporary::GPRTemporary(SpeculativeJIT* jit, SpeculateBooleanOperand& op1) + : m_jit(jit) + , m_gpr(InvalidGPRReg) +{ + if (m_jit->canReuse(op1.index())) + m_gpr = m_jit->reuse(op1.gpr()); + else + m_gpr = m_jit->allocate(); +} + +#if USE(JSVALUE64) +GPRTemporary::GPRTemporary(SpeculativeJIT* jit, JSValueOperand& op1) + : m_jit(jit) + , m_gpr(InvalidGPRReg) +{ + if (m_jit->canReuse(op1.index())) + m_gpr = m_jit->reuse(op1.gpr()); + else + m_gpr = m_jit->allocate(); +} +#else +GPRTemporary::GPRTemporary(SpeculativeJIT* jit, JSValueOperand& op1, bool tag) + : m_jit(jit) + , m_gpr(InvalidGPRReg) +{ + if (!op1.isDouble() && m_jit->canReuse(op1.index())) + m_gpr = m_jit->reuse(tag ? op1.tagGPR() : op1.payloadGPR()); + else + m_gpr = m_jit->allocate(); +} +#endif + +GPRTemporary::GPRTemporary(SpeculativeJIT* jit, StorageOperand& op1) + : m_jit(jit) + , m_gpr(InvalidGPRReg) +{ + if (m_jit->canReuse(op1.index())) + m_gpr = m_jit->reuse(op1.gpr()); + else + m_gpr = m_jit->allocate(); +} + +void GPRTemporary::adopt(GPRTemporary& other) +{ + ASSERT(!m_jit); + ASSERT(m_gpr == InvalidGPRReg); + ASSERT(other.m_jit); + ASSERT(other.m_gpr != InvalidGPRReg); + m_jit = other.m_jit; + m_gpr = other.m_gpr; + other.m_jit = 0; + other.m_gpr = InvalidGPRReg; +} + +FPRTemporary::FPRTemporary(SpeculativeJIT* jit) + : m_jit(jit) + , m_fpr(InvalidFPRReg) +{ + m_fpr = m_jit->fprAllocate(); +} + +FPRTemporary::FPRTemporary(SpeculativeJIT* jit, DoubleOperand& op1) + : m_jit(jit) + , m_fpr(InvalidFPRReg) +{ + if (m_jit->canReuse(op1.index())) + m_fpr = m_jit->reuse(op1.fpr()); + else + m_fpr = m_jit->fprAllocate(); +} + +FPRTemporary::FPRTemporary(SpeculativeJIT* jit, DoubleOperand& op1, DoubleOperand& op2) + : m_jit(jit) + , m_fpr(InvalidFPRReg) +{ + 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(); +} + +FPRTemporary::FPRTemporary(SpeculativeJIT* jit, SpeculateDoubleOperand& op1) + : m_jit(jit) + , m_fpr(InvalidFPRReg) +{ + if (m_jit->canReuse(op1.index())) + m_fpr = m_jit->reuse(op1.fpr()); + else + m_fpr = m_jit->fprAllocate(); +} + +FPRTemporary::FPRTemporary(SpeculativeJIT* jit, SpeculateDoubleOperand& op1, SpeculateDoubleOperand& op2) + : m_jit(jit) + , m_fpr(InvalidFPRReg) +{ + 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(); +} + +#if USE(JSVALUE32_64) +FPRTemporary::FPRTemporary(SpeculativeJIT* jit, JSValueOperand& op1) + : m_jit(jit) + , m_fpr(InvalidFPRReg) +{ + if (op1.isDouble() && m_jit->canReuse(op1.index())) + m_fpr = m_jit->reuse(op1.fpr()); + else + m_fpr = m_jit->fprAllocate(); +} +#endif + +#ifndef NDEBUG +void ValueSource::dump(FILE* out) const +{ + switch (kind()) { + case SourceNotSet: + fprintf(out, "NotSet"); + break; + case ValueInRegisterFile: + fprintf(out, "InRegFile"); + break; + case Int32InRegisterFile: + fprintf(out, "Int32"); + break; + case CellInRegisterFile: + fprintf(out, "Cell"); + break; + case BooleanInRegisterFile: + fprintf(out, "Bool"); + break; + case DoubleInRegisterFile: + fprintf(out, "Double"); + break; + case HaveNode: + fprintf(out, "Node(%d)", m_nodeIndex); + break; + } +} +#endif + +void SpeculativeJIT::compilePeepHoleDoubleBranch(Node& node, NodeIndex branchNodeIndex, JITCompiler::DoubleCondition condition) +{ + Node& branchNode = at(branchNodeIndex); + BlockIndex taken = branchNode.takenBlockIndex(); + BlockIndex notTaken = branchNode.notTakenBlockIndex(); + + SpeculateDoubleOperand op1(this, node.child1()); + SpeculateDoubleOperand op2(this, node.child2()); + + addBranch(m_jit.branchDouble(condition, op1.fpr(), op2.fpr()), taken); + + if (notTaken != (m_block + 1)) + addBranch(m_jit.jump(), notTaken); +} + +void SpeculativeJIT::compilePeepHoleObjectEquality(Node& node, NodeIndex branchNodeIndex, const ClassInfo* classInfo, PredictionChecker predictionCheck) +{ + Node& branchNode = at(branchNodeIndex); + BlockIndex taken = branchNode.takenBlockIndex(); + BlockIndex notTaken = branchNode.notTakenBlockIndex(); + + MacroAssembler::RelationalCondition condition = MacroAssembler::Equal; + + if (taken == (m_block + 1)) { + condition = MacroAssembler::NotEqual; + BlockIndex tmp = taken; + taken = notTaken; + notTaken = tmp; + } + + SpeculateCellOperand op1(this, node.child1()); + SpeculateCellOperand op2(this, node.child2()); + + GPRReg op1GPR = op1.gpr(); + GPRReg op2GPR = op2.gpr(); + + if (!predictionCheck(m_state.forNode(node.child1()).m_type)) + speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(op1GPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(classInfo))); + if (!predictionCheck(m_state.forNode(node.child2()).m_type)) + speculationCheck(BadType, JSValueSource::unboxedCell(op2GPR), node.child2(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(op2GPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(classInfo))); + + addBranch(m_jit.branchPtr(condition, op1GPR, op2GPR), taken); + if (notTaken != (m_block + 1)) + addBranch(m_jit.jump(), notTaken); +} + +void SpeculativeJIT::compilePeepHoleIntegerBranch(Node& node, NodeIndex branchNodeIndex, JITCompiler::RelationalCondition condition) +{ + Node& branchNode = at(branchNodeIndex); + BlockIndex taken = branchNode.takenBlockIndex(); + BlockIndex notTaken = branchNode.notTakenBlockIndex(); + + // The branch instruction will branch to the taken block. + // If taken is next, switch taken with notTaken & invert the branch condition so we can fall through. + if (taken == (m_block + 1)) { + condition = JITCompiler::invert(condition); + BlockIndex tmp = taken; + taken = notTaken; + notTaken = tmp; + } + + if (isInt32Constant(node.child1())) { + int32_t imm = valueOfInt32Constant(node.child1()); + SpeculateIntegerOperand op2(this, node.child2()); + addBranch(m_jit.branch32(condition, JITCompiler::Imm32(imm), op2.gpr()), taken); + } else if (isInt32Constant(node.child2())) { + SpeculateIntegerOperand op1(this, node.child1()); + int32_t imm = valueOfInt32Constant(node.child2()); + addBranch(m_jit.branch32(condition, op1.gpr(), JITCompiler::Imm32(imm)), taken); + } else { + SpeculateIntegerOperand op1(this, node.child1()); + SpeculateIntegerOperand op2(this, node.child2()); + addBranch(m_jit.branch32(condition, op1.gpr(), op2.gpr()), taken); + } + + // Check for fall through, otherwise we need to jump. + if (notTaken != (m_block + 1)) + addBranch(m_jit.jump(), notTaken); +} + +// Returns true if the compare is fused with a subsequent branch. +bool SpeculativeJIT::compilePeepHoleBranch(Node& node, MacroAssembler::RelationalCondition condition, MacroAssembler::DoubleCondition doubleCondition, S_DFGOperation_EJJ operation) +{ + // Fused compare & branch. + NodeIndex branchNodeIndex = detectPeepHoleBranch(); + if (branchNodeIndex != NoNode) { + // detectPeepHoleBranch currently only permits the branch to be the very next node, + // so can be no intervening nodes to also reference the compare. + ASSERT(node.adjustedRefCount() == 1); + + if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2()))) { + compilePeepHoleIntegerBranch(node, branchNodeIndex, condition); + use(node.child1()); + use(node.child2()); + } else if (Node::shouldSpeculateNumber(at(node.child1()), at(node.child2()))) { + compilePeepHoleDoubleBranch(node, branchNodeIndex, doubleCondition); + use(node.child1()); + use(node.child2()); + } else if (node.op == CompareEq && Node::shouldSpeculateFinalObject(at(node.child1()), at(node.child2()))) { + compilePeepHoleObjectEquality(node, branchNodeIndex, &JSFinalObject::s_info, isFinalObjectPrediction); + use(node.child1()); + use(node.child2()); + } else if (node.op == CompareEq && Node::shouldSpeculateArray(at(node.child1()), at(node.child2()))) { + compilePeepHoleObjectEquality(node, branchNodeIndex, &JSArray::s_info, isArrayPrediction); + use(node.child1()); + use(node.child2()); + } else + nonSpeculativePeepholeBranch(node, branchNodeIndex, condition, operation); + + m_compileIndex = branchNodeIndex; + return true; + } + return false; +} + +void SpeculativeJIT::compileMovHint(Node& node) +{ + ASSERT(node.op == SetLocal); + + setNodeIndexForOperand(node.child1(), node.local()); + m_lastSetOperand = node.local(); +} + +void SpeculativeJIT::compile(BasicBlock& block) +{ + ASSERT(m_compileOkay); + ASSERT(m_compileIndex == block.begin); + + if (!block.isReachable) { + m_compileIndex = block.end; + return; + } + + m_blockHeads[m_block] = m_jit.label(); +#if DFG_ENABLE(JIT_BREAK_ON_EVERY_BLOCK) + m_jit.breakpoint(); +#endif + + ASSERT(m_arguments.size() == block.variablesAtHead.numberOfArguments()); + for (size_t i = 0; i < m_arguments.size(); ++i) { + NodeIndex nodeIndex = block.variablesAtHead.argument(i); + if (nodeIndex == NoNode) + m_arguments[i] = ValueSource(ValueInRegisterFile); + else + m_arguments[i] = ValueSource::forPrediction(at(nodeIndex).variableAccessData()->prediction()); + } + + m_state.reset(); + m_state.beginBasicBlock(&block); + + ASSERT(m_variables.size() == block.variablesAtHead.numberOfLocals()); + for (size_t i = 0; i < m_variables.size(); ++i) { + NodeIndex nodeIndex = block.variablesAtHead.local(i); + if (nodeIndex == NoNode) + m_variables[i] = ValueSource(ValueInRegisterFile); + else if (at(nodeIndex).variableAccessData()->shouldUseDoubleFormat()) + m_variables[i] = ValueSource(DoubleInRegisterFile); + else + m_variables[i] = ValueSource::forPrediction(at(nodeIndex).variableAccessData()->prediction()); + } + + m_lastSetOperand = std::numeric_limits<int>::max(); + m_codeOriginForOSR = CodeOrigin(); + + for (; m_compileIndex < block.end; ++m_compileIndex) { + Node& node = at(m_compileIndex); + m_codeOriginForOSR = node.codeOrigin; + if (!node.shouldGenerate()) { +#if DFG_ENABLE(DEBUG_VERBOSE) + fprintf(stderr, "SpeculativeJIT skipping Node @%d (bc#%u) at JIT offset 0x%x ", (int)m_compileIndex, node.codeOrigin.bytecodeIndex, m_jit.debugOffset()); +#endif + switch (node.op) { + case SetLocal: + compileMovHint(node); + break; + + case InlineStart: { + InlineCallFrame* inlineCallFrame = node.codeOrigin.inlineCallFrame; + int argumentCountIncludingThis = inlineCallFrame->arguments.size(); + for (int i = 0; i < argumentCountIncludingThis; ++i) { + ValueRecovery recovery = computeValueRecoveryFor(m_variables[inlineCallFrame->stackOffset + CallFrame::argumentOffsetIncludingThis(i)]); + // The recovery cannot point to registers, since the call frame reification isn't + // as smart as OSR, so it can't handle that. The exception is the this argument, + // which we don't really need to be able to recover. + ASSERT(!i || !recovery.isInRegisters()); + inlineCallFrame->arguments[i] = recovery; + } + break; + } + + default: + break; + } + } else { + +#if DFG_ENABLE(DEBUG_VERBOSE) + fprintf(stderr, "SpeculativeJIT generating Node @%d (bc#%u) at JIT offset 0x%x ", (int)m_compileIndex, node.codeOrigin.bytecodeIndex, m_jit.debugOffset()); +#endif +#if DFG_ENABLE(JIT_BREAK_ON_EVERY_NODE) + m_jit.breakpoint(); +#endif +#if DFG_ENABLE(XOR_DEBUG_AID) + m_jit.xorPtr(JITCompiler::TrustedImm32(m_compileIndex), GPRInfo::regT0); + m_jit.xorPtr(JITCompiler::TrustedImm32(m_compileIndex), GPRInfo::regT0); +#endif + checkConsistency(); + compile(node); + if (!m_compileOkay) { + m_compileOkay = true; + m_compileIndex = block.end; + clearGenerationInfo(); + return; + } + +#if DFG_ENABLE(DEBUG_VERBOSE) + if (node.hasResult()) { + GenerationInfo& info = m_generationInfo[node.virtualRegister()]; + fprintf(stderr, "-> %s, vr#%d", dataFormatToString(info.registerFormat()), (int)node.virtualRegister()); + if (info.registerFormat() != DataFormatNone) { + if (info.registerFormat() == DataFormatDouble) + fprintf(stderr, ", %s", FPRInfo::debugName(info.fpr())); +#if USE(JSVALUE32_64) + else if (info.registerFormat() & DataFormatJS) + fprintf(stderr, ", %s %s", GPRInfo::debugName(info.tagGPR()), GPRInfo::debugName(info.payloadGPR())); +#endif + else + fprintf(stderr, ", %s", GPRInfo::debugName(info.gpr())); + } + fprintf(stderr, " "); + } else + fprintf(stderr, " "); +#endif + } + +#if DFG_ENABLE(VERBOSE_VALUE_RECOVERIES) + for (size_t i = 0; i < m_arguments.size(); ++i) + computeValueRecoveryFor(argumentToOperand(i)).dump(stderr); + + fprintf(stderr, " : "); + + for (int operand = 0; operand < (int)m_variables.size(); ++operand) + computeValueRecoveryFor(operand).dump(stderr); +#endif + +#if DFG_ENABLE(DEBUG_VERBOSE) + fprintf(stderr, "\n"); +#endif + + // Make sure that the abstract state is rematerialized for the next node. + m_state.execute(m_compileIndex); + + if (node.shouldGenerate()) + checkConsistency(); + } + + // Perform the most basic verification that children have been used correctly. +#if !ASSERT_DISABLED + for (unsigned index = 0; index < m_generationInfo.size(); ++index) { + GenerationInfo& info = m_generationInfo[index]; + ASSERT(!info.alive()); + } +#endif +} + +// If we are making type predictions about our arguments then +// we need to check that they are correct on function entry. +void SpeculativeJIT::checkArgumentTypes() +{ + ASSERT(!m_compileIndex); + m_codeOriginForOSR = CodeOrigin(0); + + for (size_t i = 0; i < m_arguments.size(); ++i) + m_arguments[i] = ValueSource(ValueInRegisterFile); + for (size_t i = 0; i < m_variables.size(); ++i) + m_variables[i] = ValueSource(ValueInRegisterFile); + + for (int i = 0; i < m_jit.codeBlock()->m_numParameters; ++i) { + VariableAccessData* variableAccessData = at(m_jit.graph().m_arguments[i]).variableAccessData(); + VirtualRegister virtualRegister = variableAccessData->local(); + PredictedType predictedType = variableAccessData->prediction(); +#if USE(JSVALUE64) + if (isInt32Prediction(predictedType)) + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::Below, JITCompiler::addressFor(virtualRegister), GPRInfo::tagTypeNumberRegister)); + else if (isArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); + } else if (isByteArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSByteArray::s_info))); + } else if (isBooleanPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); + m_jit.xorPtr(TrustedImm32(static_cast<int32_t>(ValueFalse)), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), TrustedImm32(static_cast<int32_t>(~1)))); + } else if (isInt8ArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->int8ArrayDescriptor().m_classInfo))); + } else if (isInt16ArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->int16ArrayDescriptor().m_classInfo))); + } else if (isInt32ArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->int32ArrayDescriptor().m_classInfo))); + } else if (isUint8ArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint8ArrayDescriptor().m_classInfo))); + } else if (isUint16ArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint16ArrayDescriptor().m_classInfo))); + } else if (isUint32ArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint32ArrayDescriptor().m_classInfo))); + } else if (isFloat32ArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->float32ArrayDescriptor().m_classInfo))); + } else if (isFloat64ArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->float64ArrayDescriptor().m_classInfo))); + } +#else + if (isInt32Prediction(predictedType)) + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::Int32Tag))); + else if (isArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); + m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); + } else if (isByteArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); + m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSByteArray::s_info))); + } else if (isBooleanPrediction(predictedType)) + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::BooleanTag))); + else if (isInt8ArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); + m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->int8ArrayDescriptor().m_classInfo))); + } else if (isInt16ArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); + m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->int16ArrayDescriptor().m_classInfo))); + } else if (isInt32ArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); + m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->int32ArrayDescriptor().m_classInfo))); + } else if (isUint8ArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); + m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint8ArrayDescriptor().m_classInfo))); + } else if (isUint16ArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); + m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint16ArrayDescriptor().m_classInfo))); + } else if (isUint32ArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); + m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint32ArrayDescriptor().m_classInfo))); + } else if (isFloat32ArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); + m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->float32ArrayDescriptor().m_classInfo))); + } else if (isFloat64ArrayPrediction(predictedType)) { + GPRTemporary temp(this); + m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); + m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->float64ArrayDescriptor().m_classInfo))); + } +#endif + } +} + +bool SpeculativeJIT::compile() +{ + checkArgumentTypes(); + + ASSERT(!m_compileIndex); + for (m_block = 0; m_block < m_jit.graph().m_blocks.size(); ++m_block) + compile(*m_jit.graph().m_blocks[m_block]); + linkBranches(); + return true; +} + +void SpeculativeJIT::linkOSREntries(LinkBuffer& linkBuffer) +{ + for (BlockIndex blockIndex = 0; blockIndex < m_jit.graph().m_blocks.size(); ++blockIndex) { + BasicBlock& block = *m_jit.graph().m_blocks[blockIndex]; + if (block.isOSRTarget) + m_jit.noticeOSREntry(block, m_blockHeads[blockIndex], linkBuffer); + } +} + +ValueRecovery SpeculativeJIT::computeValueRecoveryFor(const ValueSource& valueSource) +{ + switch (valueSource.kind()) { + case ValueInRegisterFile: + return ValueRecovery::alreadyInRegisterFile(); + + case Int32InRegisterFile: + return ValueRecovery::alreadyInRegisterFileAsUnboxedInt32(); + + case CellInRegisterFile: + return ValueRecovery::alreadyInRegisterFileAsUnboxedCell(); + + case BooleanInRegisterFile: + return ValueRecovery::alreadyInRegisterFileAsUnboxedBoolean(); + + case DoubleInRegisterFile: + return ValueRecovery::alreadyInRegisterFileAsUnboxedDouble(); + + case HaveNode: { + if (m_jit.isConstant(valueSource.nodeIndex())) + return ValueRecovery::constant(m_jit.valueOfJSConstant(valueSource.nodeIndex())); + + Node* nodePtr = &at(valueSource.nodeIndex()); + if (!nodePtr->shouldGenerate()) { + // It's legitimately dead. As in, nobody will ever use this node, or operand, + // ever. Set it to Undefined to make the GC happy after the OSR. + return ValueRecovery::constant(jsUndefined()); + } + + GenerationInfo* infoPtr = &m_generationInfo[nodePtr->virtualRegister()]; + if (!infoPtr->alive() || infoPtr->nodeIndex() != valueSource.nodeIndex()) { + // Try to see if there is an alternate node that would contain the value we want. + // There are four possibilities: + // + // ValueToNumber: If the only live version of the value is a ValueToNumber node + // then it means that all remaining uses of the value would have performed a + // ValueToNumber conversion anyway. Thus, we can substitute ValueToNumber. + // + // ValueToInt32: Likewise, if the only remaining live version of the value is + // ValueToInt32, then we can use it. But if there is both a ValueToInt32 + // and a ValueToNumber, then we better go with ValueToNumber because it + // means that some remaining uses would have converted to number while + // others would have converted to Int32. + // + // UInt32ToNumber: If the only live version of the value is a UInt32ToNumber + // then the only remaining uses are ones that want a properly formed number + // rather than a UInt32 intermediate. + // + // The reverse of the above: This node could be a UInt32ToNumber, but its + // alternative is still alive. This means that the only remaining uses of + // the number would be fine with a UInt32 intermediate. + + bool found = false; + + if (nodePtr->op == UInt32ToNumber) { + NodeIndex nodeIndex = nodePtr->child1(); + nodePtr = &at(nodeIndex); + infoPtr = &m_generationInfo[nodePtr->virtualRegister()]; + if (infoPtr->alive() && infoPtr->nodeIndex() == nodeIndex) + found = true; + } + + if (!found) { + NodeIndex valueToNumberIndex = NoNode; + NodeIndex valueToInt32Index = NoNode; + NodeIndex uint32ToNumberIndex = NoNode; + + for (unsigned virtualRegister = 0; virtualRegister < m_generationInfo.size(); ++virtualRegister) { + GenerationInfo& info = m_generationInfo[virtualRegister]; + if (!info.alive()) + continue; + if (info.nodeIndex() == NoNode) + continue; + Node& node = at(info.nodeIndex()); + if (node.child1Unchecked() != valueSource.nodeIndex()) + continue; + switch (node.op) { + case ValueToNumber: + case ValueToDouble: + valueToNumberIndex = info.nodeIndex(); + break; + case ValueToInt32: + valueToInt32Index = info.nodeIndex(); + break; + case UInt32ToNumber: + uint32ToNumberIndex = info.nodeIndex(); + break; + default: + break; + } + } + + NodeIndex nodeIndexToUse; + if (valueToNumberIndex != NoNode) + nodeIndexToUse = valueToNumberIndex; + else if (valueToInt32Index != NoNode) + nodeIndexToUse = valueToInt32Index; + else if (uint32ToNumberIndex != NoNode) + nodeIndexToUse = uint32ToNumberIndex; + else + nodeIndexToUse = NoNode; + + if (nodeIndexToUse != NoNode) { + nodePtr = &at(nodeIndexToUse); + infoPtr = &m_generationInfo[nodePtr->virtualRegister()]; + ASSERT(infoPtr->alive() && infoPtr->nodeIndex() == nodeIndexToUse); + found = true; + } + } + + if (!found) + return ValueRecovery::constant(jsUndefined()); + } + + ASSERT(infoPtr->alive()); + + if (infoPtr->registerFormat() != DataFormatNone) { + if (infoPtr->registerFormat() == DataFormatDouble) + return ValueRecovery::inFPR(infoPtr->fpr()); +#if USE(JSVALUE32_64) + if (infoPtr->registerFormat() & DataFormatJS) + return ValueRecovery::inPair(infoPtr->tagGPR(), infoPtr->payloadGPR()); +#endif + return ValueRecovery::inGPR(infoPtr->gpr(), infoPtr->registerFormat()); + } + if (infoPtr->spillFormat() != DataFormatNone) + return ValueRecovery::displacedInRegisterFile(static_cast<VirtualRegister>(nodePtr->virtualRegister()), infoPtr->spillFormat()); + + ASSERT_NOT_REACHED(); + return ValueRecovery(); + } + + default: + ASSERT_NOT_REACHED(); + return ValueRecovery(); + } +} + +void SpeculativeJIT::compileGetCharCodeAt(Node& node) +{ + ASSERT(node.child3() == NoNode); + SpeculateCellOperand string(this, node.child1()); + SpeculateStrictInt32Operand index(this, node.child2()); + StorageOperand storage(this, node.child3()); + + GPRReg stringReg = string.gpr(); + GPRReg indexReg = index.gpr(); + GPRReg storageReg = storage.gpr(); + + if (!isStringPrediction(m_state.forNode(node.child1()).m_type)) { + ASSERT(!(at(node.child1()).prediction() & PredictString)); + terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); + noResult(m_compileIndex); + return; + } + + // unsigned comparison so we can filter out negative indices and indices that are too large + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, indexReg, MacroAssembler::Address(stringReg, JSString::offsetOfLength()))); + + GPRTemporary scratch(this); + GPRReg scratchReg = scratch.gpr(); + + m_jit.loadPtr(MacroAssembler::Address(stringReg, JSString::offsetOfValue()), scratchReg); + + // Load the character into scratchReg + JITCompiler::Jump is16Bit = m_jit.branchTest32(MacroAssembler::Zero, MacroAssembler::Address(scratchReg, StringImpl::flagsOffset()), TrustedImm32(StringImpl::flagIs8Bit())); + + m_jit.load8(MacroAssembler::BaseIndex(storageReg, indexReg, MacroAssembler::TimesOne, 0), scratchReg); + JITCompiler::Jump cont8Bit = m_jit.jump(); + + is16Bit.link(&m_jit); + + m_jit.load16(MacroAssembler::BaseIndex(storageReg, indexReg, MacroAssembler::TimesTwo, 0), scratchReg); + + cont8Bit.link(&m_jit); + + integerResult(scratchReg, m_compileIndex); +} + +void SpeculativeJIT::compileGetByValOnString(Node& node) +{ + SpeculateCellOperand base(this, node.child1()); + SpeculateStrictInt32Operand property(this, node.child2()); + StorageOperand storage(this, node.child3()); + GPRReg baseReg = base.gpr(); + GPRReg propertyReg = property.gpr(); + GPRReg storageReg = storage.gpr(); + + if (!isStringPrediction(m_state.forNode(node.child1()).m_type)) { + ASSERT(!(at(node.child1()).prediction() & PredictString)); + terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); + noResult(m_compileIndex); + return; + } + + // unsigned comparison so we can filter out negative indices and indices that are too large + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSString::offsetOfLength()))); + + GPRTemporary scratch(this); + GPRReg scratchReg = scratch.gpr(); + + m_jit.loadPtr(MacroAssembler::Address(baseReg, JSString::offsetOfValue()), scratchReg); + + // Load the character into scratchReg + JITCompiler::Jump is16Bit = m_jit.branchTest32(MacroAssembler::Zero, MacroAssembler::Address(scratchReg, StringImpl::flagsOffset()), TrustedImm32(StringImpl::flagIs8Bit())); + + m_jit.load8(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesOne, 0), scratchReg); + JITCompiler::Jump cont8Bit = m_jit.jump(); + + is16Bit.link(&m_jit); + + m_jit.load16(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesTwo, 0), scratchReg); + + // We only support ascii characters + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, scratchReg, TrustedImm32(0x100))); + + // 8 bit string values don't need the isASCII check. + cont8Bit.link(&m_jit); + + GPRTemporary smallStrings(this); + GPRReg smallStringsReg = smallStrings.gpr(); + m_jit.move(MacroAssembler::TrustedImmPtr(m_jit.globalData()->smallStrings.singleCharacterStrings()), smallStringsReg); + m_jit.loadPtr(MacroAssembler::BaseIndex(smallStringsReg, scratchReg, MacroAssembler::ScalePtr, 0), scratchReg); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchTest32(MacroAssembler::Zero, scratchReg)); + cellResult(scratchReg, m_compileIndex); +} + +void SpeculativeJIT::compileValueToInt32(Node& node) +{ + if (at(node.child1()).shouldNotSpeculateInteger()) { + if (at(node.child1()).shouldSpeculateDouble()) { + SpeculateDoubleOperand op1(this, node.child1()); + GPRTemporary result(this); + FPRReg fpr = op1.fpr(); + GPRReg gpr = result.gpr(); + JITCompiler::Jump truncatedToInteger = m_jit.branchTruncateDoubleToInt32(fpr, gpr, JITCompiler::BranchIfTruncateSuccessful); + + silentSpillAllRegisters(gpr); + callOperation(toInt32, gpr, fpr); + silentFillAllRegisters(gpr); + + truncatedToInteger.link(&m_jit); + integerResult(gpr, m_compileIndex); + return; + } + // Do it the safe way. + nonSpeculativeValueToInt32(node); + return; + } + + SpeculateIntegerOperand op1(this, node.child1()); + GPRTemporary result(this, op1); + m_jit.move(op1.gpr(), result.gpr()); + integerResult(result.gpr(), m_compileIndex, op1.format()); +} + +void SpeculativeJIT::compileUInt32ToNumber(Node& node) +{ + if (!nodeCanSpeculateInteger(node.arithNodeFlags())) { + // We know that this sometimes produces doubles. So produce a double every + // time. This at least allows subsequent code to not have weird conditionals. + + IntegerOperand op1(this, node.child1()); + FPRTemporary result(this); + + GPRReg inputGPR = op1.gpr(); + FPRReg outputFPR = result.fpr(); + + m_jit.convertInt32ToDouble(inputGPR, outputFPR); + + JITCompiler::Jump positive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, inputGPR, TrustedImm32(0)); + m_jit.addDouble(JITCompiler::AbsoluteAddress(&AssemblyHelpers::twoToThe32), outputFPR); + positive.link(&m_jit); + + doubleResult(outputFPR, m_compileIndex); + return; + } + + IntegerOperand op1(this, node.child1()); + GPRTemporary result(this, op1); + + // Test the operand is positive. This is a very special speculation check - we actually + // use roll-forward speculation here, where if this fails, we jump to the baseline + // instruction that follows us, rather than the one we're executing right now. We have + // to do this because by this point, the original values necessary to compile whatever + // operation the UInt32ToNumber originated from might be dead. + speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::LessThan, op1.gpr(), TrustedImm32(0))); + + // Verify that we can do roll forward. + ASSERT(at(m_compileIndex + 1).op == SetLocal); + ASSERT(at(m_compileIndex + 1).codeOrigin == node.codeOrigin); + ASSERT(at(m_compileIndex + 2).codeOrigin != node.codeOrigin); + + // Now do the magic. + OSRExit& exit = m_jit.codeBlock()->lastOSRExit(); + Node& setLocal = at(m_compileIndex + 1); + exit.m_codeOrigin = at(m_compileIndex + 2).codeOrigin; + exit.m_lastSetOperand = setLocal.local(); + + // Create the value recovery, and stuff it into the right place. + exit.valueRecoveryForOperand(setLocal.local()) = ValueRecovery::uint32InGPR(op1.gpr()); + + m_jit.move(op1.gpr(), result.gpr()); + integerResult(result.gpr(), m_compileIndex, op1.format()); +} + +static void compileClampDoubleToByte(JITCompiler& jit, GPRReg result, FPRReg source, FPRReg scratch) +{ + // Unordered compare so we pick up NaN + static const double zero = 0; + static const double byteMax = 255; + static const double half = 0.5; + jit.loadDouble(&zero, scratch); + MacroAssembler::Jump tooSmall = jit.branchDouble(MacroAssembler::DoubleLessThanOrEqualOrUnordered, source, scratch); + jit.loadDouble(&byteMax, scratch); + MacroAssembler::Jump tooBig = jit.branchDouble(MacroAssembler::DoubleGreaterThan, source, scratch); + + jit.loadDouble(&half, scratch); + // FIXME: This should probably just use a floating point round! + // https://bugs.webkit.org/show_bug.cgi?id=72054 + jit.addDouble(source, scratch); + jit.truncateDoubleToInt32(scratch, result); + MacroAssembler::Jump truncatedInt = jit.jump(); + + tooSmall.link(&jit); + jit.xorPtr(result, result); + MacroAssembler::Jump zeroed = jit.jump(); + + tooBig.link(&jit); + jit.move(JITCompiler::TrustedImm32(255), result); + + truncatedInt.link(&jit); + zeroed.link(&jit); + +} + +void SpeculativeJIT::compilePutByValForByteArray(GPRReg base, GPRReg property, Node& node) +{ + NodeIndex baseIndex = node.child1(); + NodeIndex valueIndex = node.child3(); + + if (!isByteArrayPrediction(m_state.forNode(baseIndex).m_type)) + speculationCheck(BadType, JSValueSource::unboxedCell(base), baseIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(base, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSByteArray::s_info))); + GPRTemporary value; + GPRReg valueGPR; + + if (at(valueIndex).isConstant()) { + JSValue jsValue = valueOfJSConstant(valueIndex); + if (!jsValue.isNumber()) { + terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); + noResult(m_compileIndex); + return; + } + double d = jsValue.asNumber(); + d += 0.5; + if (!(d > 0)) + d = 0; + else if (d > 255) + d = 255; + GPRTemporary scratch(this); + GPRReg scratchReg = scratch.gpr(); + m_jit.move(Imm32((int)d), scratchReg); + value.adopt(scratch); + valueGPR = scratchReg; + } else if (!at(valueIndex).shouldNotSpeculateInteger()) { + SpeculateIntegerOperand valueOp(this, valueIndex); + GPRTemporary scratch(this); + GPRReg scratchReg = scratch.gpr(); + m_jit.move(valueOp.gpr(), scratchReg); + MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::BelowOrEqual, scratchReg, TrustedImm32(0xff)); + MacroAssembler::Jump tooBig = m_jit.branch32(MacroAssembler::GreaterThan, scratchReg, TrustedImm32(0xff)); + m_jit.xorPtr(scratchReg, scratchReg); + MacroAssembler::Jump clamped = m_jit.jump(); + tooBig.link(&m_jit); + m_jit.move(TrustedImm32(255), scratchReg); + clamped.link(&m_jit); + inBounds.link(&m_jit); + value.adopt(scratch); + valueGPR = scratchReg; + } else { + SpeculateDoubleOperand valueOp(this, valueIndex); + GPRTemporary result(this); + FPRTemporary floatScratch(this); + FPRReg fpr = valueOp.fpr(); + GPRReg gpr = result.gpr(); + compileClampDoubleToByte(m_jit, gpr, fpr, floatScratch.fpr()); + value.adopt(result); + valueGPR = gpr; + } + ASSERT_UNUSED(valueGPR, valueGPR != property); + ASSERT(valueGPR != base); + GPRTemporary storage(this); + GPRReg storageReg = storage.gpr(); + ASSERT(valueGPR != storageReg); + m_jit.loadPtr(MacroAssembler::Address(base, JSByteArray::offsetOfStorage()), storageReg); + MacroAssembler::Jump outOfBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, property, MacroAssembler::Address(storageReg, ByteArray::offsetOfSize())); + m_jit.store8(value.gpr(), MacroAssembler::BaseIndex(storageReg, property, MacroAssembler::TimesOne, ByteArray::offsetOfData())); + outOfBounds.link(&m_jit); + noResult(m_compileIndex); +} + +void SpeculativeJIT::compileGetByValOnByteArray(Node& node) +{ + SpeculateCellOperand base(this, node.child1()); + SpeculateStrictInt32Operand property(this, node.child2()); + + GPRReg baseReg = base.gpr(); + GPRReg propertyReg = property.gpr(); + + if (!isByteArrayPrediction(m_state.forNode(node.child1()).m_type)) { + terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); + noResult(m_compileIndex); + return; + } + + // Load the character into scratchReg + GPRTemporary storage(this); + GPRReg storageReg = storage.gpr(); + m_jit.loadPtr(MacroAssembler::Address(baseReg, JSByteArray::offsetOfStorage()), storageReg); + + // unsigned comparison so we can filter out negative indices and indices that are too large + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, ByteArray::offsetOfSize()))); + + m_jit.load8(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesOne, ByteArray::offsetOfData()), storageReg); + integerResult(storageReg, m_compileIndex); +} + +void SpeculativeJIT::compileGetTypedArrayLength(const TypedArrayDescriptor& descriptor, Node& node, bool needsSpeculationCheck) +{ + SpeculateCellOperand base(this, node.child1()); + GPRTemporary result(this); + + GPRReg baseGPR = base.gpr(); + GPRReg resultGPR = result.gpr(); + + if (needsSpeculationCheck) + speculationCheck(BadType, JSValueSource::unboxedCell(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(descriptor.m_classInfo))); + + m_jit.load32(MacroAssembler::Address(baseGPR, descriptor.m_lengthOffset), resultGPR); + + integerResult(resultGPR, m_compileIndex); +} + +void SpeculativeJIT::compileGetByValOnIntTypedArray(const TypedArrayDescriptor& descriptor, Node& node, size_t elementSize, TypedArraySpeculationRequirements speculationRequirements, TypedArraySignedness signedness) +{ + SpeculateCellOperand base(this, node.child1()); + SpeculateStrictInt32Operand property(this, node.child2()); + StorageOperand storage(this, node.child3()); + + GPRReg baseReg = base.gpr(); + GPRReg propertyReg = property.gpr(); + GPRReg storageReg = storage.gpr(); + + GPRTemporary result(this); + GPRReg resultReg = result.gpr(); + + if (speculationRequirements != NoTypedArrayTypeSpecCheck) { + ASSERT_NOT_REACHED(); + terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); + noResult(m_compileIndex); + return; + } + + MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(baseReg, descriptor.m_lengthOffset)); + m_jit.xorPtr(resultReg, resultReg); + MacroAssembler::Jump outOfBounds = m_jit.jump(); + inBounds.link(&m_jit); + switch (elementSize) { + case 1: + if (signedness == SignedTypedArray) + m_jit.load8Signed(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesOne), resultReg); + else + m_jit.load8(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesOne), resultReg); + break; + case 2: + if (signedness == SignedTypedArray) + m_jit.load16Signed(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesTwo), resultReg); + else + m_jit.load16(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesTwo), resultReg); + break; + case 4: + m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesFour), resultReg); + break; + default: + ASSERT_NOT_REACHED(); + } + outOfBounds.link(&m_jit); + if (elementSize < 4 || signedness == SignedTypedArray) + integerResult(resultReg, m_compileIndex); + else { + FPRTemporary fresult(this); + m_jit.convertInt32ToDouble(resultReg, fresult.fpr()); + JITCompiler::Jump positive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, resultReg, TrustedImm32(0)); + m_jit.addDouble(JITCompiler::AbsoluteAddress(&AssemblyHelpers::twoToThe32), fresult.fpr()); + positive.link(&m_jit); + doubleResult(fresult.fpr(), m_compileIndex); + } +} + +void SpeculativeJIT::compilePutByValForIntTypedArray(const TypedArrayDescriptor& descriptor, GPRReg base, GPRReg property, Node& node, size_t elementSize, TypedArraySpeculationRequirements speculationRequirements, TypedArraySignedness signedness) +{ + NodeIndex baseIndex = node.child1(); + NodeIndex valueIndex = node.child3(); + + if (speculationRequirements != NoTypedArrayTypeSpecCheck) + speculationCheck(BadType, JSValueSource::unboxedCell(base), baseIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(base, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(descriptor.m_classInfo))); + GPRTemporary value; + GPRReg valueGPR; + + if (at(valueIndex).isConstant()) { + JSValue jsValue = valueOfJSConstant(valueIndex); + if (!jsValue.isNumber()) { + terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); + noResult(m_compileIndex); + return; + } + double d = jsValue.asNumber(); + GPRTemporary scratch(this); + GPRReg scratchReg = scratch.gpr(); + m_jit.move(Imm32((int)d), scratchReg); + value.adopt(scratch); + valueGPR = scratchReg; + } else if (!at(valueIndex).shouldNotSpeculateInteger()) { + SpeculateIntegerOperand valueOp(this, valueIndex); + GPRTemporary scratch(this); + GPRReg scratchReg = scratch.gpr(); + m_jit.move(valueOp.gpr(), scratchReg); + value.adopt(scratch); + valueGPR = scratchReg; + } else { + SpeculateDoubleOperand valueOp(this, valueIndex); + GPRTemporary result(this); + FPRReg fpr = valueOp.fpr(); + GPRReg gpr = result.gpr(); + MacroAssembler::Jump notNaN = m_jit.branchDouble(MacroAssembler::DoubleEqual, fpr, fpr); + m_jit.xorPtr(gpr, gpr); + MacroAssembler::Jump fixed = m_jit.jump(); + notNaN.link(&m_jit); + + MacroAssembler::Jump done; + if (signedness == SignedTypedArray) + done = m_jit.branchTruncateDoubleToInt32(fpr, gpr, MacroAssembler::BranchIfTruncateSuccessful); + else + done = m_jit.branchTruncateDoubleToUint32(fpr, gpr, MacroAssembler::BranchIfTruncateSuccessful); + + silentSpillAllRegisters(gpr); + callOperation(toInt32, gpr, fpr); + silentFillAllRegisters(gpr); + + done.link(&m_jit); + fixed.link(&m_jit); + value.adopt(result); + valueGPR = gpr; + } + ASSERT_UNUSED(valueGPR, valueGPR != property); + ASSERT(valueGPR != base); + GPRTemporary storage(this); + GPRReg storageReg = storage.gpr(); + ASSERT(valueGPR != storageReg); + m_jit.loadPtr(MacroAssembler::Address(base, descriptor.m_storageOffset), storageReg); + MacroAssembler::Jump outOfBounds; + if (speculationRequirements != NoTypedArraySpecCheck) + outOfBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, property, MacroAssembler::Address(base, descriptor.m_lengthOffset)); + + switch (elementSize) { + case 1: + m_jit.store8(value.gpr(), MacroAssembler::BaseIndex(storageReg, property, MacroAssembler::TimesOne)); + break; + case 2: + m_jit.store16(value.gpr(), MacroAssembler::BaseIndex(storageReg, property, MacroAssembler::TimesTwo)); + break; + case 4: + m_jit.store32(value.gpr(), MacroAssembler::BaseIndex(storageReg, property, MacroAssembler::TimesFour)); + break; + default: + ASSERT_NOT_REACHED(); + } + if (speculationRequirements != NoTypedArraySpecCheck) + outOfBounds.link(&m_jit); + noResult(m_compileIndex); +} + +void SpeculativeJIT::compileGetByValOnFloatTypedArray(const TypedArrayDescriptor& descriptor, Node& node, size_t elementSize, TypedArraySpeculationRequirements speculationRequirements) +{ + SpeculateCellOperand base(this, node.child1()); + SpeculateStrictInt32Operand property(this, node.child2()); + StorageOperand storage(this, node.child3()); + + GPRReg baseReg = base.gpr(); + GPRReg propertyReg = property.gpr(); + GPRReg storageReg = storage.gpr(); + + if (speculationRequirements != NoTypedArrayTypeSpecCheck) { + ASSERT_NOT_REACHED(); + terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); + noResult(m_compileIndex); + return; + } + + FPRTemporary result(this); + FPRReg resultReg = result.fpr(); + ASSERT(speculationRequirements != NoTypedArraySpecCheck); + MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(baseReg, descriptor.m_lengthOffset)); + static const double zero = 0; + m_jit.loadDouble(&zero, resultReg); + MacroAssembler::Jump outOfBounds = m_jit.jump(); + inBounds.link(&m_jit); + switch (elementSize) { + case 4: + m_jit.loadFloat(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesFour), resultReg); + m_jit.convertFloatToDouble(resultReg, resultReg); + break; + case 8: { + m_jit.loadDouble(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), resultReg); + MacroAssembler::Jump notNaN = m_jit.branchDouble(MacroAssembler::DoubleEqual, resultReg, resultReg); + static const double NaN = std::numeric_limits<double>::quiet_NaN(); + m_jit.loadDouble(&NaN, resultReg); + notNaN.link(&m_jit); + break; + } + default: + ASSERT_NOT_REACHED(); + } + outOfBounds.link(&m_jit); + doubleResult(resultReg, m_compileIndex); +} + +void SpeculativeJIT::compilePutByValForFloatTypedArray(const TypedArrayDescriptor& descriptor, GPRReg base, GPRReg property, Node& node, size_t elementSize, TypedArraySpeculationRequirements speculationRequirements) +{ + NodeIndex baseIndex = node.child1(); + NodeIndex valueIndex = node.child3(); + + SpeculateDoubleOperand valueOp(this, valueIndex); + + if (speculationRequirements != NoTypedArrayTypeSpecCheck) + speculationCheck(BadType, JSValueSource::unboxedCell(base), baseIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(base, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(descriptor.m_classInfo))); + + GPRTemporary result(this); + + GPRTemporary storage(this); + GPRReg storageReg = storage.gpr(); + + m_jit.loadPtr(MacroAssembler::Address(base, descriptor.m_storageOffset), storageReg); + MacroAssembler::Jump outOfBounds; + if (speculationRequirements != NoTypedArraySpecCheck) + outOfBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, property, MacroAssembler::Address(base, descriptor.m_lengthOffset)); + + switch (elementSize) { + case 4: { + FPRTemporary scratch(this); + m_jit.moveDouble(valueOp.fpr(), scratch.fpr()); + m_jit.convertDoubleToFloat(valueOp.fpr(), scratch.fpr()); + m_jit.storeFloat(scratch.fpr(), MacroAssembler::BaseIndex(storageReg, property, MacroAssembler::TimesFour)); + break; + } + case 8: + m_jit.storeDouble(valueOp.fpr(), MacroAssembler::BaseIndex(storageReg, property, MacroAssembler::TimesEight)); + break; + default: + ASSERT_NOT_REACHED(); + } + if (speculationRequirements != NoTypedArraySpecCheck) + outOfBounds.link(&m_jit); + noResult(m_compileIndex); +} + +void SpeculativeJIT::compileInstanceOfForObject(Node&, GPRReg valueReg, GPRReg prototypeReg, GPRReg scratchReg) +{ + // Check that prototype is an object. + m_jit.loadPtr(MacroAssembler::Address(prototypeReg, JSCell::structureOffset()), scratchReg); + speculationCheck(BadType, JSValueRegs(), NoNode, m_jit.branchIfNotObject(scratchReg)); + + // Initialize scratchReg with the value being checked. + m_jit.move(valueReg, scratchReg); + + // Walk up the prototype chain of the value (in scratchReg), comparing to prototypeReg. + MacroAssembler::Label loop(&m_jit); + m_jit.loadPtr(MacroAssembler::Address(scratchReg, JSCell::structureOffset()), scratchReg); +#if USE(JSVALUE64) + m_jit.loadPtr(MacroAssembler::Address(scratchReg, Structure::prototypeOffset()), scratchReg); +#else + m_jit.load32(MacroAssembler::Address(scratchReg, Structure::prototypeOffset() + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), scratchReg); +#endif + MacroAssembler::Jump isInstance = m_jit.branchPtr(MacroAssembler::Equal, scratchReg, prototypeReg); +#if USE(JSVALUE64) + m_jit.branchTestPtr(MacroAssembler::Zero, scratchReg, GPRInfo::tagMaskRegister).linkTo(loop, &m_jit); +#else + m_jit.branchTest32(MacroAssembler::NonZero, scratchReg).linkTo(loop, &m_jit); +#endif + + // No match - result is false. +#if USE(JSVALUE64) + m_jit.move(MacroAssembler::TrustedImmPtr(JSValue::encode(jsBoolean(false))), scratchReg); +#else + m_jit.move(MacroAssembler::TrustedImm32(0), scratchReg); +#endif + MacroAssembler::Jump putResult = m_jit.jump(); + + isInstance.link(&m_jit); +#if USE(JSVALUE64) + m_jit.move(MacroAssembler::TrustedImmPtr(JSValue::encode(jsBoolean(true))), scratchReg); +#else + m_jit.move(MacroAssembler::TrustedImm32(1), scratchReg); +#endif + + putResult.link(&m_jit); +} + +void SpeculativeJIT::compileInstanceOf(Node& node) +{ + if (!!(at(node.child1()).prediction() & ~PredictCell) && !!(m_state.forNode(node.child1()).m_type & ~PredictCell)) { + // It might not be a cell. Speculate less aggressively. + + JSValueOperand value(this, node.child1()); + SpeculateCellOperand prototype(this, node.child3()); + GPRTemporary scratch(this); + + GPRReg prototypeReg = prototype.gpr(); + GPRReg scratchReg = scratch.gpr(); + +#if USE(JSVALUE64) + GPRReg valueReg = value.gpr(); + MacroAssembler::Jump isCell = m_jit.branchTestPtr(MacroAssembler::Zero, valueReg, GPRInfo::tagMaskRegister); + m_jit.move(MacroAssembler::TrustedImmPtr(JSValue::encode(jsBoolean(false))), scratchReg); +#else + GPRReg valueTagReg = value.tagGPR(); + GPRReg valueReg = value.payloadGPR(); + MacroAssembler::Jump isCell = m_jit.branch32(MacroAssembler::Equal, valueTagReg, TrustedImm32(JSValue::CellTag)); + m_jit.move(MacroAssembler::TrustedImm32(0), scratchReg); +#endif + + MacroAssembler::Jump done = m_jit.jump(); + + isCell.link(&m_jit); + + compileInstanceOfForObject(node, valueReg, prototypeReg, scratchReg); + + done.link(&m_jit); + +#if USE(JSVALUE64) + jsValueResult(scratchReg, m_compileIndex, DataFormatJSBoolean); +#else + booleanResult(scratchReg, m_compileIndex); +#endif + return; + } + + SpeculateCellOperand value(this, node.child1()); + // Base unused since we speculate default InstanceOf behaviour in CheckHasInstance. + SpeculateCellOperand prototype(this, node.child3()); + + GPRTemporary scratch(this); + + GPRReg valueReg = value.gpr(); + GPRReg prototypeReg = prototype.gpr(); + GPRReg scratchReg = scratch.gpr(); + + compileInstanceOfForObject(node, valueReg, prototypeReg, scratchReg); + +#if USE(JSVALUE64) + jsValueResult(scratchReg, m_compileIndex, DataFormatJSBoolean); +#else + booleanResult(scratchReg, m_compileIndex); +#endif +} + +static bool isPowerOfTwo(int32_t num) +{ + return num && !(num & (num - 1)); +} + +void SpeculativeJIT::compileSoftModulo(Node& node) +{ + bool shouldGeneratePowerOfTwoCheck = true; + + // In the fast path, the dividend value could be the final result + // (in case of |dividend| < |divisor|), so we speculate it as strict int32. + SpeculateStrictInt32Operand op1(this, node.child1()); + GPRReg op1Gpr = op1.gpr(); + + if (isInt32Constant(node.child2())) { + int32_t divisor = valueOfInt32Constant(node.child2()); + if (divisor < 0) + divisor = -divisor; + + if (isPowerOfTwo(divisor)) { + GPRTemporary result(this); + GPRReg resultGPR = result.gpr(); + m_jit.move(op1Gpr, resultGPR); + JITCompiler::Jump positiveDividend = m_jit.branch32(JITCompiler::GreaterThanOrEqual, op1Gpr, TrustedImm32(0)); + m_jit.neg32(resultGPR); + m_jit.and32(TrustedImm32(divisor - 1), resultGPR); + m_jit.neg32(resultGPR); + JITCompiler::Jump done = m_jit.jump(); + + positiveDividend.link(&m_jit); + m_jit.and32(TrustedImm32(divisor - 1), resultGPR); + + done.link(&m_jit); + integerResult(resultGPR, m_compileIndex); + return; + } +#if CPU(X86) || CPU(X86_64) + if (divisor) { + GPRTemporary eax(this, X86Registers::eax); + GPRTemporary edx(this, X86Registers::edx); + GPRTemporary scratch(this); + GPRReg scratchGPR = scratch.gpr(); + + m_jit.move(op1Gpr, eax.gpr()); + m_jit.move(TrustedImm32(divisor), scratchGPR); + m_jit.assembler().cdq(); + m_jit.assembler().idivl_r(scratchGPR); + integerResult(edx.gpr(), m_compileIndex); + return; + } +#endif + // Fallback to non-constant case but avoid unnecessary checks. + shouldGeneratePowerOfTwoCheck = false; + } + + SpeculateIntegerOperand op2(this, node.child2()); + GPRReg op2Gpr = op2.gpr(); + + speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branchTest32(JITCompiler::Zero, op2Gpr)); + +#if CPU(X86) || CPU(X86_64) + GPRTemporary eax(this, X86Registers::eax); + GPRTemporary edx(this, X86Registers::edx); + GPRReg temp2 = InvalidGPRReg; + if (op2Gpr == X86Registers::eax || op2Gpr == X86Registers::edx) { + temp2 = allocate(); + m_jit.move(op2Gpr, temp2); + op2Gpr = temp2; + } + GPRReg resultGPR = edx.gpr(); + GPRReg scratchGPR = eax.gpr(); +#else + GPRTemporary result(this); + GPRTemporary scratch(this); + GPRTemporary scratch3(this); + GPRReg scratchGPR3 = scratch3.gpr(); + GPRReg resultGPR = result.gpr(); + GPRReg scratchGPR = scratch.gpr(); +#endif + + GPRTemporary scratch2(this); + GPRReg scratchGPR2 = scratch2.gpr(); + JITCompiler::JumpList exitBranch; + + // resultGPR is to hold the ABS value of the dividend before final result is produced + m_jit.move(op1Gpr, resultGPR); + // scratchGPR2 is to hold the ABS value of the divisor + m_jit.move(op2Gpr, scratchGPR2); + + // Check for negative result remainder + // According to ECMA-262, the sign of the result equals the sign of the dividend + JITCompiler::Jump positiveDividend = m_jit.branch32(JITCompiler::GreaterThanOrEqual, op1Gpr, TrustedImm32(0)); + m_jit.neg32(resultGPR); + m_jit.move(TrustedImm32(1), scratchGPR); + JITCompiler::Jump saveCondition = m_jit.jump(); + + positiveDividend.link(&m_jit); + m_jit.move(TrustedImm32(0), scratchGPR); + + // Save the condition for negative remainder + saveCondition.link(&m_jit); + m_jit.push(scratchGPR); + + JITCompiler::Jump positiveDivisor = m_jit.branch32(JITCompiler::GreaterThanOrEqual, op2Gpr, TrustedImm32(0)); + m_jit.neg32(scratchGPR2); + + positiveDivisor.link(&m_jit); + exitBranch.append(m_jit.branch32(JITCompiler::LessThan, resultGPR, scratchGPR2)); + + // Power of two fast case + if (shouldGeneratePowerOfTwoCheck) { + m_jit.move(scratchGPR2, scratchGPR); + m_jit.sub32(TrustedImm32(1), scratchGPR); + JITCompiler::Jump notPowerOfTwo = m_jit.branchTest32(JITCompiler::NonZero, scratchGPR, scratchGPR2); + m_jit.and32(scratchGPR, resultGPR); + exitBranch.append(m_jit.jump()); + + notPowerOfTwo.link(&m_jit); + } + +#if CPU(X86) || CPU(X86_64) + m_jit.move(resultGPR, eax.gpr()); + m_jit.assembler().cdq(); + m_jit.assembler().idivl_r(scratchGPR2); +#elif CPU(ARM_THUMB2) + m_jit.countLeadingZeros32(scratchGPR2, scratchGPR); + m_jit.countLeadingZeros32(resultGPR, scratchGPR3); + m_jit.sub32(scratchGPR3, scratchGPR); + + JITCompiler::Jump useFullTable = m_jit.branch32(JITCompiler::Equal, scratchGPR, TrustedImm32(31)); + + m_jit.neg32(scratchGPR); + m_jit.add32(TrustedImm32(31), scratchGPR); + + int elementSizeByShift = -1; + elementSizeByShift = 3; + m_jit.relativeTableJump(scratchGPR, elementSizeByShift); + + useFullTable.link(&m_jit); + // Modulo table + for (int i = 31; i > 0; --i) { + ShiftTypeAndAmount shift(SRType_LSL, i); + m_jit.assembler().sub_S(scratchGPR, resultGPR, scratchGPR2, shift); + m_jit.assembler().it(ARMv7Assembler::ConditionCS); + m_jit.assembler().mov(resultGPR, scratchGPR); + } + + JITCompiler::Jump lower = m_jit.branch32(JITCompiler::Below, resultGPR, scratchGPR2); + m_jit.sub32(scratchGPR2, resultGPR); + lower.link(&m_jit); +#endif // CPU(X86) || CPU(X86_64) + + exitBranch.link(&m_jit); + + // Check for negative remainder + m_jit.pop(scratchGPR); + JITCompiler::Jump positiveResult = m_jit.branch32(JITCompiler::Equal, scratchGPR, TrustedImm32(0)); + m_jit.neg32(resultGPR); + positiveResult.link(&m_jit); + + integerResult(resultGPR, m_compileIndex); + +#if CPU(X86) || CPU(X86_64) + if (temp2 != InvalidGPRReg) + unlock(temp2); +#endif +} + +void SpeculativeJIT::compileArithMul(Node& node) +{ + if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())) && node.canSpeculateInteger()) { + SpeculateIntegerOperand op1(this, node.child1()); + SpeculateIntegerOperand op2(this, node.child2()); + GPRTemporary result(this); + + GPRReg reg1 = op1.gpr(); + GPRReg reg2 = op2.gpr(); + + // What is unfortunate is that we cannot take advantage of nodeCanTruncateInteger() + // here. A multiply on integers performed in the double domain and then truncated to + // an integer will give a different result than a multiply performed in the integer + // domain and then truncated, if the integer domain result would have resulted in + // something bigger than what a 32-bit integer can hold. JavaScript mandates that + // the semantics are always as if the multiply had been performed in the double + // domain. + + speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branchMul32(MacroAssembler::Overflow, reg1, reg2, result.gpr())); + + // Check for negative zero, if the users of this node care about such things. + if (!nodeCanIgnoreNegativeZero(node.arithNodeFlags())) { + MacroAssembler::Jump resultNonZero = m_jit.branchTest32(MacroAssembler::NonZero, result.gpr()); + speculationCheck(NegativeZero, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::LessThan, reg1, TrustedImm32(0))); + speculationCheck(NegativeZero, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::LessThan, reg2, TrustedImm32(0))); + resultNonZero.link(&m_jit); + } + + integerResult(result.gpr(), m_compileIndex); + return; + } + + SpeculateDoubleOperand op1(this, node.child1()); + SpeculateDoubleOperand op2(this, node.child2()); + FPRTemporary result(this, op1, op2); + + FPRReg reg1 = op1.fpr(); + FPRReg reg2 = op2.fpr(); + + m_jit.mulDouble(reg1, reg2, result.fpr()); + + doubleResult(result.fpr(), m_compileIndex); +} + +void SpeculativeJIT::compileArithMod(Node& node) +{ + if (!at(node.child1()).shouldNotSpeculateInteger() && !at(node.child2()).shouldNotSpeculateInteger() + && node.canSpeculateInteger()) { + compileSoftModulo(node); + return; + } + + SpeculateDoubleOperand op1(this, node.child1()); + SpeculateDoubleOperand op2(this, node.child2()); + + FPRReg op1FPR = op1.fpr(); + FPRReg op2FPR = op2.fpr(); + + flushRegisters(); + + FPRResult result(this); + + callOperation(fmodAsDFGOperation, result.fpr(), op1FPR, op2FPR); + + doubleResult(result.fpr(), m_compileIndex); +} + +// Returns true if the compare is fused with a subsequent branch. +bool SpeculativeJIT::compare(Node& node, MacroAssembler::RelationalCondition condition, MacroAssembler::DoubleCondition doubleCondition, S_DFGOperation_EJJ operation) +{ + if (compilePeepHoleBranch(node, condition, doubleCondition, operation)) + return true; + + if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2()))) + compileIntegerCompare(node, condition); + else if (Node::shouldSpeculateNumber(at(node.child1()), at(node.child2()))) + compileDoubleCompare(node, doubleCondition); + else if (node.op == CompareEq && Node::shouldSpeculateFinalObject(at(node.child1()), at(node.child2()))) + compileObjectEquality(node, &JSFinalObject::s_info, isFinalObjectPrediction); + else if (node.op == CompareEq && Node::shouldSpeculateArray(at(node.child1()), at(node.child2()))) + compileObjectEquality(node, &JSArray::s_info, isArrayPrediction); + else + nonSpeculativeNonPeepholeCompare(node, condition, operation); + + return false; +} + +bool SpeculativeJIT::compileStrictEqForConstant(Node& node, NodeIndex value, JSValue constant) +{ + JSValueOperand op1(this, value); + + NodeIndex branchNodeIndex = detectPeepHoleBranch(); + if (branchNodeIndex != NoNode) { + Node& branchNode = at(branchNodeIndex); + BlockIndex taken = branchNode.takenBlockIndex(); + BlockIndex notTaken = branchNode.notTakenBlockIndex(); + MacroAssembler::RelationalCondition condition = MacroAssembler::Equal; + + // The branch instruction will branch to the taken block. + // If taken is next, switch taken with notTaken & invert the branch condition so we can fall through. + if (taken == (m_block + 1)) { + condition = MacroAssembler::NotEqual; + BlockIndex tmp = taken; + taken = notTaken; + notTaken = tmp; + } + +#if USE(JSVALUE64) + addBranch(m_jit.branchPtr(condition, op1.gpr(), MacroAssembler::TrustedImmPtr(bitwise_cast<void*>(JSValue::encode(constant)))), taken); +#else + GPRReg payloadGPR = op1.payloadGPR(); + GPRReg tagGPR = op1.tagGPR(); + if (condition == MacroAssembler::Equal) { + // Drop down if not equal, go elsewhere if equal. + MacroAssembler::Jump notEqual = m_jit.branch32(MacroAssembler::NotEqual, tagGPR, MacroAssembler::Imm32(constant.tag())); + addBranch(m_jit.branch32(MacroAssembler::Equal, payloadGPR, MacroAssembler::Imm32(constant.payload())), taken); + notEqual.link(&m_jit); + } else { + // Drop down if equal, go elsehwere if not equal. + addBranch(m_jit.branch32(MacroAssembler::NotEqual, tagGPR, MacroAssembler::Imm32(constant.tag())), taken); + addBranch(m_jit.branch32(MacroAssembler::NotEqual, payloadGPR, MacroAssembler::Imm32(constant.payload())), taken); + } +#endif + + if (notTaken != (m_block + 1)) + addBranch(m_jit.jump(), notTaken); + + use(node.child1()); + use(node.child2()); + m_compileIndex = branchNodeIndex; + return true; + } + + GPRTemporary result(this); + +#if USE(JSVALUE64) + GPRReg op1GPR = op1.gpr(); + GPRReg resultGPR = result.gpr(); + m_jit.move(MacroAssembler::TrustedImmPtr(bitwise_cast<void*>(ValueFalse)), resultGPR); + MacroAssembler::Jump notEqual = m_jit.branchPtr(MacroAssembler::NotEqual, op1GPR, MacroAssembler::TrustedImmPtr(bitwise_cast<void*>(JSValue::encode(constant)))); + m_jit.or32(MacroAssembler::Imm32(1), resultGPR); + notEqual.link(&m_jit); + jsValueResult(resultGPR, m_compileIndex, DataFormatJSBoolean); +#else + GPRReg op1PayloadGPR = op1.payloadGPR(); + GPRReg op1TagGPR = op1.tagGPR(); + GPRReg resultGPR = result.gpr(); + m_jit.move(Imm32(0), resultGPR); + MacroAssembler::JumpList notEqual; + notEqual.append(m_jit.branch32(MacroAssembler::NotEqual, op1TagGPR, MacroAssembler::Imm32(constant.tag()))); + notEqual.append(m_jit.branch32(MacroAssembler::NotEqual, op1PayloadGPR, MacroAssembler::Imm32(constant.payload()))); + m_jit.move(Imm32(1), resultGPR); + notEqual.link(&m_jit); + booleanResult(resultGPR, m_compileIndex); +#endif + + return false; +} + +bool SpeculativeJIT::compileStrictEq(Node& node) +{ + // 1) If either operand is a constant and that constant is not a double, integer, + // or string, then do a JSValue comparison. + + if (isJSConstant(node.child1())) { + JSValue value = valueOfJSConstant(node.child1()); + if (!value.isNumber() && !value.isString()) + return compileStrictEqForConstant(node, node.child2(), value); + } + + if (isJSConstant(node.child2())) { + JSValue value = valueOfJSConstant(node.child2()); + if (!value.isNumber() && !value.isString()) + return compileStrictEqForConstant(node, node.child1(), value); + } + + // 2) If the operands are predicted integer, do an integer comparison. + + if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2()))) { + NodeIndex branchNodeIndex = detectPeepHoleBranch(); + if (branchNodeIndex != NoNode) { + compilePeepHoleIntegerBranch(node, branchNodeIndex, MacroAssembler::Equal); + use(node.child1()); + use(node.child2()); + m_compileIndex = branchNodeIndex; + return true; + } + compileIntegerCompare(node, MacroAssembler::Equal); + return false; + } + + // 3) If the operands are predicted double, do a double comparison. + + if (Node::shouldSpeculateNumber(at(node.child1()), at(node.child2()))) { + NodeIndex branchNodeIndex = detectPeepHoleBranch(); + if (branchNodeIndex != NoNode) { + compilePeepHoleDoubleBranch(node, branchNodeIndex, MacroAssembler::DoubleEqual); + use(node.child1()); + use(node.child2()); + m_compileIndex = branchNodeIndex; + return true; + } + compileDoubleCompare(node, MacroAssembler::DoubleEqual); + return false; + } + + // 4) If the operands are predicted final object or array, then do a final object + // or array comparison. + + if (Node::shouldSpeculateFinalObject(at(node.child1()), at(node.child2()))) { + NodeIndex branchNodeIndex = detectPeepHoleBranch(); + if (branchNodeIndex != NoNode) { + compilePeepHoleObjectEquality(node, branchNodeIndex, &JSFinalObject::s_info, isFinalObjectPrediction); + use(node.child1()); + use(node.child2()); + m_compileIndex = branchNodeIndex; + return true; + } + compileObjectEquality(node, &JSFinalObject::s_info, isFinalObjectPrediction); + return false; + } + + if (Node::shouldSpeculateArray(at(node.child1()), at(node.child2()))) { + NodeIndex branchNodeIndex = detectPeepHoleBranch(); + if (branchNodeIndex != NoNode) { + compilePeepHoleObjectEquality(node, branchNodeIndex, &JSArray::s_info, isArrayPrediction); + use(node.child1()); + use(node.child2()); + m_compileIndex = branchNodeIndex; + return true; + } + compileObjectEquality(node, &JSArray::s_info, isArrayPrediction); + return false; + } + + // 5) Fall back to non-speculative strict equality. + + return nonSpeculativeStrictEq(node); +} + +void SpeculativeJIT::compileGetIndexedPropertyStorage(Node& node) +{ + if (!node.prediction() || !at(node.child1()).prediction() || !at(node.child2()).prediction()) { + terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); + return; + } + + SpeculateCellOperand base(this, node.child1()); + GPRReg baseReg = base.gpr(); + + PredictedType basePrediction = at(node.child2()).prediction(); + if (!(basePrediction & PredictInt32) && basePrediction) { + ASSERT_NOT_REACHED(); + terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); + noResult(m_compileIndex); + return; + } + + GPRTemporary storage(this); + GPRReg storageReg = storage.gpr(); + if (at(node.child1()).prediction() == PredictString) { + if (!isStringPrediction(m_state.forNode(node.child1()).m_type)) + speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSString::s_info))); + + m_jit.loadPtr(MacroAssembler::Address(baseReg, JSString::offsetOfValue()), storageReg); + + // Speculate that we're not accessing a rope + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchTest32(MacroAssembler::Zero, storageReg)); + + m_jit.loadPtr(MacroAssembler::Address(storageReg, StringImpl::dataOffset()), storageReg); + } else if (at(node.child1()).shouldSpeculateByteArray()) { + if (!isByteArrayPrediction(m_state.forNode(node.child1()).m_type)) + speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSByteArray::s_info))); + m_jit.loadPtr(MacroAssembler::Address(baseReg, JSByteArray::offsetOfStorage()), storageReg); + } else if (at(node.child1()).shouldSpeculateInt8Array()) { + const TypedArrayDescriptor& descriptor = m_jit.globalData()->int8ArrayDescriptor(); + if (!isInt8ArrayPrediction(m_state.forNode(node.child1()).m_type)) + speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(descriptor.m_classInfo))); + m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg); + } else if (at(node.child1()).shouldSpeculateInt16Array()) { + const TypedArrayDescriptor& descriptor = m_jit.globalData()->int16ArrayDescriptor(); + if (!isInt16ArrayPrediction(m_state.forNode(node.child1()).m_type)) + speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(descriptor.m_classInfo))); + m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg); + } else if (at(node.child1()).shouldSpeculateInt32Array()) { + const TypedArrayDescriptor& descriptor = m_jit.globalData()->int32ArrayDescriptor(); + if (!isInt32ArrayPrediction(m_state.forNode(node.child1()).m_type)) + speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(descriptor.m_classInfo))); + m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg); + } else if (at(node.child1()).shouldSpeculateUint8Array()) { + const TypedArrayDescriptor& descriptor = m_jit.globalData()->uint8ArrayDescriptor(); + if (!isUint8ArrayPrediction(m_state.forNode(node.child1()).m_type)) + speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(descriptor.m_classInfo))); + m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg); + } else if (at(node.child1()).shouldSpeculateUint16Array()) { + const TypedArrayDescriptor& descriptor = m_jit.globalData()->uint16ArrayDescriptor(); + if (!isUint16ArrayPrediction(m_state.forNode(node.child1()).m_type)) + speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(descriptor.m_classInfo))); + m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg); + } else if (at(node.child1()).shouldSpeculateUint32Array()) { + const TypedArrayDescriptor& descriptor = m_jit.globalData()->uint32ArrayDescriptor(); + if (!isUint32ArrayPrediction(m_state.forNode(node.child1()).m_type)) + speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(descriptor.m_classInfo))); + m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg); + } else if (at(node.child1()).shouldSpeculateFloat32Array()) { + const TypedArrayDescriptor& descriptor = m_jit.globalData()->float32ArrayDescriptor(); + if (!isFloat32ArrayPrediction(m_state.forNode(node.child1()).m_type)) + speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(descriptor.m_classInfo))); + m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg); + } else if (at(node.child1()).shouldSpeculateFloat64Array()) { + const TypedArrayDescriptor& descriptor = m_jit.globalData()->float64ArrayDescriptor(); + if (!isFloat64ArrayPrediction(m_state.forNode(node.child1()).m_type)) + speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(descriptor.m_classInfo))); + m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg); + } else { + if (!isArrayPrediction(m_state.forNode(node.child1()).m_type)) + speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); + m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg); + } + storageResult(storageReg, m_compileIndex); +} + +} } // namespace JSC::DFG + +#endif |