| /* |
| * 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(); |
| } |
| |
| 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) |
| { |
| #if CPU(X86) |
| // we currenty lazily allocate the reg, as the number of regs on X86 is limited. |
| #else |
| m_gpr = m_jit->allocate(); |
| #endif |
| } |
| |
| 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 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, void* vptr, 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), MacroAssembler::TrustedImmPtr(vptr))); |
| if (!predictionCheck(m_state.forNode(node.child2()).m_type)) |
| speculationCheck(BadType, JSValueSource::unboxedCell(op2GPR), node.child2(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(op2GPR), MacroAssembler::TrustedImmPtr(vptr))); |
| |
| 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, m_jit.globalData()->jsFinalObjectVPtr, isFinalObjectPrediction); |
| use(node.child1()); |
| use(node.child2()); |
| } else if (node.op == CompareEq && Node::shouldSpeculateArray(at(node.child1()), at(node.child2()))) { |
| compilePeepHoleObjectEquality(node, branchNodeIndex, m_jit.globalData()->jsArrayVPtr, 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 |
| 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsArrayVPtr))); |
| } 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsByteArrayVPtr))); |
| } 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->int8ArrayDescriptor().m_vptr))); |
| } 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->int16ArrayDescriptor().m_vptr))); |
| } 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->int32ArrayDescriptor().m_vptr))); |
| } 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint8ArrayDescriptor().m_vptr))); |
| } 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint16ArrayDescriptor().m_vptr))); |
| } 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint32ArrayDescriptor().m_vptr))); |
| } 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->float32ArrayDescriptor().m_vptr))); |
| } 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->float64ArrayDescriptor().m_vptr))); |
| } |
| #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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsArrayVPtr))); |
| } 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsByteArrayVPtr))); |
| } 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->int8ArrayDescriptor().m_vptr))); |
| } 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->int16ArrayDescriptor().m_vptr))); |
| } 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->int32ArrayDescriptor().m_vptr))); |
| } 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint8ArrayDescriptor().m_vptr))); |
| } 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint16ArrayDescriptor().m_vptr))); |
| } 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint32ArrayDescriptor().m_vptr))); |
| } 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->float32ArrayDescriptor().m_vptr))); |
| } 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()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->float64ArrayDescriptor().m_vptr))); |
| } |
| #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 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), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsByteArrayVPtr))); |
| 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), MacroAssembler::TrustedImmPtr(descriptor.m_vptr))); |
| |
| 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), MacroAssembler::TrustedImmPtr(descriptor.m_vptr))); |
| 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), MacroAssembler::TrustedImmPtr(descriptor.m_vptr))); |
| |
| 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, m_jit.globalData()->jsFinalObjectVPtr, isFinalObjectPrediction); |
| else if (node.op == CompareEq && Node::shouldSpeculateArray(at(node.child1()), at(node.child2()))) |
| compileObjectEquality(node, m_jit.globalData()->jsArrayVPtr, 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, m_jit.globalData()->jsFinalObjectVPtr, isFinalObjectPrediction); |
| use(node.child1()); |
| use(node.child2()); |
| m_compileIndex = branchNodeIndex; |
| return true; |
| } |
| compileObjectEquality(node, m_jit.globalData()->jsFinalObjectVPtr, isFinalObjectPrediction); |
| return false; |
| } |
| |
| if (Node::shouldSpeculateArray(at(node.child1()), at(node.child2()))) { |
| NodeIndex branchNodeIndex = detectPeepHoleBranch(); |
| if (branchNodeIndex != NoNode) { |
| compilePeepHoleObjectEquality(node, branchNodeIndex, m_jit.globalData()->jsArrayVPtr, isArrayPrediction); |
| use(node.child1()); |
| use(node.child2()); |
| m_compileIndex = branchNodeIndex; |
| return true; |
| } |
| compileObjectEquality(node, m_jit.globalData()->jsArrayVPtr, isArrayPrediction); |
| return false; |
| } |
| |
| // 5) Fall back to non-speculative strict equality. |
| |
| return nonSpeculativeStrictEq(node); |
| } |
| |
| void SpeculativeJIT::compileGetIndexedPropertyStorage(Node& node) |
| { |
| 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), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsStringVPtr))); |
| |
| 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), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsByteArrayVPtr))); |
| 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), MacroAssembler::TrustedImmPtr(descriptor.m_vptr))); |
| 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), MacroAssembler::TrustedImmPtr(descriptor.m_vptr))); |
| 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), MacroAssembler::TrustedImmPtr(descriptor.m_vptr))); |
| 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), MacroAssembler::TrustedImmPtr(descriptor.m_vptr))); |
| 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), MacroAssembler::TrustedImmPtr(descriptor.m_vptr))); |
| 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), MacroAssembler::TrustedImmPtr(descriptor.m_vptr))); |
| 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), MacroAssembler::TrustedImmPtr(descriptor.m_vptr))); |
| 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), MacroAssembler::TrustedImmPtr(descriptor.m_vptr))); |
| 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), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsArrayVPtr))); |
| m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg); |
| } |
| storageResult(storageReg, m_compileIndex); |
| } |
| |
| } } // namespace JSC::DFG |
| |
| #endif |