diff options
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp')
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp | 1005 |
1 files changed, 656 insertions, 349 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index 7bcb44576..18db85c22 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -28,7 +28,6 @@ #if ENABLE(DFG_JIT) -#include "JSByteArray.h" #include "LinkBuffer.h" namespace JSC { namespace DFG { @@ -60,12 +59,16 @@ GPRReg SpeculativeJIT::fillStorage(NodeIndex nodeIndex) 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; + if (info.spillFormat() == DataFormatStorage) { + GPRReg gpr = allocate(); + m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); + m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), gpr); + info.fillStorage(gpr); + return gpr; + } + + // Must be a cell; fill it as a cell and then return the pointer. + return fillSpeculateCell(nodeIndex); } case DataFormatStorage: { @@ -75,33 +78,31 @@ GPRReg SpeculativeJIT::fillStorage(NodeIndex nodeIndex) } default: - ASSERT_NOT_REACHED(); + return fillSpeculateCell(nodeIndex); } - - return InvalidGPRReg; } void SpeculativeJIT::useChildren(Node& node) { - if (node.flags & NodeHasVarArgs) { + if (node.flags() & NodeHasVarArgs) { for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); childIdx++) use(m_jit.graph().m_varArgChildren[childIdx]); } else { - NodeUse child1 = node.child1(); + Edge child1 = node.child1(); if (!child1) { ASSERT(!node.child2() && !node.child3()); return; } use(child1); - NodeUse child2 = node.child2(); + Edge child2 = node.child2(); if (!child2) { ASSERT(!node.child3()); return; } use(child2); - NodeUse child3 = node.child3(); + Edge child3 = node.child3(); if (!child3) return; use(child3); @@ -184,31 +185,6 @@ bool SpeculativeJIT::isKnownNotNumber(NodeIndex nodeIndex) || (node.hasConstant() && !isNumberConstant(nodeIndex)); } -bool SpeculativeJIT::isKnownBoolean(NodeIndex nodeIndex) -{ - Node& node = m_jit.graph()[nodeIndex]; - if (node.hasBooleanResult()) - return true; - - if (isBooleanConstant(nodeIndex)) - return true; - - VirtualRegister virtualRegister = node.virtualRegister(); - GenerationInfo& info = m_generationInfo[virtualRegister]; - - return info.isJSBoolean(); -} - -bool SpeculativeJIT::isKnownNotBoolean(NodeIndex nodeIndex) -{ - Node& node = m_jit.graph()[nodeIndex]; - VirtualRegister virtualRegister = node.virtualRegister(); - GenerationInfo& info = m_generationInfo[virtualRegister]; - if (node.hasConstant() && !valueOfJSConstant(nodeIndex).isBoolean()) - return true; - return !(info.isJSBoolean() || info.isUnknownJS()); -} - void SpeculativeJIT::writeBarrier(MacroAssembler& jit, GPRReg owner, GPRReg scratch1, GPRReg scratch2, WriteBarrierUseKind useKind) { UNUSED_PARAM(jit); @@ -249,7 +225,7 @@ void SpeculativeJIT::markCellCard(MacroAssembler& jit, GPRReg owner, GPRReg scra #endif } -void SpeculativeJIT::writeBarrier(GPRReg ownerGPR, GPRReg valueGPR, NodeUse valueUse, WriteBarrierUseKind useKind, GPRReg scratch1, GPRReg scratch2) +void SpeculativeJIT::writeBarrier(GPRReg ownerGPR, GPRReg valueGPR, Edge valueUse, WriteBarrierUseKind useKind, GPRReg scratch1, GPRReg scratch2) { UNUSED_PARAM(ownerGPR); UNUSED_PARAM(valueGPR); @@ -325,7 +301,7 @@ void SpeculativeJIT::writeBarrier(GPRReg ownerGPR, JSCell* value, WriteBarrierUs #endif } -void SpeculativeJIT::writeBarrier(JSCell* owner, GPRReg valueGPR, NodeUse valueUse, WriteBarrierUseKind useKind, GPRReg scratch) +void SpeculativeJIT::writeBarrier(JSCell* owner, GPRReg valueGPR, Edge valueUse, WriteBarrierUseKind useKind, GPRReg scratch) { UNUSED_PARAM(owner); UNUSED_PARAM(valueGPR); @@ -774,6 +750,9 @@ void ValueSource::dump(FILE* out) const case SourceNotSet: fprintf(out, "NotSet"); break; + case SourceIsDead: + fprintf(out, "IsDead"); + break; case ValueInRegisterFile: fprintf(out, "InRegFile"); break; @@ -882,25 +861,52 @@ bool SpeculativeJIT::compilePeepHoleBranch(Node& node, MacroAssembler::Relationa // so can be no intervening nodes to also reference the compare. ASSERT(node.adjustedRefCount() == 1); - if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2()))) { + 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()))) { + else if (Node::shouldSpeculateNumber(at(node.child1()), at(node.child2()))) compilePeepHoleDoubleBranch(node, branchNodeIndex, doubleCondition); - use(node.child1()); - use(node.child2()); - } else if (node.op == CompareEq && Node::shouldSpeculateFinalObject(at(node.child1()), at(node.child2()))) { - compilePeepHoleObjectEquality(node, branchNodeIndex, &JSFinalObject::s_info, isFinalObjectPrediction); - use(node.child1()); - use(node.child2()); - } else if (node.op == CompareEq && Node::shouldSpeculateArray(at(node.child1()), at(node.child2()))) { - compilePeepHoleObjectEquality(node, branchNodeIndex, &JSArray::s_info, isArrayPrediction); - use(node.child1()); - use(node.child2()); - } else + else if (node.op() == CompareEq) { + if (Node::shouldSpeculateFinalObject( + at(node.child1()), at(node.child2()))) { + compilePeepHoleObjectEquality( + node, branchNodeIndex, &JSFinalObject::s_info, + isFinalObjectPrediction); + } else if (Node::shouldSpeculateArray( + at(node.child1()), at(node.child2()))) { + compilePeepHoleObjectEquality( + node, branchNodeIndex, &JSArray::s_info, + isArrayPrediction); + } else if (at(node.child1()).shouldSpeculateFinalObject() + && at(node.child2()).shouldSpeculateFinalObjectOrOther()) { + compilePeepHoleObjectToObjectOrOtherEquality( + node.child1(), node.child2(), branchNodeIndex, + &JSFinalObject::s_info, isFinalObjectPrediction); + } else if (at(node.child1()).shouldSpeculateFinalObjectOrOther() + && at(node.child2()).shouldSpeculateFinalObject()) { + compilePeepHoleObjectToObjectOrOtherEquality( + node.child2(), node.child1(), branchNodeIndex, + &JSFinalObject::s_info, isFinalObjectPrediction); + } else if (at(node.child1()).shouldSpeculateArray() + && at(node.child2()).shouldSpeculateArrayOrOther()) { + compilePeepHoleObjectToObjectOrOtherEquality( + node.child1(), node.child2(), branchNodeIndex, + &JSArray::s_info, isArrayPrediction); + } else if (at(node.child1()).shouldSpeculateArrayOrOther() + && at(node.child2()).shouldSpeculateArray()) { + compilePeepHoleObjectToObjectOrOtherEquality( + node.child2(), node.child1(), branchNodeIndex, + &JSArray::s_info, isArrayPrediction); + } else { + nonSpeculativePeepholeBranch(node, branchNodeIndex, condition, operation); + return true; + } + } else { nonSpeculativePeepholeBranch(node, branchNodeIndex, condition, operation); + return true; + } + use(node.child1()); + use(node.child2()); m_indexInBlock = branchIndexInBlock; m_compileIndex = branchNodeIndex; return true; @@ -910,7 +916,7 @@ bool SpeculativeJIT::compilePeepHoleBranch(Node& node, MacroAssembler::Relationa void SpeculativeJIT::compileMovHint(Node& node) { - ASSERT(node.op == SetLocal); + ASSERT(node.op() == SetLocal); setNodeIndexForOperand(node.child1().index(), node.local()); m_lastSetOperand = node.local(); @@ -927,6 +933,8 @@ void SpeculativeJIT::compile(BasicBlock& block) #if DFG_ENABLE(JIT_BREAK_ON_EVERY_BLOCK) m_jit.breakpoint(); #endif + + m_jit.jitAssertHasValidCallFrame(); ASSERT(m_arguments.size() == block.variablesAtHead.numberOfArguments()); for (size_t i = 0; i < m_arguments.size(); ++i) { @@ -943,7 +951,9 @@ void SpeculativeJIT::compile(BasicBlock& 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_jit.graph().localIsCaptured(i)) + if ((nodeIndex == NoNode || !at(nodeIndex).refCount()) && !m_jit.graph().localIsCaptured(i)) + m_variables[i] = ValueSource(SourceIsDead); + else if (m_jit.graph().localIsCaptured(i)) m_variables[i] = ValueSource(ValueInRegisterFile); else if (at(nodeIndex).variableAccessData()->shouldUseDoubleFormat()) m_variables[i] = ValueSource(DoubleInRegisterFile); @@ -969,7 +979,7 @@ void SpeculativeJIT::compile(BasicBlock& block) #if DFG_ENABLE(DEBUG_VERBOSE) dataLog("SpeculativeJIT skipping Node @%d (bc#%u) at JIT offset 0x%x ", (int)m_compileIndex, node.codeOrigin.bytecodeIndex, m_jit.debugOffset()); #endif - switch (node.op) { + switch (node.op()) { case SetLocal: compileMovHint(node); break; @@ -979,10 +989,16 @@ void SpeculativeJIT::compile(BasicBlock& block) 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()); + // The recovery should refer either to something that has already been + // stored into the register file at the right place, or to a constant, + // since the Arguments code isn't smart enough to handle anything else. + // The exception is the this argument, which we don't really need to be + // able to recover. +#if DFG_ENABLE(DEBUG_VERBOSE) + dataLog("\nRecovery for argument %d: ", i); + recovery.dump(WTF::dataFile()); +#endif + ASSERT(!i || (recovery.isAlreadyInRegisterFile() || recovery.isConstant())); inlineCallFrame->arguments[i] = recovery; } break; @@ -1076,7 +1092,7 @@ void SpeculativeJIT::checkArgumentTypes() for (int i = 0; i < m_jit.codeBlock()->numParameters(); ++i) { NodeIndex nodeIndex = m_jit.graph().m_arguments[i]; Node& node = at(nodeIndex); - ASSERT(node.op == SetArgument); + ASSERT(node.op() == SetArgument); if (!node.shouldGenerate()) { // The argument is dead. We don't do any checks for such arguments. continue; @@ -1096,11 +1112,6 @@ void SpeculativeJIT::checkArgumentTypes() m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); - } else if (isByteArrayPrediction(predictedType)) { - GPRTemporary temp(this); - m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSByteArray::s_info))); } else if (isBooleanPrediction(predictedType)) { GPRTemporary temp(this); m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); @@ -1161,12 +1172,6 @@ void SpeculativeJIT::checkArgumentTypes() speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); - } else if (isByteArrayPrediction(predictedType)) { - GPRTemporary temp(this); - m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); - m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSByteArray::s_info))); } else if (isBooleanPrediction(predictedType)) speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::BooleanTag))); else if (isInt8ArrayPrediction(predictedType)) { @@ -1278,6 +1283,9 @@ void SpeculativeJIT::linkOSREntries(LinkBuffer& linkBuffer) ValueRecovery SpeculativeJIT::computeValueRecoveryFor(const ValueSource& valueSource) { switch (valueSource.kind()) { + case SourceIsDead: + return ValueRecovery::constant(jsUndefined()); + case ValueInRegisterFile: return ValueRecovery::alreadyInRegisterFile(); @@ -1309,6 +1317,10 @@ ValueRecovery SpeculativeJIT::computeValueRecoveryFor(const ValueSource& valueSo // Try to see if there is an alternate node that would contain the value we want. // There are four possibilities: // + // Int32ToDouble: We can use this in place of the original node, but + // we'd rather not; so we use it only if it is the only remaining + // live version. + // // ValueToInt32: If the only remaining live version of the value is // ValueToInt32, then we can use it. // @@ -1319,10 +1331,13 @@ ValueRecovery SpeculativeJIT::computeValueRecoveryFor(const ValueSource& valueSo // 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. + // + // DoubleAsInt32: Same as UInt32ToNumber. + // bool found = false; - if (nodePtr->op == UInt32ToNumber) { + if (nodePtr->op() == UInt32ToNumber || nodePtr->op() == DoubleAsInt32) { NodeIndex nodeIndex = nodePtr->child1().index(); nodePtr = &at(nodeIndex); infoPtr = &m_generationInfo[nodePtr->virtualRegister()]; @@ -1331,8 +1346,10 @@ ValueRecovery SpeculativeJIT::computeValueRecoveryFor(const ValueSource& valueSo } if (!found) { + NodeIndex int32ToDoubleIndex = NoNode; NodeIndex valueToInt32Index = NoNode; NodeIndex uint32ToNumberIndex = NoNode; + NodeIndex doubleAsInt32Index = NoNode; for (unsigned virtualRegister = 0; virtualRegister < m_generationInfo.size(); ++virtualRegister) { GenerationInfo& info = m_generationInfo[virtualRegister]; @@ -1343,20 +1360,29 @@ ValueRecovery SpeculativeJIT::computeValueRecoveryFor(const ValueSource& valueSo Node& node = at(info.nodeIndex()); if (node.child1Unchecked() != valueSource.nodeIndex()) continue; - switch (node.op) { + switch (node.op()) { + case Int32ToDouble: + int32ToDoubleIndex = info.nodeIndex(); + break; case ValueToInt32: valueToInt32Index = info.nodeIndex(); break; case UInt32ToNumber: uint32ToNumberIndex = info.nodeIndex(); break; + case DoubleAsInt32: + doubleAsInt32Index = info.nodeIndex(); default: break; } } NodeIndex nodeIndexToUse; - if (valueToInt32Index != NoNode) + if (doubleAsInt32Index != NoNode) + nodeIndexToUse = doubleAsInt32Index; + else if (int32ToDoubleIndex != NoNode) + nodeIndexToUse = int32ToDoubleIndex; + else if (valueToInt32Index != NoNode) nodeIndexToUse = valueToInt32Index; else if (uint32ToNumberIndex != NoNode) nodeIndexToUse = uint32ToNumberIndex; @@ -1488,33 +1514,179 @@ void SpeculativeJIT::compileGetByValOnString(Node& node) cellResult(scratchReg, m_compileIndex); } +GeneratedOperandType SpeculativeJIT::checkGeneratedTypeForToInt32(NodeIndex nodeIndex) +{ +#if DFG_ENABLE(DEBUG_VERBOSE) + dataLog("checkGeneratedTypeForToInt32@%d ", nodeIndex); +#endif + Node& node = at(nodeIndex); + VirtualRegister virtualRegister = node.virtualRegister(); + GenerationInfo& info = m_generationInfo[virtualRegister]; + + if (info.registerFormat() == DataFormatNone) { + if (node.hasConstant()) { + if (isInt32Constant(nodeIndex)) + return GeneratedOperandInteger; + + if (isNumberConstant(nodeIndex)) + return GeneratedOperandDouble; + + terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); + return GeneratedOperandTypeUnknown; + } + + if (info.spillFormat() == DataFormatDouble) + return GeneratedOperandDouble; + } + + switch (info.registerFormat()) { + case DataFormatBoolean: // This type never occurs. + case DataFormatStorage: + ASSERT_NOT_REACHED(); + + case DataFormatCell: + terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); + return GeneratedOperandTypeUnknown; + + case DataFormatNone: + case DataFormatJSCell: + case DataFormatJS: + case DataFormatJSBoolean: + return GeneratedOperandJSValue; + + case DataFormatJSInteger: + case DataFormatInteger: + return GeneratedOperandInteger; + + case DataFormatJSDouble: + case DataFormatDouble: + return GeneratedOperandDouble; + } + + ASSERT_NOT_REACHED(); + return GeneratedOperandTypeUnknown; +} + void SpeculativeJIT::compileValueToInt32(Node& node) { - if (at(node.child1()).shouldNotSpeculateInteger()) { - if (at(node.child1()).shouldSpeculateDouble()) { - SpeculateDoubleOperand op1(this, node.child1()); + if (at(node.child1()).shouldSpeculateInteger()) { + SpeculateIntegerOperand op1(this, node.child1()); + GPRTemporary result(this, op1); + m_jit.move(op1.gpr(), result.gpr()); + integerResult(result.gpr(), m_compileIndex, op1.format()); + return; + } + + if (at(node.child1()).shouldSpeculateNumber()) { + switch (checkGeneratedTypeForToInt32(node.child1().index())) { + case GeneratedOperandInteger: { + SpeculateIntegerOperand op1(this, node.child1()); + GPRTemporary result(this, op1); + m_jit.move(op1.gpr(), result.gpr()); + integerResult(result.gpr(), m_compileIndex, op1.format()); + return; + } + case GeneratedOperandDouble: { GPRTemporary result(this); + DoubleOperand op1(this, node.child1()); 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); + case GeneratedOperandJSValue: { + GPRTemporary result(this); +#if USE(JSVALUE64) + JSValueOperand op1(this, node.child1()); + + GPRReg gpr = op1.gpr(); + GPRReg resultGpr = result.gpr(); + FPRTemporary tempFpr(this); + FPRReg fpr = tempFpr.fpr(); + + JITCompiler::Jump isInteger = m_jit.branchPtr(MacroAssembler::AboveOrEqual, gpr, GPRInfo::tagTypeNumberRegister); + + speculationCheck(BadType, JSValueRegs(gpr), node.child1().index(), m_jit.branchTestPtr(MacroAssembler::Zero, gpr, GPRInfo::tagTypeNumberRegister)); + + // First, if we get here we have a double encoded as a JSValue + m_jit.move(gpr, resultGpr); + unboxDouble(resultGpr, fpr); + + silentSpillAllRegisters(resultGpr); + callOperation(toInt32, resultGpr, fpr); + silentFillAllRegisters(resultGpr); + + JITCompiler::Jump converted = m_jit.jump(); + + isInteger.link(&m_jit); + m_jit.zeroExtend32ToPtr(gpr, resultGpr); + + converted.link(&m_jit); +#else + Node& childNode = at(node.child1().index()); + VirtualRegister virtualRegister = childNode.virtualRegister(); + GenerationInfo& info = m_generationInfo[virtualRegister]; + + JSValueOperand op1(this, node.child1()); + + GPRReg payloadGPR = op1.payloadGPR(); + GPRReg resultGpr = result.gpr(); + + if (info.registerFormat() == DataFormatJSInteger) + m_jit.move(payloadGPR, resultGpr); + else { + GPRReg tagGPR = op1.tagGPR(); + FPRTemporary tempFpr(this); + FPRReg fpr = tempFpr.fpr(); + FPRTemporary scratch(this); + + JITCompiler::Jump isInteger = m_jit.branch32(MacroAssembler::Equal, tagGPR, TrustedImm32(JSValue::Int32Tag)); + + speculationCheck(BadType, JSValueRegs(tagGPR, payloadGPR), node.child1().index(), m_jit.branch32(MacroAssembler::AboveOrEqual, tagGPR, TrustedImm32(JSValue::LowestTag))); + + unboxDouble(tagGPR, payloadGPR, fpr, scratch.fpr()); + + silentSpillAllRegisters(resultGpr); + callOperation(toInt32, resultGpr, fpr); + silentFillAllRegisters(resultGpr); + + JITCompiler::Jump converted = m_jit.jump(); + + isInteger.link(&m_jit); + m_jit.move(payloadGPR, resultGpr); + + converted.link(&m_jit); + } +#endif + integerResult(resultGpr, m_compileIndex); + return; + } + case GeneratedOperandTypeUnknown: + ASSERT_NOT_REACHED(); + break; + } + } + + if (at(node.child1()).shouldSpeculateBoolean()) { + SpeculateBooleanOperand op1(this, node.child1()); + GPRTemporary result(this, op1); + + m_jit.and32(JITCompiler::TrustedImm32(1), op1.gpr()); + + integerResult(op1.gpr(), m_compileIndex); return; } - SpeculateIntegerOperand op1(this, node.child1()); - GPRTemporary result(this, op1); - m_jit.move(op1.gpr(), result.gpr()); - integerResult(result.gpr(), m_compileIndex, op1.format()); + // Do it the safe way. + nonSpeculativeValueToInt32(node); + return; } void SpeculativeJIT::compileUInt32ToNumber(Node& node) @@ -1547,26 +1719,105 @@ void SpeculativeJIT::compileUInt32ToNumber(Node& node) // 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()); + forwardSpeculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::LessThan, op1.gpr(), TrustedImm32(0)), ValueRecovery::uint32InGPR(op1.gpr())); m_jit.move(op1.gpr(), result.gpr()); integerResult(result.gpr(), m_compileIndex, op1.format()); } +void SpeculativeJIT::compileDoubleAsInt32(Node& node) +{ + SpeculateDoubleOperand op1(this, node.child1()); + FPRTemporary scratch(this); + GPRTemporary result(this); + + FPRReg valueFPR = op1.fpr(); + FPRReg scratchFPR = scratch.fpr(); + GPRReg resultGPR = result.gpr(); + + JITCompiler::JumpList failureCases; + m_jit.branchConvertDoubleToInt32(valueFPR, resultGPR, failureCases, scratchFPR); + forwardSpeculationCheck(Overflow, JSValueRegs(), NoNode, failureCases, ValueRecovery::inFPR(valueFPR)); + + integerResult(resultGPR, m_compileIndex); +} + +void SpeculativeJIT::compileInt32ToDouble(Node& node) +{ +#if USE(JSVALUE64) + // On JSVALUE64 we have a way of loading double constants in a more direct manner + // than a int->double conversion. On 32_64, unfortunately, we currently don't have + // any such mechanism - though we could have it, if we just provisioned some memory + // in CodeBlock for the double form of integer constants. + if (at(node.child1()).hasConstant()) { + ASSERT(isInt32Constant(node.child1().index())); + FPRTemporary result(this); + GPRTemporary temp(this); + m_jit.move(MacroAssembler::ImmPtr(reinterpret_cast<void*>(reinterpretDoubleToIntptr(valueOfNumberConstant(node.child1().index())))), temp.gpr()); + m_jit.movePtrToDouble(temp.gpr(), result.fpr()); + doubleResult(result.fpr(), m_compileIndex); + return; + } +#endif + + if (isInt32Prediction(m_state.forNode(node.child1()).m_type)) { + SpeculateIntegerOperand op1(this, node.child1()); + FPRTemporary result(this); + m_jit.convertInt32ToDouble(op1.gpr(), result.fpr()); + doubleResult(result.fpr(), m_compileIndex); + return; + } + + JSValueOperand op1(this, node.child1()); + FPRTemporary result(this); + +#if USE(JSVALUE64) + GPRTemporary temp(this); + + GPRReg op1GPR = op1.gpr(); + GPRReg tempGPR = temp.gpr(); + FPRReg resultFPR = result.fpr(); + + JITCompiler::Jump isInteger = m_jit.branchPtr( + MacroAssembler::AboveOrEqual, op1GPR, GPRInfo::tagTypeNumberRegister); + + speculationCheck( + BadType, JSValueRegs(op1GPR), node.child1(), + m_jit.branchTestPtr(MacroAssembler::Zero, op1GPR, GPRInfo::tagTypeNumberRegister)); + + m_jit.move(op1GPR, tempGPR); + unboxDouble(tempGPR, resultFPR); + JITCompiler::Jump done = m_jit.jump(); + + isInteger.link(&m_jit); + m_jit.convertInt32ToDouble(op1GPR, resultFPR); + done.link(&m_jit); +#else + FPRTemporary temp(this); + + GPRReg op1TagGPR = op1.tagGPR(); + GPRReg op1PayloadGPR = op1.payloadGPR(); + FPRReg tempFPR = temp.fpr(); + FPRReg resultFPR = result.fpr(); + + JITCompiler::Jump isInteger = m_jit.branch32( + MacroAssembler::Equal, op1TagGPR, TrustedImm32(JSValue::Int32Tag)); + + speculationCheck( + BadType, JSValueRegs(op1TagGPR, op1PayloadGPR), node.child1(), + m_jit.branch32(MacroAssembler::AboveOrEqual, op1TagGPR, TrustedImm32(JSValue::LowestTag))); + + unboxDouble(op1TagGPR, op1PayloadGPR, resultFPR, tempFPR); + JITCompiler::Jump done = m_jit.jump(); + + isInteger.link(&m_jit); + m_jit.convertInt32ToDouble(op1PayloadGPR, resultFPR); + done.link(&m_jit); +#endif + + doubleResult(resultFPR, m_compileIndex); +} + static double clampDoubleToByte(double d) { d += 0.5; @@ -1619,85 +1870,6 @@ static void compileClampDoubleToByte(JITCompiler& jit, GPRReg result, FPRReg sou } -void SpeculativeJIT::compilePutByValForByteArray(GPRReg base, GPRReg property, Node& node) -{ - NodeUse baseUse = node.child1(); - NodeUse valueUse = node.child3(); - - if (!isByteArrayPrediction(m_state.forNode(baseUse).m_type)) - speculationCheck(BadType, JSValueSource::unboxedCell(base), baseUse.index(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(base, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSByteArray::s_info))); - GPRTemporary value; - GPRReg valueGPR; - - if (at(valueUse).isConstant()) { - JSValue jsValue = valueOfJSConstant(valueUse.index()); - if (!jsValue.isNumber()) { - terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); - noResult(m_compileIndex); - return; - } - int clampedValue = clampDoubleToByte(jsValue.asNumber()); - GPRTemporary scratch(this); - GPRReg scratchReg = scratch.gpr(); - m_jit.move(Imm32(clampedValue), scratchReg); - value.adopt(scratch); - valueGPR = scratchReg; - } else if (!at(valueUse).shouldNotSpeculateInteger()) { - SpeculateIntegerOperand valueOp(this, valueUse); - GPRTemporary scratch(this); - GPRReg scratchReg = scratch.gpr(); - m_jit.move(valueOp.gpr(), scratchReg); - compileClampIntegerToByte(m_jit, scratchReg); - value.adopt(scratch); - valueGPR = scratchReg; - } else { - SpeculateDoubleOperand valueOp(this, valueUse); - 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()); @@ -1758,22 +1930,30 @@ void SpeculativeJIT::compileGetByValOnIntTypedArray(const TypedArrayDescriptor& ASSERT_NOT_REACHED(); } outOfBounds.link(&m_jit); - if (elementSize < 4 || signedness == SignedTypedArray) + 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); + return; } + + ASSERT(elementSize == 4 && signedness == UnsignedTypedArray); + if (node.shouldSpeculateInteger()) { + forwardSpeculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::LessThan, resultReg, TrustedImm32(0)), ValueRecovery::uint32InGPR(resultReg)); + integerResult(resultReg, m_compileIndex); + return; + } + + 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, TypedArrayRounding rounding) { - NodeUse baseUse = node.child1(); - NodeUse valueUse = node.child3(); + Edge baseUse = node.child1(); + Edge valueUse = node.child3(); if (speculationRequirements != NoTypedArrayTypeSpecCheck) speculationCheck(BadType, JSValueSource::unboxedCell(base), baseUse, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(base, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(descriptor.m_classInfo))); @@ -1797,7 +1977,7 @@ void SpeculativeJIT::compilePutByValForIntTypedArray(const TypedArrayDescriptor& m_jit.move(Imm32(static_cast<int>(d)), scratchReg); value.adopt(scratch); valueGPR = scratchReg; - } else if (!at(valueUse).shouldNotSpeculateInteger()) { + } else if (at(valueUse).shouldSpeculateInteger()) { SpeculateIntegerOperand valueOp(this, valueUse); GPRTemporary scratch(this); GPRReg scratchReg = scratch.gpr(); @@ -1918,8 +2098,8 @@ void SpeculativeJIT::compileGetByValOnFloatTypedArray(const TypedArrayDescriptor void SpeculativeJIT::compilePutByValForFloatTypedArray(const TypedArrayDescriptor& descriptor, GPRReg base, GPRReg property, Node& node, size_t elementSize, TypedArraySpeculationRequirements speculationRequirements) { - NodeUse baseUse = node.child1(); - NodeUse valueUse = node.child3(); + Edge baseUse = node.child1(); + Edge valueUse = node.child3(); SpeculateDoubleOperand valueOp(this, valueUse); @@ -1999,8 +2179,12 @@ void SpeculativeJIT::compileInstanceOfForObject(Node&, GPRReg valueReg, GPRReg p void SpeculativeJIT::compileInstanceOf(Node& node) { - if (!!(at(node.child1()).prediction() & ~PredictCell) && !!(m_state.forNode(node.child1()).m_type & ~PredictCell)) { + if ((!!(at(node.child1()).prediction() & ~PredictCell) + && !!(m_state.forNode(node.child1()).m_type & ~PredictCell)) + || at(node.child1()).adjustedRefCount() == 1) { // It might not be a cell. Speculate less aggressively. + // Or: it might only be used once (i.e. by us), so we get zero benefit + // from speculating any more aggressively than we absolutely need to. JSValueOperand value(this, node.child1()); SpeculateCellOperand prototype(this, node.child3()); @@ -2055,172 +2239,139 @@ void SpeculativeJIT::compileInstanceOf(Node& node) #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 CPU(X86) || CPU(X86_64) if (isInt32Constant(node.child2().index())) { int32_t divisor = valueOfInt32Constant(node.child2().index()); - 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) { + GPRReg op1Gpr = op1.gpr(); + GPRTemporary eax(this, X86Registers::eax); GPRTemporary edx(this, X86Registers::edx); GPRTemporary scratch(this); GPRReg scratchGPR = scratch.gpr(); + GPRReg op1SaveGPR; + if (op1Gpr == X86Registers::eax || op1Gpr == X86Registers::edx) { + op1SaveGPR = allocate(); + ASSERT(op1Gpr != op1SaveGPR); + m_jit.move(op1Gpr, op1SaveGPR); + } else + op1SaveGPR = op1Gpr; + ASSERT(op1SaveGPR != X86Registers::eax); + ASSERT(op1SaveGPR != X86Registers::edx); + m_jit.move(op1Gpr, eax.gpr()); m_jit.move(TrustedImm32(divisor), scratchGPR); + if (divisor == -1) + speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branch32(JITCompiler::Equal, eax.gpr(), TrustedImm32(-2147483647-1))); m_jit.assembler().cdq(); m_jit.assembler().idivl_r(scratchGPR); + // Check that we're not about to create negative zero. + // FIXME: if the node use doesn't care about neg zero, we can do this more easily. + JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, op1SaveGPR, TrustedImm32(0)); + speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branchTest32(JITCompiler::Zero, edx.gpr())); + numeratorPositive.link(&m_jit); + + if (op1SaveGPR != op1Gpr) + unlock(op1SaveGPR); + integerResult(edx.gpr(), m_compileIndex); return; } -#endif - // Fallback to non-constant case but avoid unnecessary checks. - shouldGeneratePowerOfTwoCheck = false; } +#endif 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); + GPRReg op1GPR = op1.gpr(); + GPRReg op2GPR = op2.gpr(); + + GPRReg op2TempGPR; + GPRReg temp; + GPRReg op1SaveGPR; + + if (op2GPR == X86Registers::eax || op2GPR == X86Registers::edx) { + op2TempGPR = allocate(); + temp = op2TempGPR; + } else { + op2TempGPR = InvalidGPRReg; + if (op1GPR == X86Registers::eax) + temp = X86Registers::edx; + else + temp = X86Registers::eax; } - -#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); + + if (op1GPR == X86Registers::eax || op1GPR == X86Registers::edx) { + op1SaveGPR = allocate(); + ASSERT(op1GPR != op1SaveGPR); + m_jit.move(op1GPR, op1SaveGPR); + } else + op1SaveGPR = op1GPR; + + ASSERT(temp != op1GPR); + ASSERT(temp != op2GPR); + ASSERT(op1SaveGPR != X86Registers::eax); + ASSERT(op1SaveGPR != X86Registers::edx); + + m_jit.add32(JITCompiler::TrustedImm32(1), op2GPR, temp); + + JITCompiler::Jump safeDenominator = m_jit.branch32(JITCompiler::Above, temp, JITCompiler::TrustedImm32(1)); + + JITCompiler::Jump done; + // FIXME: if the node is not used as number then we can do this more easily. + speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branchTest32(JITCompiler::Zero, op2GPR)); + speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branch32(JITCompiler::Equal, op1GPR, TrustedImm32(-2147483647-1))); + + safeDenominator.link(&m_jit); + + if (op2TempGPR != InvalidGPRReg) { + m_jit.move(op2GPR, op2TempGPR); + op2GPR = op2TempGPR; } + + m_jit.move(op1GPR, eax.gpr()); + m_jit.assembler().cdq(); + m_jit.assembler().idivl_r(op2GPR); + + if (op2TempGPR != InvalidGPRReg) + unlock(op2TempGPR); - JITCompiler::Jump lower = m_jit.branch32(JITCompiler::Below, resultGPR, scratchGPR2); - m_jit.sub32(scratchGPR2, resultGPR); - lower.link(&m_jit); + // Check that we're not about to create negative zero. + // FIXME: if the node use doesn't care about neg zero, we can do this more easily. + JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, op1SaveGPR, TrustedImm32(0)); + speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branchTest32(JITCompiler::Zero, edx.gpr())); + numeratorPositive.link(&m_jit); + + if (op1SaveGPR != op1GPR) + unlock(op1SaveGPR); + + integerResult(edx.gpr(), m_compileIndex); +#else // CPU(X86) || CPU(X86_64) --> so not X86 + // Do this the *safest* way possible: call out to a C function that will do the modulo, + // and then attempt to convert back. + GPRReg op1GPR = op1.gpr(); + GPRReg op2GPR = op2.gpr(); + + FPRResult result(this); + + flushRegisters(); + callOperation(operationFModOnInts, result.fpr(), op1GPR, op2GPR); + + FPRTemporary scratch(this); + GPRTemporary intResult(this); + JITCompiler::JumpList failureCases; + m_jit.branchConvertDoubleToInt32(result.fpr(), intResult.gpr(), failureCases, scratch.fpr()); + speculationCheck(Overflow, JSValueRegs(), NoNode, failureCases); + + integerResult(intResult.gpr(), m_compileIndex); #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::compileAdd(Node& node) @@ -2299,7 +2450,7 @@ void SpeculativeJIT::compileAdd(Node& node) return; } - if (node.op == ValueAdd) { + if (node.op() == ValueAdd) { compileValueAdd(node); return; } @@ -2444,9 +2595,85 @@ void SpeculativeJIT::compileArithMul(Node& node) doubleResult(result.fpr(), m_compileIndex); } +#if CPU(X86) || CPU(X86_64) +void SpeculativeJIT::compileIntegerArithDivForX86(Node& node) +{ + SpeculateIntegerOperand op1(this, node.child1()); + SpeculateIntegerOperand op2(this, node.child2()); + GPRTemporary eax(this, X86Registers::eax); + GPRTemporary edx(this, X86Registers::edx); + GPRReg op1GPR = op1.gpr(); + GPRReg op2GPR = op2.gpr(); + + GPRReg op2TempGPR; + GPRReg temp; + if (op2GPR == X86Registers::eax || op2GPR == X86Registers::edx) { + op2TempGPR = allocate(); + temp = op2TempGPR; + } else { + op2TempGPR = InvalidGPRReg; + if (op1GPR == X86Registers::eax) + temp = X86Registers::edx; + else + temp = X86Registers::eax; + } + + ASSERT(temp != op1GPR); + ASSERT(temp != op2GPR); + + m_jit.add32(JITCompiler::TrustedImm32(1), op2GPR, temp); + + JITCompiler::Jump safeDenominator = m_jit.branch32(JITCompiler::Above, temp, JITCompiler::TrustedImm32(1)); + + JITCompiler::Jump done; + if (nodeUsedAsNumber(node.arithNodeFlags())) { + speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branchTest32(JITCompiler::Zero, op2GPR)); + speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branch32(JITCompiler::Equal, op1GPR, TrustedImm32(-2147483647-1))); + } else { + JITCompiler::Jump zero = m_jit.branchTest32(JITCompiler::Zero, op2GPR); + JITCompiler::Jump notNeg2ToThe31 = m_jit.branch32(JITCompiler::Equal, op1GPR, TrustedImm32(-2147483647-1)); + zero.link(&m_jit); + m_jit.move(TrustedImm32(0), eax.gpr()); + done = m_jit.jump(); + notNeg2ToThe31.link(&m_jit); + } + + safeDenominator.link(&m_jit); + + // If the user cares about negative zero, then speculate that we're not about + // to produce negative zero. + if (!nodeCanIgnoreNegativeZero(node.arithNodeFlags())) { + MacroAssembler::Jump numeratorNonZero = m_jit.branchTest32(MacroAssembler::NonZero, op1GPR); + speculationCheck(NegativeZero, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::LessThan, op2GPR, TrustedImm32(0))); + numeratorNonZero.link(&m_jit); + } + + if (op2TempGPR != InvalidGPRReg) { + m_jit.move(op2GPR, op2TempGPR); + op2GPR = op2TempGPR; + } + + m_jit.move(op1GPR, eax.gpr()); + m_jit.assembler().cdq(); + m_jit.assembler().idivl_r(op2GPR); + + if (op2TempGPR != InvalidGPRReg) + unlock(op2TempGPR); + + // Check that there was no remainder. If there had been, then we'd be obligated to + // produce a double result instead. + if (nodeUsedAsNumber(node.arithNodeFlags())) + speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branchTest32(JITCompiler::NonZero, edx.gpr())); + else + done.link(&m_jit); + + integerResult(eax.gpr(), m_compileIndex); +} +#endif // CPU(X86) || CPU(X86_64) + void SpeculativeJIT::compileArithMod(Node& node) { - if (!at(node.child1()).shouldNotSpeculateInteger() && !at(node.child2()).shouldNotSpeculateInteger() + if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())) && node.canSpeculateInteger()) { compileSoftModulo(node); return; @@ -2473,21 +2700,65 @@ bool SpeculativeJIT::compare(Node& node, MacroAssembler::RelationalCondition con if (compilePeepHoleBranch(node, condition, doubleCondition, operation)) return true; - if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2()))) + if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2()))) { compileIntegerCompare(node, condition); - else if (Node::shouldSpeculateNumber(at(node.child1()), at(node.child2()))) + return false; + } + + if (Node::shouldSpeculateNumber(at(node.child1()), at(node.child2()))) { compileDoubleCompare(node, doubleCondition); - else if (node.op == CompareEq && Node::shouldSpeculateFinalObject(at(node.child1()), at(node.child2()))) - compileObjectEquality(node, &JSFinalObject::s_info, isFinalObjectPrediction); - else if (node.op == CompareEq && Node::shouldSpeculateArray(at(node.child1()), at(node.child2()))) - compileObjectEquality(node, &JSArray::s_info, isArrayPrediction); - else - nonSpeculativeNonPeepholeCompare(node, condition, operation); + return false; + } + if (node.op() == CompareEq) { + if (Node::shouldSpeculateFinalObject(at(node.child1()), at(node.child2()))) { + compileObjectEquality(node, &JSFinalObject::s_info, isFinalObjectPrediction); + return false; + } + + if (Node::shouldSpeculateArray(at(node.child1()), at(node.child2()))) { + compileObjectEquality(node, &JSArray::s_info, isArrayPrediction); + return false; + } + + if (at(node.child1()).shouldSpeculateFinalObject() + && at(node.child2()).shouldSpeculateFinalObjectOrOther()) { + compileObjectToObjectOrOtherEquality( + node.child1(), node.child2(), &JSFinalObject::s_info, + isFinalObjectPrediction); + return false; + } + + if (at(node.child1()).shouldSpeculateFinalObjectOrOther() + && at(node.child2()).shouldSpeculateFinalObject()) { + compileObjectToObjectOrOtherEquality( + node.child2(), node.child1(), &JSFinalObject::s_info, + isFinalObjectPrediction); + return false; + } + + if (at(node.child1()).shouldSpeculateArray() + && at(node.child2()).shouldSpeculateArrayOrOther()) { + compileObjectToObjectOrOtherEquality( + node.child1(), node.child2(), &JSArray::s_info, + isArrayPrediction); + return false; + } + + if (at(node.child1()).shouldSpeculateArrayOrOther() + && at(node.child2()).shouldSpeculateArray()) { + compileObjectToObjectOrOtherEquality( + node.child2(), node.child1(), &JSArray::s_info, + isArrayPrediction); + return false; + } + } + + nonSpeculativeNonPeepholeCompare(node, condition, operation); return false; } -bool SpeculativeJIT::compileStrictEqForConstant(Node& node, NodeUse value, JSValue constant) +bool SpeculativeJIT::compileStrictEqForConstant(Node& node, Edge value, JSValue constant) { JSValueOperand op1(this, value); @@ -2652,7 +2923,7 @@ bool SpeculativeJIT::compileStrictEq(Node& node) void SpeculativeJIT::compileGetIndexedPropertyStorage(Node& node) { if (!node.prediction() || !at(node.child1()).prediction() || !at(node.child2()).prediction()) { - terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); + terminateSpeculativeExecution(InadequateCoverage, JSValueRegs(), NoNode); return; } @@ -2679,10 +2950,6 @@ void SpeculativeJIT::compileGetIndexedPropertyStorage(Node& node) speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchTest32(MacroAssembler::Zero, storageReg)); m_jit.loadPtr(MacroAssembler::Address(storageReg, StringImpl::dataOffset()), storageReg); - } else if (at(node.child1()).shouldSpeculateByteArray()) { - if (!isByteArrayPrediction(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSByteArray::s_info))); - m_jit.loadPtr(MacroAssembler::Address(baseReg, JSByteArray::offsetOfStorage()), storageReg); } else if (at(node.child1()).shouldSpeculateInt8Array()) { const TypedArrayDescriptor& descriptor = m_jit.globalData()->int8ArrayDescriptor(); if (!isInt8ArrayPrediction(m_state.forNode(node.child1()).m_type)) @@ -2758,6 +3025,46 @@ void SpeculativeJIT::compileNewFunctionExpression(Node& node) cellResult(resultGPR, m_compileIndex); } +bool SpeculativeJIT::compileRegExpExec(Node& node) +{ + unsigned branchIndexInBlock = detectPeepHoleBranch(); + if (branchIndexInBlock == UINT_MAX) + return false; + NodeIndex branchNodeIndex = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); + ASSERT(node.adjustedRefCount() == 1); + + Node& branchNode = at(branchNodeIndex); + BlockIndex taken = branchNode.takenBlockIndex(); + BlockIndex notTaken = branchNode.notTakenBlockIndex(); + + bool invert = false; + if (taken == (m_block + 1)) { + invert = true; + BlockIndex tmp = taken; + taken = notTaken; + notTaken = tmp; + } + + SpeculateCellOperand base(this, node.child1()); + SpeculateCellOperand argument(this, node.child2()); + GPRReg baseGPR = base.gpr(); + GPRReg argumentGPR = argument.gpr(); + + flushRegisters(); + GPRResult result(this); + callOperation(operationRegExpTest, result.gpr(), baseGPR, argumentGPR); + + branchTest32(invert ? JITCompiler::Zero : JITCompiler::NonZero, result.gpr(), taken); + jump(notTaken); + + use(node.child1()); + use(node.child2()); + m_indexInBlock = branchIndexInBlock; + m_compileIndex = branchNodeIndex; + + return true; +} + } } // namespace JSC::DFG #endif |