diff options
author | Oswald Buddenhagen <oswald.buddenhagen@qt.io> | 2017-05-30 12:48:17 +0200 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@qt.io> | 2017-05-30 12:48:17 +0200 |
commit | 881da28418d380042aa95a97f0cbd42560a64f7c (patch) | |
tree | a794dff3274695e99c651902dde93d934ea7a5af /Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp | |
parent | 7e104c57a70fdf551bb3d22a5d637cdcbc69dbea (diff) | |
parent | 0fcedcd17cc00d3dd44c718b3cb36c1033319671 (diff) | |
download | qtwebkit-881da28418d380042aa95a97f0cbd42560a64f7c.tar.gz |
Merge 'wip/next' into dev
Change-Id: Iff9ee5e23bb326c4371ec8ed81d56f2f05d680e9
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp')
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp | 6783 |
1 files changed, 4849 insertions, 1934 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index 07312e036..de2025ada 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2011-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,12 +28,34 @@ #if ENABLE(DFG_JIT) -#include "Arguments.h" +#include "BinarySwitch.h" +#include "DFGAbstractInterpreterInlines.h" #include "DFGArrayifySlowPathGenerator.h" #include "DFGCallArrayAllocatorSlowPathGenerator.h" +#include "DFGCallCreateDirectArgumentsSlowPathGenerator.h" +#include "DFGMayExit.h" +#include "DFGOSRExitFuzz.h" +#include "DFGSaneStringGetByValSlowPathGenerator.h" #include "DFGSlowPathGenerator.h" -#include "JSCJSValueInlines.h" +#include "DirectArguments.h" +#include "JITAddGenerator.h" +#include "JITBitAndGenerator.h" +#include "JITBitOrGenerator.h" +#include "JITBitXorGenerator.h" +#include "JITDivGenerator.h" +#include "JITLeftShiftGenerator.h" +#include "JITMulGenerator.h" +#include "JITRightShiftGenerator.h" +#include "JITSubGenerator.h" +#include "JSCInlines.h" +#include "JSEnvironmentRecord.h" +#include "JSGeneratorFunction.h" +#include "JSLexicalEnvironment.h" #include "LinkBuffer.h" +#include "ScopedArguments.h" +#include "ScratchRegisterAllocator.h" +#include "WriteBarrierBuffer.h" +#include <wtf/MathExtras.h> namespace JSC { namespace DFG { @@ -41,16 +63,13 @@ SpeculativeJIT::SpeculativeJIT(JITCompiler& jit) : m_compileOkay(true) , m_jit(jit) , m_currentNode(0) + , m_lastGeneratedNode(LastNodeType) , m_indexInBlock(0) - , m_generationInfo(m_jit.codeBlock()->m_numCalleeRegisters) - , m_blockHeads(jit.graph().m_blocks.size()) - , m_arguments(jit.codeBlock()->numParameters()) - , m_variables(jit.graph().m_localVars) - , m_lastSetOperand(std::numeric_limits<int>::max()) + , m_generationInfo(m_jit.graph().frameRegisterCount()) , m_state(m_jit.graph()) - , m_stream(&jit.codeBlock()->variableEventStream()) - , m_minifiedGraph(&jit.codeBlock()->minifiedDFG()) - , m_isCheckingArgumentTypes(false) + , m_interpreter(m_jit.graph(), m_state) + , m_stream(&jit.jitCode()->variableEventStream) + , m_minifiedGraph(&jit.jitCode()->minifiedDFG) { } @@ -81,12 +100,12 @@ void SpeculativeJIT::emitAllocateJSArray(GPRReg resultGPR, Structure* structure, if (hasDouble(structure->indexingType()) && numElements < vectorLength) { #if USE(JSVALUE64) - m_jit.move(TrustedImm64(bitwise_cast<int64_t>(QNaN)), scratchGPR); + m_jit.move(TrustedImm64(bitwise_cast<int64_t>(PNaN)), scratchGPR); for (unsigned i = numElements; i < vectorLength; ++i) m_jit.store64(scratchGPR, MacroAssembler::Address(storageGPR, sizeof(double) * i)); #else EncodedValueDescriptor value; - value.asInt64 = JSValue::encode(JSValue(JSValue::EncodeAsDouble, QNaN)); + value.asInt64 = JSValue::encode(JSValue(JSValue::EncodeAsDouble, PNaN)); for (unsigned i = numElements; i < vectorLength; ++i) { m_jit.store32(TrustedImm32(value.asBits.tag), MacroAssembler::Address(storageGPR, sizeof(double) * i + OBJECT_OFFSETOF(JSValue, u.asBits.tag))); m_jit.store32(TrustedImm32(value.asBits.payload), MacroAssembler::Address(storageGPR, sizeof(double) * i + OBJECT_OFFSETOF(JSValue, u.asBits.payload))); @@ -97,268 +116,232 @@ void SpeculativeJIT::emitAllocateJSArray(GPRReg resultGPR, Structure* structure, // I want a slow path that also loads out the storage pointer, and that's // what this custom CallArrayAllocatorSlowPathGenerator gives me. It's a lot // of work for a very small piece of functionality. :-/ - addSlowPathGenerator(adoptPtr( - new CallArrayAllocatorSlowPathGenerator( - slowCases, this, operationNewArrayWithSize, resultGPR, storageGPR, - structure, numElements))); + addSlowPathGenerator(std::make_unique<CallArrayAllocatorSlowPathGenerator>( + slowCases, this, operationNewArrayWithSize, resultGPR, storageGPR, + structure, numElements)); } -void SpeculativeJIT::backwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, MacroAssembler::Jump jumpToFail) +void SpeculativeJIT::emitGetLength(InlineCallFrame* inlineCallFrame, GPRReg lengthGPR, bool includeThis) { - if (!m_compileOkay) - return; - ASSERT(m_isCheckingArgumentTypes || m_canExit); - m_jit.appendExitInfo(jumpToFail); - m_jit.codeBlock()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(node), this, m_stream->size())); + if (inlineCallFrame && !inlineCallFrame->isVarargs()) + m_jit.move(TrustedImm32(inlineCallFrame->arguments.size() - !includeThis), lengthGPR); + else { + VirtualRegister argumentCountRegister; + if (!inlineCallFrame) + argumentCountRegister = VirtualRegister(JSStack::ArgumentCount); + else + argumentCountRegister = inlineCallFrame->argumentCountRegister; + m_jit.load32(JITCompiler::payloadFor(argumentCountRegister), lengthGPR); + if (!includeThis) + m_jit.sub32(TrustedImm32(1), lengthGPR); + } } -void SpeculativeJIT::backwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, const MacroAssembler::JumpList& jumpsToFail) +void SpeculativeJIT::emitGetLength(CodeOrigin origin, GPRReg lengthGPR, bool includeThis) { - if (!m_compileOkay) - return; - ASSERT(m_isCheckingArgumentTypes || m_canExit); - m_jit.appendExitInfo(jumpsToFail); - m_jit.codeBlock()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(node), this, m_stream->size())); + emitGetLength(origin.inlineCallFrame, lengthGPR, includeThis); } -void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, MacroAssembler::Jump jumpToFail) +void SpeculativeJIT::emitGetCallee(CodeOrigin origin, GPRReg calleeGPR) { - if (!m_compileOkay) - return; - backwardSpeculationCheck(kind, jsValueSource, node, jumpToFail); - if (m_speculationDirection == ForwardSpeculation) - convertLastOSRExitToForward(); + if (origin.inlineCallFrame) { + if (origin.inlineCallFrame->isClosureCall) { + m_jit.loadPtr( + JITCompiler::addressFor(origin.inlineCallFrame->calleeRecovery.virtualRegister()), + calleeGPR); + } else { + m_jit.move( + TrustedImmPtr(origin.inlineCallFrame->calleeRecovery.constant().asCell()), + calleeGPR); + } + } else + m_jit.loadPtr(JITCompiler::addressFor(JSStack::Callee), calleeGPR); } -void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail) +void SpeculativeJIT::emitGetArgumentStart(CodeOrigin origin, GPRReg startGPR) { - ASSERT(m_isCheckingArgumentTypes || m_canExit); - speculationCheck(kind, jsValueSource, nodeUse.node(), jumpToFail); + m_jit.addPtr( + TrustedImm32( + JITCompiler::argumentsStart(origin).offset() * static_cast<int>(sizeof(Register))), + GPRInfo::callFrameRegister, startGPR); } -OSRExitJumpPlaceholder SpeculativeJIT::backwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node) +MacroAssembler::Jump SpeculativeJIT::emitOSRExitFuzzCheck() { - if (!m_compileOkay) - return OSRExitJumpPlaceholder(); - ASSERT(m_isCheckingArgumentTypes || m_canExit); - unsigned index = m_jit.codeBlock()->numberOfOSRExits(); - m_jit.appendExitInfo(); - m_jit.codeBlock()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(node), this, m_stream->size())); - return OSRExitJumpPlaceholder(index); + if (!doOSRExitFuzzing()) + return MacroAssembler::Jump(); + + MacroAssembler::Jump result; + + m_jit.pushToSave(GPRInfo::regT0); + m_jit.load32(&g_numberOfOSRExitFuzzChecks, GPRInfo::regT0); + m_jit.add32(TrustedImm32(1), GPRInfo::regT0); + m_jit.store32(GPRInfo::regT0, &g_numberOfOSRExitFuzzChecks); + unsigned atOrAfter = Options::fireOSRExitFuzzAtOrAfter(); + unsigned at = Options::fireOSRExitFuzzAt(); + if (at || atOrAfter) { + unsigned threshold; + MacroAssembler::RelationalCondition condition; + if (atOrAfter) { + threshold = atOrAfter; + condition = MacroAssembler::Below; + } else { + threshold = at; + condition = MacroAssembler::NotEqual; + } + MacroAssembler::Jump ok = m_jit.branch32( + condition, GPRInfo::regT0, MacroAssembler::TrustedImm32(threshold)); + m_jit.popToRestore(GPRInfo::regT0); + result = m_jit.jump(); + ok.link(&m_jit); + } + m_jit.popToRestore(GPRInfo::regT0); + + return result; } -OSRExitJumpPlaceholder SpeculativeJIT::backwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse) +void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, MacroAssembler::Jump jumpToFail) { - ASSERT(m_isCheckingArgumentTypes || m_canExit); - return backwardSpeculationCheck(kind, jsValueSource, nodeUse.node()); + if (!m_compileOkay) + return; + JITCompiler::Jump fuzzJump = emitOSRExitFuzzCheck(); + if (fuzzJump.isSet()) { + JITCompiler::JumpList jumpsToFail; + jumpsToFail.append(fuzzJump); + jumpsToFail.append(jumpToFail); + m_jit.appendExitInfo(jumpsToFail); + } else + m_jit.appendExitInfo(jumpToFail); + m_jit.jitCode()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(node), this, m_stream->size())); } void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, const MacroAssembler::JumpList& jumpsToFail) { if (!m_compileOkay) return; - backwardSpeculationCheck(kind, jsValueSource, node, jumpsToFail); - if (m_speculationDirection == ForwardSpeculation) - convertLastOSRExitToForward(); -} - -void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, const MacroAssembler::JumpList& jumpsToFail) -{ - ASSERT(m_isCheckingArgumentTypes || m_canExit); - speculationCheck(kind, jsValueSource, nodeUse.node(), jumpsToFail); + JITCompiler::Jump fuzzJump = emitOSRExitFuzzCheck(); + if (fuzzJump.isSet()) { + JITCompiler::JumpList myJumpsToFail; + myJumpsToFail.append(jumpsToFail); + myJumpsToFail.append(fuzzJump); + m_jit.appendExitInfo(myJumpsToFail); + } else + m_jit.appendExitInfo(jumpsToFail); + m_jit.jitCode()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(node), this, m_stream->size())); } -void SpeculativeJIT::backwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery) +OSRExitJumpPlaceholder SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node) { if (!m_compileOkay) - return; - ASSERT(m_isCheckingArgumentTypes || m_canExit); - m_jit.codeBlock()->appendSpeculationRecovery(recovery); - m_jit.appendExitInfo(jumpToFail); - m_jit.codeBlock()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(node), this, m_stream->size(), m_jit.codeBlock()->numberOfSpeculationRecoveries())); + return OSRExitJumpPlaceholder(); + unsigned index = m_jit.jitCode()->osrExit.size(); + m_jit.appendExitInfo(); + m_jit.jitCode()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(node), this, m_stream->size())); + return OSRExitJumpPlaceholder(index); } -void SpeculativeJIT::backwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery) +OSRExitJumpPlaceholder SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse) { - ASSERT(m_isCheckingArgumentTypes || m_canExit); - backwardSpeculationCheck(kind, jsValueSource, nodeUse.node(), jumpToFail, recovery); + return speculationCheck(kind, jsValueSource, nodeUse.node()); } -void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery) +void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail) { - if (!m_compileOkay) - return; - backwardSpeculationCheck(kind, jsValueSource, node, jumpToFail, recovery); - if (m_speculationDirection == ForwardSpeculation) - convertLastOSRExitToForward(); + speculationCheck(kind, jsValueSource, nodeUse.node(), jumpToFail); } -void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge edge, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery) +void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, const MacroAssembler::JumpList& jumpsToFail) { - speculationCheck(kind, jsValueSource, edge.node(), jumpToFail, recovery); + speculationCheck(kind, jsValueSource, nodeUse.node(), jumpsToFail); } -JumpReplacementWatchpoint* SpeculativeJIT::speculationWatchpoint(ExitKind kind, JSValueSource jsValueSource, Node* node) +void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery) { if (!m_compileOkay) - return 0; - ASSERT(m_isCheckingArgumentTypes || m_canExit); - m_jit.appendExitInfo(JITCompiler::JumpList()); - OSRExit& exit = m_jit.codeBlock()->osrExit( - m_jit.codeBlock()->appendOSRExit(OSRExit( - kind, jsValueSource, - m_jit.graph().methodOfGettingAValueProfileFor(node), - this, m_stream->size()))); - exit.m_watchpointIndex = m_jit.codeBlock()->appendWatchpoint( - JumpReplacementWatchpoint(m_jit.watchpointLabel())); - if (m_speculationDirection == ForwardSpeculation) - convertLastOSRExitToForward(); - return &m_jit.codeBlock()->watchpoint(exit.m_watchpointIndex); -} - -JumpReplacementWatchpoint* SpeculativeJIT::speculationWatchpoint(ExitKind kind) -{ - return speculationWatchpoint(kind, JSValueSource(), 0); -} - -void SpeculativeJIT::convertLastOSRExitToForward(const ValueRecovery& valueRecovery) -{ - if (!valueRecovery) { - // Check that either the current node is a SetLocal, or the preceding node was a - // SetLocal with the same code origin. - if (!m_currentNode->containsMovHint()) { - Node* setLocal = m_jit.graph().m_blocks[m_block]->at(m_indexInBlock - 1); - ASSERT_UNUSED(setLocal, setLocal->containsMovHint()); - ASSERT_UNUSED(setLocal, setLocal->codeOrigin == m_currentNode->codeOrigin); - } - - // Find the next node. - unsigned indexInBlock = m_indexInBlock + 1; - Node* node = 0; - for (;;) { - if (indexInBlock == m_jit.graph().m_blocks[m_block]->size()) { - // This is an inline return. Give up and do a backwards speculation. This is safe - // because an inline return has its own bytecode index and it's always safe to - // reexecute that bytecode. - ASSERT(node->op() == Jump); - return; - } - node = m_jit.graph().m_blocks[m_block]->at(indexInBlock); - if (node->codeOrigin != m_currentNode->codeOrigin) - break; - indexInBlock++; - } - - ASSERT(node->codeOrigin != m_currentNode->codeOrigin); - OSRExit& exit = m_jit.codeBlock()->lastOSRExit(); - exit.m_codeOrigin = node->codeOrigin; return; - } - - unsigned setLocalIndexInBlock = m_indexInBlock + 1; - - Node* setLocal = m_jit.graph().m_blocks[m_block]->at(setLocalIndexInBlock); - bool hadInt32ToDouble = false; - - if (setLocal->op() == ForwardInt32ToDouble) { - setLocal = m_jit.graph().m_blocks[m_block]->at(++setLocalIndexInBlock); - hadInt32ToDouble = true; - } - if (setLocal->op() == Flush || setLocal->op() == Phantom) - setLocal = m_jit.graph().m_blocks[m_block]->at(++setLocalIndexInBlock); - - if (hadInt32ToDouble) - ASSERT(setLocal->child1()->child1() == m_currentNode); - else - ASSERT(setLocal->child1() == m_currentNode); - ASSERT(setLocal->containsMovHint()); - ASSERT(setLocal->codeOrigin == m_currentNode->codeOrigin); - - Node* nextNode = m_jit.graph().m_blocks[m_block]->at(setLocalIndexInBlock + 1); - if (nextNode->op() == Jump && nextNode->codeOrigin == m_currentNode->codeOrigin) { - // We're at an inlined return. Use a backward speculation instead. - return; - } - ASSERT(nextNode->codeOrigin != m_currentNode->codeOrigin); - - OSRExit& exit = m_jit.codeBlock()->lastOSRExit(); - exit.m_codeOrigin = nextNode->codeOrigin; - - exit.m_lastSetOperand = setLocal->local(); - exit.m_valueRecoveryOverride = adoptRef( - new ValueRecoveryOverride(setLocal->local(), valueRecovery)); + unsigned recoveryIndex = m_jit.jitCode()->appendSpeculationRecovery(recovery); + m_jit.appendExitInfo(jumpToFail); + m_jit.jitCode()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(node), this, m_stream->size(), recoveryIndex)); } -void SpeculativeJIT::forwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, MacroAssembler::Jump jumpToFail, const ValueRecovery& valueRecovery) +void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery) { - ASSERT(m_isCheckingArgumentTypes || m_canExit); - backwardSpeculationCheck(kind, jsValueSource, node, jumpToFail); - convertLastOSRExitToForward(valueRecovery); + speculationCheck(kind, jsValueSource, nodeUse.node(), jumpToFail, recovery); } -void SpeculativeJIT::forwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, const MacroAssembler::JumpList& jumpsToFail, const ValueRecovery& valueRecovery) +void SpeculativeJIT::emitInvalidationPoint(Node* node) { - ASSERT(m_isCheckingArgumentTypes || m_canExit); - backwardSpeculationCheck(kind, jsValueSource, node, jumpsToFail); - convertLastOSRExitToForward(valueRecovery); + if (!m_compileOkay) + return; + OSRExitCompilationInfo& info = m_jit.appendExitInfo(JITCompiler::JumpList()); + m_jit.jitCode()->appendOSRExit(OSRExit( + UncountableInvalidation, JSValueSource(), + m_jit.graph().methodOfGettingAValueProfileFor(node), + this, m_stream->size())); + info.m_replacementSource = m_jit.watchpointLabel(); + ASSERT(info.m_replacementSource.isSet()); + noResult(node); } void SpeculativeJIT::terminateSpeculativeExecution(ExitKind kind, JSValueRegs jsValueRegs, Node* node) { - ASSERT(m_isCheckingArgumentTypes || m_canExit); -#if DFG_ENABLE(DEBUG_VERBOSE) - dataLogF("SpeculativeJIT was terminated.\n"); -#endif if (!m_compileOkay) return; speculationCheck(kind, jsValueRegs, node, m_jit.jump()); m_compileOkay = false; + if (verboseCompilationEnabled()) + dataLog("Bailing compilation.\n"); } void SpeculativeJIT::terminateSpeculativeExecution(ExitKind kind, JSValueRegs jsValueRegs, Edge nodeUse) { - ASSERT(m_isCheckingArgumentTypes || m_canExit); terminateSpeculativeExecution(kind, jsValueRegs, nodeUse.node()); } -void SpeculativeJIT::backwardTypeCheck(JSValueSource source, Edge edge, SpeculatedType typesPassedThrough, MacroAssembler::Jump jumpToFail) +void SpeculativeJIT::typeCheck(JSValueSource source, Edge edge, SpeculatedType typesPassedThrough, MacroAssembler::Jump jumpToFail, ExitKind exitKind) { ASSERT(needsTypeCheck(edge, typesPassedThrough)); - m_state.forNode(edge).filter(typesPassedThrough); - backwardSpeculationCheck(BadType, source, edge.node(), jumpToFail); + m_interpreter.filter(edge, typesPassedThrough); + speculationCheck(exitKind, source, edge.node(), jumpToFail); } -void SpeculativeJIT::typeCheck(JSValueSource source, Edge edge, SpeculatedType typesPassedThrough, MacroAssembler::Jump jumpToFail) +RegisterSet SpeculativeJIT::usedRegisters() { - backwardTypeCheck(source, edge, typesPassedThrough, jumpToFail); - if (m_speculationDirection == ForwardSpeculation) - convertLastOSRExitToForward(); -} - -void SpeculativeJIT::forwardTypeCheck(JSValueSource source, Edge edge, SpeculatedType typesPassedThrough, MacroAssembler::Jump jumpToFail, const ValueRecovery& valueRecovery) -{ - backwardTypeCheck(source, edge, typesPassedThrough, jumpToFail); - convertLastOSRExitToForward(valueRecovery); + RegisterSet result; + + for (unsigned i = GPRInfo::numberOfRegisters; i--;) { + GPRReg gpr = GPRInfo::toRegister(i); + if (m_gprs.isInUse(gpr)) + result.set(gpr); + } + for (unsigned i = FPRInfo::numberOfRegisters; i--;) { + FPRReg fpr = FPRInfo::toRegister(i); + if (m_fprs.isInUse(fpr)) + result.set(fpr); + } + + result.merge(RegisterSet::stubUnavailableRegisters()); + + return result; } -void SpeculativeJIT::addSlowPathGenerator(PassOwnPtr<SlowPathGenerator> slowPathGenerator) +void SpeculativeJIT::addSlowPathGenerator(std::unique_ptr<SlowPathGenerator> slowPathGenerator) { - m_slowPathGenerators.append(slowPathGenerator); + m_slowPathGenerators.append(WTFMove(slowPathGenerator)); } -void SpeculativeJIT::runSlowPathGenerators() +void SpeculativeJIT::runSlowPathGenerators(PCToCodeOriginMapBuilder& pcToCodeOriginMapBuilder) { -#if DFG_ENABLE(DEBUG_VERBOSE) - dataLogF("Running %lu slow path generators.\n", m_slowPathGenerators.size()); -#endif - for (unsigned i = 0; i < m_slowPathGenerators.size(); ++i) + for (unsigned i = 0; i < m_slowPathGenerators.size(); ++i) { + pcToCodeOriginMapBuilder.appendItem(m_jit.label(), m_slowPathGenerators[i]->origin().semantic); m_slowPathGenerators[i]->generate(this); + } } // 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) +static double JIT_OPERATION fmodAsDFGOperation(double x, double y) { return fmod(x, y); } @@ -376,7 +359,7 @@ void SpeculativeJIT::clearGenerationInfo() SilentRegisterSavePlan SpeculativeJIT::silentSavePlanForGPR(VirtualRegister spillMe, GPRReg source) { - GenerationInfo& info = m_generationInfo[spillMe]; + GenerationInfo& info = generationInfoFromVirtualRegister(spillMe); Node* node = info.node(); DataFormat registerFormat = info.registerFormat(); ASSERT(registerFormat != DataFormatNone); @@ -390,10 +373,12 @@ SilentRegisterSavePlan SpeculativeJIT::silentSavePlanForGPR(VirtualRegister spil else { #if USE(JSVALUE64) ASSERT(info.gpr() == source); - if (registerFormat == DataFormatInteger) + if (registerFormat == DataFormatInt32) spillAction = Store32Payload; else if (registerFormat == DataFormatCell || registerFormat == DataFormatStorage) spillAction = StorePtr; + else if (registerFormat == DataFormatInt52 || registerFormat == DataFormatStrictInt52) + spillAction = Store64; else { ASSERT(registerFormat & DataFormatJS); spillAction = Store64; @@ -409,22 +394,24 @@ SilentRegisterSavePlan SpeculativeJIT::silentSavePlanForGPR(VirtualRegister spil #endif } - if (registerFormat == DataFormatInteger) { + if (registerFormat == DataFormatInt32) { ASSERT(info.gpr() == source); - ASSERT(isJSInteger(info.registerFormat())); + ASSERT(isJSInt32(info.registerFormat())); if (node->hasConstant()) { - ASSERT(isInt32Constant(node)); + ASSERT(node->isInt32Constant()); fillAction = SetInt32Constant; } else fillAction = Load32Payload; } else if (registerFormat == DataFormatBoolean) { #if USE(JSVALUE64) RELEASE_ASSERT_NOT_REACHED(); +#if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE) fillAction = DoNothingForFill; +#endif #elif USE(JSVALUE32_64) ASSERT(info.gpr() == source); if (node->hasConstant()) { - ASSERT(isBooleanConstant(node)); + ASSERT(node->isBooleanConstant()); fillAction = SetBooleanConstant; } else fillAction = Load32Payload; @@ -432,8 +419,8 @@ SilentRegisterSavePlan SpeculativeJIT::silentSavePlanForGPR(VirtualRegister spil } else if (registerFormat == DataFormatCell) { ASSERT(info.gpr() == source); if (node->hasConstant()) { - JSValue value = valueOfJSConstant(node); - ASSERT_UNUSED(value, value.isCell()); + DFG_ASSERT(m_jit.graph(), m_currentNode, node->isCellConstant()); + node->asCell(); // To get the assertion. fillAction = SetCellConstant; } else { #if USE(JSVALUE64) @@ -445,21 +432,48 @@ SilentRegisterSavePlan SpeculativeJIT::silentSavePlanForGPR(VirtualRegister spil } else if (registerFormat == DataFormatStorage) { ASSERT(info.gpr() == source); fillAction = LoadPtr; + } else if (registerFormat == DataFormatInt52) { + if (node->hasConstant()) + fillAction = SetInt52Constant; + else if (info.spillFormat() == DataFormatInt52) + fillAction = Load64; + else if (info.spillFormat() == DataFormatStrictInt52) + fillAction = Load64ShiftInt52Left; + else if (info.spillFormat() == DataFormatNone) + fillAction = Load64; + else { + RELEASE_ASSERT_NOT_REACHED(); +#if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE) + fillAction = Load64; // Make GCC happy. +#endif + } + } else if (registerFormat == DataFormatStrictInt52) { + if (node->hasConstant()) + fillAction = SetStrictInt52Constant; + else if (info.spillFormat() == DataFormatInt52) + fillAction = Load64ShiftInt52Right; + else if (info.spillFormat() == DataFormatStrictInt52) + fillAction = Load64; + else if (info.spillFormat() == DataFormatNone) + fillAction = Load64; + else { + RELEASE_ASSERT_NOT_REACHED(); +#if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE) + fillAction = Load64; // Make GCC happy. +#endif + } } else { ASSERT(registerFormat & DataFormatJS); #if USE(JSVALUE64) ASSERT(info.gpr() == source); if (node->hasConstant()) { - if (valueOfJSConstant(node).isCell()) + if (node->isCellConstant()) fillAction = SetTrustedJSConstant; else fillAction = SetJSConstant; - } else if (info.spillFormat() == DataFormatInteger) { - ASSERT(registerFormat == DataFormatJSInteger); + } else if (info.spillFormat() == DataFormatInt32) { + ASSERT(registerFormat == DataFormatJSInt32); fillAction = Load32PayloadBoxInt; - } else if (info.spillFormat() == DataFormatDouble) { - ASSERT(registerFormat == DataFormatJSDouble); - fillAction = LoadDoubleBoxDouble; } else fillAction = Load64; #else @@ -470,8 +484,8 @@ SilentRegisterSavePlan SpeculativeJIT::silentSavePlanForGPR(VirtualRegister spil fillAction = Load32Payload; else { // Fill the Tag switch (info.spillFormat()) { - case DataFormatInteger: - ASSERT(registerFormat == DataFormatJSInteger); + case DataFormatInt32: + ASSERT(registerFormat == DataFormatJSInt32); fillAction = SetInt32Tag; break; case DataFormatCell: @@ -495,7 +509,7 @@ SilentRegisterSavePlan SpeculativeJIT::silentSavePlanForGPR(VirtualRegister spil SilentRegisterSavePlan SpeculativeJIT::silentSavePlanForFPR(VirtualRegister spillMe, FPRReg source) { - GenerationInfo& info = m_generationInfo[spillMe]; + GenerationInfo& info = generationInfoFromVirtualRegister(spillMe); Node* node = info.node(); ASSERT(info.registerFormat() == DataFormatDouble); @@ -513,18 +527,16 @@ SilentRegisterSavePlan SpeculativeJIT::silentSavePlanForFPR(VirtualRegister spil #if USE(JSVALUE64) if (node->hasConstant()) { - ASSERT(isNumberConstant(node)); + node->asNumber(); // To get the assertion. fillAction = SetDoubleConstant; - } else if (info.spillFormat() != DataFormatNone && info.spillFormat() != DataFormatDouble) { - // it was already spilled previously and not as a double, which means we need unboxing. - ASSERT(info.spillFormat() & DataFormatJS); - fillAction = LoadJSUnboxDouble; - } else + } else { + ASSERT(info.spillFormat() == DataFormatNone || info.spillFormat() == DataFormatDouble); fillAction = LoadDouble; + } #elif USE(JSVALUE32_64) - ASSERT(info.registerFormat() == DataFormatDouble || info.registerFormat() == DataFormatJSDouble); + ASSERT(info.registerFormat() == DataFormatDouble); if (node->hasConstant()) { - ASSERT(isNumberConstant(node)); + node->asNumber(); // To get the assertion. fillAction = SetDoubleConstant; } else fillAction = LoadDouble; @@ -569,13 +581,21 @@ void SpeculativeJIT::silentFill(const SilentRegisterSavePlan& plan, GPRReg canTr case DoNothingForFill: break; case SetInt32Constant: - m_jit.move(Imm32(valueOfInt32Constant(plan.node())), plan.gpr()); + m_jit.move(Imm32(plan.node()->asInt32()), plan.gpr()); break; +#if USE(JSVALUE64) + case SetInt52Constant: + m_jit.move(Imm64(plan.node()->asMachineInt() << JSValue::int52ShiftAmount), plan.gpr()); + break; + case SetStrictInt52Constant: + m_jit.move(Imm64(plan.node()->asMachineInt()), plan.gpr()); + break; +#endif // USE(JSVALUE64) case SetBooleanConstant: - m_jit.move(TrustedImm32(valueOfBooleanConstant(plan.node())), plan.gpr()); + m_jit.move(TrustedImm32(plan.node()->asBoolean()), plan.gpr()); break; case SetCellConstant: - m_jit.move(TrustedImmPtr(valueOfJSConstant(plan.node()).asCell()), plan.gpr()); + m_jit.move(TrustedImmPtr(plan.node()->asCell()), plan.gpr()); break; #if USE(JSVALUE64) case SetTrustedJSConstant: @@ -585,27 +605,28 @@ void SpeculativeJIT::silentFill(const SilentRegisterSavePlan& plan, GPRReg canTr m_jit.move(valueOfJSConstantAsImm64(plan.node()), plan.gpr()); break; case SetDoubleConstant: - m_jit.move(Imm64(reinterpretDoubleToInt64(valueOfNumberConstant(plan.node()))), canTrample); + m_jit.move(Imm64(reinterpretDoubleToInt64(plan.node()->asNumber())), canTrample); m_jit.move64ToDouble(canTrample, plan.fpr()); break; case Load32PayloadBoxInt: m_jit.load32(JITCompiler::payloadFor(plan.node()->virtualRegister()), plan.gpr()); m_jit.or64(GPRInfo::tagTypeNumberRegister, plan.gpr()); break; - case LoadDoubleBoxDouble: - m_jit.load64(JITCompiler::addressFor(plan.node()->virtualRegister()), plan.gpr()); - m_jit.sub64(GPRInfo::tagTypeNumberRegister, plan.gpr()); + case Load32PayloadConvertToInt52: + m_jit.load32(JITCompiler::payloadFor(plan.node()->virtualRegister()), plan.gpr()); + m_jit.signExtend32ToPtr(plan.gpr(), plan.gpr()); + m_jit.lshift64(TrustedImm32(JSValue::int52ShiftAmount), plan.gpr()); break; - case LoadJSUnboxDouble: - m_jit.load64(JITCompiler::addressFor(plan.node()->virtualRegister()), canTrample); - unboxDouble(canTrample, plan.fpr()); + case Load32PayloadSignExtend: + m_jit.load32(JITCompiler::payloadFor(plan.node()->virtualRegister()), plan.gpr()); + m_jit.signExtend32ToPtr(plan.gpr(), plan.gpr()); break; #else case SetJSConstantTag: - m_jit.move(Imm32(valueOfJSConstant(plan.node()).tag()), plan.gpr()); + m_jit.move(Imm32(plan.node()->asJSValue().tag()), plan.gpr()); break; case SetJSConstantPayload: - m_jit.move(Imm32(valueOfJSConstant(plan.node()).payload()), plan.gpr()); + m_jit.move(Imm32(plan.node()->asJSValue().payload()), plan.gpr()); break; case SetInt32Tag: m_jit.move(TrustedImm32(JSValue::Int32Tag), plan.gpr()); @@ -617,7 +638,7 @@ void SpeculativeJIT::silentFill(const SilentRegisterSavePlan& plan, GPRReg canTr m_jit.move(TrustedImm32(JSValue::BooleanTag), plan.gpr()); break; case SetDoubleConstant: - m_jit.loadDouble(addressOfDoubleConstant(plan.node()), plan.fpr()); + m_jit.loadDouble(TrustedImmPtr(m_jit.addressOfDoubleConstant(plan.node())), plan.fpr()); break; #endif case Load32Tag: @@ -633,6 +654,14 @@ void SpeculativeJIT::silentFill(const SilentRegisterSavePlan& plan, GPRReg canTr case Load64: m_jit.load64(JITCompiler::addressFor(plan.node()->virtualRegister()), plan.gpr()); break; + case Load64ShiftInt52Right: + m_jit.load64(JITCompiler::addressFor(plan.node()->virtualRegister()), plan.gpr()); + m_jit.rshift64(TrustedImm32(JSValue::int52ShiftAmount), plan.gpr()); + break; + case Load64ShiftInt52Left: + m_jit.load64(JITCompiler::addressFor(plan.node()->virtualRegister()), plan.gpr()); + m_jit.lshift64(TrustedImm32(JSValue::int52ShiftAmount), plan.gpr()); + break; #endif case LoadDouble: m_jit.loadDouble(JITCompiler::addressFor(plan.node()->virtualRegister()), plan.fpr()); @@ -642,39 +671,15 @@ void SpeculativeJIT::silentFill(const SilentRegisterSavePlan& plan, GPRReg canTr } } -const TypedArrayDescriptor* SpeculativeJIT::typedArrayDescriptor(ArrayMode arrayMode) -{ - switch (arrayMode.type()) { - case Array::Int8Array: - return &m_jit.vm()->int8ArrayDescriptor(); - case Array::Int16Array: - return &m_jit.vm()->int16ArrayDescriptor(); - case Array::Int32Array: - return &m_jit.vm()->int32ArrayDescriptor(); - case Array::Uint8Array: - return &m_jit.vm()->uint8ArrayDescriptor(); - case Array::Uint8ClampedArray: - return &m_jit.vm()->uint8ClampedArrayDescriptor(); - case Array::Uint16Array: - return &m_jit.vm()->uint16ArrayDescriptor(); - case Array::Uint32Array: - return &m_jit.vm()->uint32ArrayDescriptor(); - case Array::Float32Array: - return &m_jit.vm()->float32ArrayDescriptor(); - case Array::Float64Array: - return &m_jit.vm()->float64ArrayDescriptor(); - default: - return 0; - } -} - JITCompiler::Jump SpeculativeJIT::jumpSlowForUnwantedArrayMode(GPRReg tempGPR, ArrayMode arrayMode, IndexingType shape) { switch (arrayMode.arrayClass()) { case Array::OriginalArray: { CRASH(); +#if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE) JITCompiler::Jump result; // I already know that VC++ takes unkindly to the expression "return Jump()", so I'm doing it this way in anticipation of someone eventually using VC++ to compile the DFG. return result; +#endif } case Array::Array: @@ -682,10 +687,19 @@ JITCompiler::Jump SpeculativeJIT::jumpSlowForUnwantedArrayMode(GPRReg tempGPR, A return m_jit.branch32( MacroAssembler::NotEqual, tempGPR, TrustedImm32(IsArray | shape)); - default: + case Array::NonArray: + case Array::OriginalNonArray: + m_jit.and32(TrustedImm32(IsArray | IndexingShapeMask), tempGPR); + return m_jit.branch32( + MacroAssembler::NotEqual, tempGPR, TrustedImm32(shape)); + + case Array::PossiblyArray: m_jit.and32(TrustedImm32(IndexingShapeMask), tempGPR); return m_jit.branch32(MacroAssembler::NotEqual, tempGPR, TrustedImm32(shape)); } + + RELEASE_ASSERT_NOT_REACHED(); + return JITCompiler::Jump(); } JITCompiler::JumpList SpeculativeJIT::jumpSlowForUnwantedArrayMode(GPRReg tempGPR, ArrayMode arrayMode) @@ -702,6 +716,9 @@ JITCompiler::JumpList SpeculativeJIT::jumpSlowForUnwantedArrayMode(GPRReg tempGP case Array::Contiguous: return jumpSlowForUnwantedArrayMode(tempGPR, arrayMode, ContiguousShape); + case Array::Undecided: + return jumpSlowForUnwantedArrayMode(tempGPR, arrayMode, UndecidedShape); + case Array::ArrayStorage: case Array::SlowPutArrayStorage: { ASSERT(!arrayMode.isJSArrayWithOriginalStructure()); @@ -753,8 +770,6 @@ void SpeculativeJIT::checkArray(Node* node) SpeculateCellOperand base(this, node->child1()); GPRReg baseReg = base.gpr(); - const TypedArrayDescriptor* result = typedArrayDescriptor(node->arrayMode()); - if (node->arrayMode().alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1()))) { noResult(m_currentNode); return; @@ -763,19 +778,19 @@ void SpeculativeJIT::checkArray(Node* node) const ClassInfo* expectedClassInfo = 0; switch (node->arrayMode().type()) { + case Array::AnyTypedArray: case Array::String: - expectedClassInfo = &JSString::s_info; + RELEASE_ASSERT_NOT_REACHED(); // Should have been a Phantom(String:) break; case Array::Int32: case Array::Double: case Array::Contiguous: + case Array::Undecided: case Array::ArrayStorage: case Array::SlowPutArrayStorage: { GPRTemporary temp(this); GPRReg tempGPR = temp.gpr(); - m_jit.loadPtr( - MacroAssembler::Address(baseReg, JSCell::structureOffset()), tempGPR); - m_jit.load8(MacroAssembler::Address(tempGPR, Structure::indexingTypeOffset()), tempGPR); + m_jit.load8(MacroAssembler::Address(baseReg, JSCell::indexingTypeOffset()), tempGPR); speculationCheck( BadIndexingType, JSValueSource::unboxedCell(baseReg), 0, jumpSlowForUnwantedArrayMode(tempGPR, node->arrayMode())); @@ -783,30 +798,29 @@ void SpeculativeJIT::checkArray(Node* node) noResult(m_currentNode); return; } - case Array::Arguments: - expectedClassInfo = &Arguments::s_info; - break; - case Array::Int8Array: - case Array::Int16Array: - case Array::Int32Array: - case Array::Uint8Array: - case Array::Uint8ClampedArray: - case Array::Uint16Array: - case Array::Uint32Array: - case Array::Float32Array: - case Array::Float64Array: - expectedClassInfo = result->m_classInfo; - break; + case Array::DirectArguments: + speculateCellTypeWithoutTypeFiltering(node->child1(), baseReg, DirectArgumentsType); + noResult(m_currentNode); + return; + case Array::ScopedArguments: + speculateCellTypeWithoutTypeFiltering(node->child1(), baseReg, ScopedArgumentsType); + noResult(m_currentNode); + return; default: - RELEASE_ASSERT_NOT_REACHED(); - break; + speculateCellTypeWithoutTypeFiltering( + node->child1(), baseReg, + typeForTypedArrayType(node->arrayMode().typedArrayType())); + noResult(m_currentNode); + return; } + RELEASE_ASSERT(expectedClassInfo); + GPRTemporary temp(this); - m_jit.loadPtr( - MacroAssembler::Address(baseReg, JSCell::structureOffset()), temp.gpr()); + GPRTemporary temp2(this); + m_jit.emitLoadStructure(baseReg, temp.gpr(), temp2.gpr()); speculationCheck( - Uncountable, JSValueRegs(), 0, + BadType, JSValueSource::unboxedCell(baseReg), node, m_jit.branchPtr( MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), Structure::classInfoOffset()), @@ -834,22 +848,19 @@ void SpeculativeJIT::arrayify(Node* node, GPRReg baseReg, GPRReg propertyReg) MacroAssembler::JumpList slowPath; if (node->op() == ArrayifyToStructure) { - slowPath.append(m_jit.branchWeakPtr( + slowPath.append(m_jit.branchWeakStructure( JITCompiler::NotEqual, - JITCompiler::Address(baseReg, JSCell::structureOffset()), + JITCompiler::Address(baseReg, JSCell::structureIDOffset()), node->structure())); } else { - m_jit.loadPtr( - MacroAssembler::Address(baseReg, JSCell::structureOffset()), structureGPR); - m_jit.load8( - MacroAssembler::Address(structureGPR, Structure::indexingTypeOffset()), tempGPR); + MacroAssembler::Address(baseReg, JSCell::indexingTypeOffset()), tempGPR); slowPath.append(jumpSlowForUnwantedArrayMode(tempGPR, node->arrayMode())); } - addSlowPathGenerator(adoptPtr(new ArrayifySlowPathGenerator( - slowPath, this, node, baseReg, propertyReg, tempGPR, structureGPR))); + addSlowPathGenerator(std::make_unique<ArrayifySlowPathGenerator>( + slowPath, this, node, baseReg, propertyReg, tempGPR, structureGPR)); noResult(m_currentNode); } @@ -865,7 +876,7 @@ void SpeculativeJIT::arrayify(Node* node) return; } - SpeculateIntegerOperand property(this, node->child2()); + SpeculateInt32Operand property(this, node->child2()); arrayify(node, base.gpr(), property.gpr()); } @@ -873,7 +884,7 @@ void SpeculativeJIT::arrayify(Node* node) GPRReg SpeculativeJIT::fillStorage(Edge edge) { VirtualRegister virtualRegister = edge->virtualRegister(); - GenerationInfo& info = m_generationInfo[virtualRegister]; + GenerationInfo& info = generationInfoFromVirtualRegister(virtualRegister); switch (info.registerFormat()) { case DataFormatNone: { @@ -929,74 +940,72 @@ void SpeculativeJIT::useChildren(Node* node) } } -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 -} - -void SpeculativeJIT::writeBarrier(GPRReg ownerGPR, GPRReg valueGPR, Edge valueUse, WriteBarrierUseKind useKind, GPRReg scratch1, GPRReg scratch2) -{ - UNUSED_PARAM(ownerGPR); - UNUSED_PARAM(valueGPR); - UNUSED_PARAM(scratch1); - UNUSED_PARAM(scratch2); - UNUSED_PARAM(useKind); - - if (isKnownNotCell(valueUse.node())) - return; - -#if ENABLE(WRITE_BARRIER_PROFILING) - JITCompiler::emitCount(m_jit, WriteBarrierCounters::jitCounterFor(useKind)); -#endif -} - -void SpeculativeJIT::writeBarrier(GPRReg ownerGPR, JSCell* value, WriteBarrierUseKind useKind, GPRReg scratch1, GPRReg scratch2) +void SpeculativeJIT::compileIn(Node* node) { - UNUSED_PARAM(ownerGPR); - UNUSED_PARAM(value); - UNUSED_PARAM(scratch1); - UNUSED_PARAM(scratch2); - UNUSED_PARAM(useKind); + SpeculateCellOperand base(this, node->child2()); + GPRReg baseGPR = base.gpr(); - if (Heap::isMarked(value)) - return; + if (JSString* string = node->child1()->dynamicCastConstant<JSString*>()) { + if (string->tryGetValueImpl() && string->tryGetValueImpl()->isAtomic()) { + StructureStubInfo* stubInfo = m_jit.codeBlock()->addStubInfo(AccessType::In); + + GPRTemporary result(this); + GPRReg resultGPR = result.gpr(); -#if ENABLE(WRITE_BARRIER_PROFILING) - JITCompiler::emitCount(m_jit, WriteBarrierCounters::jitCounterFor(useKind)); + use(node->child1()); + + MacroAssembler::PatchableJump jump = m_jit.patchableJump(); + MacroAssembler::Label done = m_jit.label(); + + // Since this block is executed only when the result of string->tryGetValueImpl() is atomic, + // we can cast it to const AtomicStringImpl* safely. + auto slowPath = slowPathCall( + jump.m_jump, this, operationInOptimize, + JSValueRegs::payloadOnly(resultGPR), stubInfo, baseGPR, + static_cast<const AtomicStringImpl*>(string->tryGetValueImpl())); + + stubInfo->callSiteIndex = m_jit.addCallSite(node->origin.semantic); + stubInfo->codeOrigin = node->origin.semantic; + stubInfo->patch.baseGPR = static_cast<int8_t>(baseGPR); + stubInfo->patch.valueGPR = static_cast<int8_t>(resultGPR); +#if USE(JSVALUE32_64) + stubInfo->patch.valueTagGPR = static_cast<int8_t>(InvalidGPRReg); + stubInfo->patch.baseTagGPR = static_cast<int8_t>(InvalidGPRReg); #endif -} + stubInfo->patch.usedRegisters = usedRegisters(); -void SpeculativeJIT::writeBarrier(JSCell* owner, GPRReg valueGPR, Edge valueUse, WriteBarrierUseKind useKind, GPRReg scratch) -{ - UNUSED_PARAM(owner); - UNUSED_PARAM(valueGPR); - UNUSED_PARAM(scratch); - UNUSED_PARAM(useKind); + m_jit.addIn(InRecord(jump, done, slowPath.get(), stubInfo)); + addSlowPathGenerator(WTFMove(slowPath)); - if (isKnownNotCell(valueUse.node())) - return; + base.use(); -#if ENABLE(WRITE_BARRIER_PROFILING) - JITCompiler::emitCount(m_jit, WriteBarrierCounters::jitCounterFor(useKind)); -#endif + blessedBooleanResult(resultGPR, node, UseChildrenCalledExplicitly); + return; + } + } + + JSValueOperand key(this, node->child1()); + JSValueRegs regs = key.jsValueRegs(); + + GPRFlushedCallResult result(this); + GPRReg resultGPR = result.gpr(); + + base.use(); + key.use(); + + flushRegisters(); + callOperation( + operationGenericIn, extractResult(JSValueRegs::payloadOnly(resultGPR)), + baseGPR, regs); + m_jit.exceptionCheck(); + blessedBooleanResult(resultGPR, node, UseChildrenCalledExplicitly); } -bool SpeculativeJIT::nonSpeculativeCompare(Node* node, MacroAssembler::RelationalCondition cond, S_DFGOperation_EJJ helperFunction) +bool SpeculativeJIT::nonSpeculativeCompare(Node* node, MacroAssembler::RelationalCondition cond, S_JITOperation_EJJ helperFunction) { unsigned branchIndexInBlock = detectPeepHoleBranch(); if (branchIndexInBlock != UINT_MAX) { - Node* branchNode = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); + Node* branchNode = m_block->at(branchIndexInBlock); ASSERT(node->adjustedRefCount() == 1); @@ -1017,7 +1026,7 @@ bool SpeculativeJIT::nonSpeculativeStrictEq(Node* node, bool invert) { unsigned branchIndexInBlock = detectPeepHoleBranch(); if (branchIndexInBlock != UINT_MAX) { - Node* branchNode = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); + Node* branchNode = m_block->at(branchIndexInBlock); ASSERT(node->adjustedRefCount() == 1); @@ -1034,7 +1043,6 @@ bool SpeculativeJIT::nonSpeculativeStrictEq(Node* node, bool invert) return false; } -#ifndef NDEBUG static const char* dataFormatString(DataFormat format) { // These values correspond to the DataFormat enum. @@ -1090,116 +1098,6 @@ void SpeculativeJIT::dump(const char* label) if (label) dataLogF("</%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()) { - dataLogF("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()) { - dataLogF("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) { - dataLogF("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) { - dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for virtual register %d (fpr %s).\n", virtualRegister, FPRInfo::debugName(fpr)); - failed = true; - } - break; - } - case DataFormatOSRMarker: - case DataFormatDead: - case DataFormatArguments: - RELEASE_ASSERT_NOT_REACHED(); - 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()) { - dataLogF("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()) { - dataLogF("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()) { - dataLogF("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()) { - dataLogF("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) @@ -1221,110 +1119,40 @@ GPRTemporary::GPRTemporary(SpeculativeJIT* jit, GPRReg specific) m_gpr = m_jit->allocate(specific); } -GPRTemporary::GPRTemporary(SpeculativeJIT* jit, SpeculateIntegerOperand& op1) - : m_jit(jit) - , m_gpr(InvalidGPRReg) -{ - if (m_jit->canReuse(op1.node())) - m_gpr = m_jit->reuse(op1.gpr()); - else - m_gpr = m_jit->allocate(); -} - -GPRTemporary::GPRTemporary(SpeculativeJIT* jit, SpeculateIntegerOperand& op1, SpeculateIntegerOperand& op2) +#if USE(JSVALUE32_64) +GPRTemporary::GPRTemporary( + SpeculativeJIT* jit, ReuseTag, JSValueOperand& op1, WhichValueWord which) : m_jit(jit) , m_gpr(InvalidGPRReg) { - if (m_jit->canReuse(op1.node())) - m_gpr = m_jit->reuse(op1.gpr()); - else if (m_jit->canReuse(op2.node())) - m_gpr = m_jit->reuse(op2.gpr()); + if (!op1.isDouble() && m_jit->canReuse(op1.node())) + m_gpr = m_jit->reuse(op1.gpr(which)); else m_gpr = m_jit->allocate(); } +#endif // USE(JSVALUE32_64) -GPRTemporary::GPRTemporary(SpeculativeJIT* jit, SpeculateStrictInt32Operand& op1) - : m_jit(jit) - , m_gpr(InvalidGPRReg) -{ - if (m_jit->canReuse(op1.node())) - m_gpr = m_jit->reuse(op1.gpr()); - else - m_gpr = m_jit->allocate(); -} +JSValueRegsTemporary::JSValueRegsTemporary() { } -GPRTemporary::GPRTemporary(SpeculativeJIT* jit, IntegerOperand& op1) - : m_jit(jit) - , m_gpr(InvalidGPRReg) +JSValueRegsTemporary::JSValueRegsTemporary(SpeculativeJIT* jit) +#if USE(JSVALUE64) + : m_gpr(jit) +#else + : m_payloadGPR(jit) + , m_tagGPR(jit) +#endif { - if (m_jit->canReuse(op1.node())) - 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.node())) - m_gpr = m_jit->reuse(op1.gpr()); - else if (m_jit->canReuse(op2.node())) - m_gpr = m_jit->reuse(op2.gpr()); - else - m_gpr = m_jit->allocate(); -} +JSValueRegsTemporary::~JSValueRegsTemporary() { } -GPRTemporary::GPRTemporary(SpeculativeJIT* jit, SpeculateCellOperand& op1) - : m_jit(jit) - , m_gpr(InvalidGPRReg) +JSValueRegs JSValueRegsTemporary::regs() { - if (m_jit->canReuse(op1.node())) - 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.node())) - 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.node())) - m_gpr = m_jit->reuse(op1.gpr()); - else - m_gpr = m_jit->allocate(); -} + return JSValueRegs(m_gpr.gpr()); #else -GPRTemporary::GPRTemporary(SpeculativeJIT* jit, JSValueOperand& op1, bool tag) - : m_jit(jit) - , m_gpr(InvalidGPRReg) -{ - if (!op1.isDouble() && m_jit->canReuse(op1.node())) - m_gpr = m_jit->reuse(tag ? op1.tagGPR() : op1.payloadGPR()); - else - m_gpr = m_jit->allocate(); -} + return JSValueRegs(m_tagGPR.gpr(), m_payloadGPR.gpr()); #endif - -GPRTemporary::GPRTemporary(SpeculativeJIT* jit, StorageOperand& op1) - : m_jit(jit) - , m_gpr(InvalidGPRReg) -{ - if (m_jit->canReuse(op1.node())) - m_gpr = m_jit->reuse(op1.gpr()); - else - m_gpr = m_jit->allocate(); } void GPRTemporary::adopt(GPRTemporary& other) @@ -1364,6 +1192,8 @@ FPRTemporary::FPRTemporary(SpeculativeJIT* jit, SpeculateDoubleOperand& op1, Spe m_fpr = m_jit->reuse(op1.fpr()); else if (m_jit->canReuse(op2.node())) m_fpr = m_jit->reuse(op2.fpr()); + else if (m_jit->canReuse(op1.node(), op2.node()) && op1.fpr() == op2.fpr()) + m_fpr = m_jit->reuse(op1.fpr()); else m_fpr = m_jit->fprAllocate(); } @@ -1382,8 +1212,8 @@ FPRTemporary::FPRTemporary(SpeculativeJIT* jit, JSValueOperand& op1) void SpeculativeJIT::compilePeepHoleDoubleBranch(Node* node, Node* branchNode, JITCompiler::DoubleCondition condition) { - BlockIndex taken = branchNode->takenBlockIndex(); - BlockIndex notTaken = branchNode->notTakenBlockIndex(); + BasicBlock* taken = branchNode->branchData()->taken.block; + BasicBlock* notTaken = branchNode->branchData()->notTaken.block; SpeculateDoubleOperand op1(this, node->child1()); SpeculateDoubleOperand op2(this, node->child2()); @@ -1394,14 +1224,14 @@ void SpeculativeJIT::compilePeepHoleDoubleBranch(Node* node, Node* branchNode, J void SpeculativeJIT::compilePeepHoleObjectEquality(Node* node, Node* branchNode) { - BlockIndex taken = branchNode->takenBlockIndex(); - BlockIndex notTaken = branchNode->notTakenBlockIndex(); + BasicBlock* taken = branchNode->branchData()->taken.block; + BasicBlock* notTaken = branchNode->branchData()->notTaken.block; MacroAssembler::RelationalCondition condition = MacroAssembler::Equal; if (taken == nextBlock()) { condition = MacroAssembler::NotEqual; - BlockIndex tmp = taken; + BasicBlock* tmp = taken; taken = notTaken; notTaken = tmp; } @@ -1412,56 +1242,36 @@ void SpeculativeJIT::compilePeepHoleObjectEquality(Node* node, Node* branchNode) GPRReg op1GPR = op1.gpr(); GPRReg op2GPR = op2.gpr(); - if (m_jit.graph().globalObjectFor(node->codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid()) { - m_jit.graph().globalObjectFor(node->codeOrigin)->masqueradesAsUndefinedWatchpoint()->add(speculationWatchpoint()); + if (masqueradesAsUndefinedWatchpointIsStillValid()) { if (m_state.forNode(node->child1()).m_type & ~SpecObject) { speculationCheck( - BadType, JSValueSource::unboxedCell(op1GPR), node->child1(), - m_jit.branchPtr( - MacroAssembler::Equal, - MacroAssembler::Address(op1GPR, JSCell::structureOffset()), - MacroAssembler::TrustedImmPtr(m_jit.vm()->stringStructure.get()))); + BadType, JSValueSource::unboxedCell(op1GPR), node->child1(), m_jit.branchIfNotObject(op1GPR)); } if (m_state.forNode(node->child2()).m_type & ~SpecObject) { speculationCheck( - BadType, JSValueSource::unboxedCell(op2GPR), node->child2(), - m_jit.branchPtr( - MacroAssembler::Equal, - MacroAssembler::Address(op2GPR, JSCell::structureOffset()), - MacroAssembler::TrustedImmPtr(m_jit.vm()->stringStructure.get()))); + BadType, JSValueSource::unboxedCell(op2GPR), node->child2(), m_jit.branchIfNotObject(op2GPR)); } } else { - GPRTemporary structure(this); - GPRReg structureGPR = structure.gpr(); - - m_jit.loadPtr(MacroAssembler::Address(op1GPR, JSCell::structureOffset()), structureGPR); if (m_state.forNode(node->child1()).m_type & ~SpecObject) { speculationCheck( BadType, JSValueSource::unboxedCell(op1GPR), node->child1(), - m_jit.branchPtr( - MacroAssembler::Equal, - structureGPR, - MacroAssembler::TrustedImmPtr(m_jit.vm()->stringStructure.get()))); + m_jit.branchIfNotObject(op1GPR)); } speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), node->child1(), m_jit.branchTest8( MacroAssembler::NonZero, - MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::Address(op1GPR, JSCell::typeInfoFlagsOffset()), MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); - m_jit.loadPtr(MacroAssembler::Address(op2GPR, JSCell::structureOffset()), structureGPR); if (m_state.forNode(node->child2()).m_type & ~SpecObject) { speculationCheck( BadType, JSValueSource::unboxedCell(op2GPR), node->child2(), - m_jit.branchPtr( - MacroAssembler::Equal, - structureGPR, - MacroAssembler::TrustedImmPtr(m_jit.vm()->stringStructure.get()))); + m_jit.branchIfNotObject(op2GPR)); } speculationCheck(BadType, JSValueSource::unboxedCell(op2GPR), node->child2(), m_jit.branchTest8( MacroAssembler::NonZero, - MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::Address(op2GPR, JSCell::typeInfoFlagsOffset()), MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); } @@ -1471,26 +1281,26 @@ void SpeculativeJIT::compilePeepHoleObjectEquality(Node* node, Node* branchNode) void SpeculativeJIT::compilePeepHoleBooleanBranch(Node* node, Node* branchNode, JITCompiler::RelationalCondition condition) { - BlockIndex taken = branchNode->takenBlockIndex(); - BlockIndex notTaken = branchNode->notTakenBlockIndex(); + BasicBlock* taken = branchNode->branchData()->taken.block; + BasicBlock* notTaken = branchNode->branchData()->notTaken.block; // 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 == nextBlock()) { condition = JITCompiler::invert(condition); - BlockIndex tmp = taken; + BasicBlock* tmp = taken; taken = notTaken; notTaken = tmp; } - if (isBooleanConstant(node->child1().node())) { - bool imm = valueOfBooleanConstant(node->child1().node()); + if (node->child1()->isInt32Constant()) { + int32_t imm = node->child1()->asInt32(); SpeculateBooleanOperand op2(this, node->child2()); - branch32(condition, JITCompiler::Imm32(static_cast<int32_t>(JSValue::encode(jsBoolean(imm)))), op2.gpr(), taken); - } else if (isBooleanConstant(node->child2().node())) { + branch32(condition, JITCompiler::Imm32(imm), op2.gpr(), taken); + } else if (node->child2()->isInt32Constant()) { SpeculateBooleanOperand op1(this, node->child1()); - bool imm = valueOfBooleanConstant(node->child2().node()); - branch32(condition, op1.gpr(), JITCompiler::Imm32(static_cast<int32_t>(JSValue::encode(jsBoolean(imm)))), taken); + int32_t imm = node->child2()->asInt32(); + branch32(condition, op1.gpr(), JITCompiler::Imm32(imm), taken); } else { SpeculateBooleanOperand op1(this, node->child1()); SpeculateBooleanOperand op2(this, node->child2()); @@ -1500,31 +1310,31 @@ void SpeculativeJIT::compilePeepHoleBooleanBranch(Node* node, Node* branchNode, jump(notTaken); } -void SpeculativeJIT::compilePeepHoleIntegerBranch(Node* node, Node* branchNode, JITCompiler::RelationalCondition condition) +void SpeculativeJIT::compilePeepHoleInt32Branch(Node* node, Node* branchNode, JITCompiler::RelationalCondition condition) { - BlockIndex taken = branchNode->takenBlockIndex(); - BlockIndex notTaken = branchNode->notTakenBlockIndex(); + BasicBlock* taken = branchNode->branchData()->taken.block; + BasicBlock* notTaken = branchNode->branchData()->notTaken.block; // 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 == nextBlock()) { condition = JITCompiler::invert(condition); - BlockIndex tmp = taken; + BasicBlock* tmp = taken; taken = notTaken; notTaken = tmp; } - if (isInt32Constant(node->child1().node())) { - int32_t imm = valueOfInt32Constant(node->child1().node()); - SpeculateIntegerOperand op2(this, node->child2()); + if (node->child1()->isInt32Constant()) { + int32_t imm = node->child1()->asInt32(); + SpeculateInt32Operand op2(this, node->child2()); branch32(condition, JITCompiler::Imm32(imm), op2.gpr(), taken); - } else if (isInt32Constant(node->child2().node())) { - SpeculateIntegerOperand op1(this, node->child1()); - int32_t imm = valueOfInt32Constant(node->child2().node()); + } else if (node->child2()->isInt32Constant()) { + SpeculateInt32Operand op1(this, node->child1()); + int32_t imm = node->child2()->asInt32(); branch32(condition, op1.gpr(), JITCompiler::Imm32(imm), taken); } else { - SpeculateIntegerOperand op1(this, node->child1()); - SpeculateIntegerOperand op2(this, node->child2()); + SpeculateInt32Operand op1(this, node->child1()); + SpeculateInt32Operand op2(this, node->child2()); branch32(condition, op1.gpr(), op2.gpr(), taken); } @@ -1532,34 +1342,44 @@ void SpeculativeJIT::compilePeepHoleIntegerBranch(Node* node, Node* branchNode, } // 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) +bool SpeculativeJIT::compilePeepHoleBranch(Node* node, MacroAssembler::RelationalCondition condition, MacroAssembler::DoubleCondition doubleCondition, S_JITOperation_EJJ operation) { // Fused compare & branch. unsigned branchIndexInBlock = detectPeepHoleBranch(); if (branchIndexInBlock != UINT_MAX) { - Node* branchNode = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); + Node* branchNode = m_block->at(branchIndexInBlock); // 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->isBinaryUseKind(Int32Use)) - compilePeepHoleIntegerBranch(node, branchNode, condition); - else if (node->isBinaryUseKind(NumberUse)) + compilePeepHoleInt32Branch(node, branchNode, condition); +#if USE(JSVALUE64) + else if (node->isBinaryUseKind(Int52RepUse)) + compilePeepHoleInt52Branch(node, branchNode, condition); +#endif // USE(JSVALUE64) + else if (node->isBinaryUseKind(DoubleRepUse)) compilePeepHoleDoubleBranch(node, branchNode, doubleCondition); else if (node->op() == CompareEq) { - if (node->isBinaryUseKind(StringUse)) { + if (node->isBinaryUseKind(StringUse) || node->isBinaryUseKind(StringIdentUse)) { // Use non-peephole comparison, for now. return false; } if (node->isBinaryUseKind(BooleanUse)) compilePeepHoleBooleanBranch(node, branchNode, condition); + else if (node->isBinaryUseKind(SymbolUse)) + compilePeepHoleSymbolEquality(node, branchNode); else if (node->isBinaryUseKind(ObjectUse)) compilePeepHoleObjectEquality(node, branchNode); - else if (node->child1().useKind() == ObjectUse && node->child2().useKind() == ObjectOrOtherUse) + else if (node->isBinaryUseKind(ObjectUse, ObjectOrOtherUse)) compilePeepHoleObjectToObjectOrOtherEquality(node->child1(), node->child2(), branchNode); - else if (node->child1().useKind() == ObjectOrOtherUse && node->child2().useKind() == ObjectUse) + else if (node->isBinaryUseKind(ObjectOrOtherUse, ObjectUse)) compilePeepHoleObjectToObjectOrOtherEquality(node->child2(), node->child1(), branchNode); + else if (!needsTypeCheck(node->child1(), SpecOther)) + nonSpeculativePeepholeBranchNullOrUndefined(node->child2(), branchNode); + else if (!needsTypeCheck(node->child2(), SpecOther)) + nonSpeculativePeepholeBranchNullOrUndefined(node->child1(), branchNode); else { nonSpeculativePeepholeBranch(node, branchNode, condition, operation); return true; @@ -1584,7 +1404,7 @@ void SpeculativeJIT::noticeOSRBirth(Node* node) return; VirtualRegister virtualRegister = node->virtualRegister(); - GenerationInfo& info = m_generationInfo[virtualRegister]; + GenerationInfo& info = generationInfoFromVirtualRegister(virtualRegister); info.noticeOSRBirth(*m_stream, node, virtualRegister); } @@ -1593,256 +1413,126 @@ void SpeculativeJIT::compileMovHint(Node* node) { ASSERT(node->containsMovHint() && node->op() != ZombieHint); - m_lastSetOperand = node->local(); - Node* child = node->child1().node(); noticeOSRBirth(child); - if (child->op() == UInt32ToNumber) - noticeOSRBirth(child->child1().node()); - - m_stream->appendAndLog(VariableEvent::movHint(MinifiedID(child), node->local())); -} - -void SpeculativeJIT::compileMovHintAndCheck(Node* node) -{ - compileMovHint(node); - speculate(node, node->child1()); - noResult(node); + m_stream->appendAndLog(VariableEvent::movHint(MinifiedID(child), node->unlinkedLocal())); } -void SpeculativeJIT::compileInlineStart(Node* node) +void SpeculativeJIT::bail(AbortReason reason) { - InlineCallFrame* inlineCallFrame = node->codeOrigin.inlineCallFrame; - int argumentCountIncludingThis = inlineCallFrame->arguments.size(); - unsigned argumentPositionStart = node->argumentPositionStart(); - CodeBlock* codeBlock = baselineCodeBlockForInlineCallFrame(inlineCallFrame); - for (int i = 0; i < argumentCountIncludingThis; ++i) { - ValueRecovery recovery; - if (codeBlock->isCaptured(argumentToOperand(i))) - recovery = ValueRecovery::alreadyInJSStack(); - else { - ArgumentPosition& argumentPosition = - m_jit.graph().m_argumentPositions[argumentPositionStart + i]; - ValueSource valueSource; - if (!argumentPosition.shouldUnboxIfPossible()) - valueSource = ValueSource(ValueInJSStack); - else if (argumentPosition.shouldUseDoubleFormat()) - valueSource = ValueSource(DoubleInJSStack); - else if (isInt32Speculation(argumentPosition.prediction())) - valueSource = ValueSource(Int32InJSStack); - else if (isCellSpeculation(argumentPosition.prediction())) - valueSource = ValueSource(CellInJSStack); - else if (isBooleanSpeculation(argumentPosition.prediction())) - valueSource = ValueSource(BooleanInJSStack); - else - valueSource = ValueSource(ValueInJSStack); - recovery = computeValueRecoveryFor(valueSource); - } - // The recovery should refer either to something that has already been - // stored into the stack 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) - dataLogF("\nRecovery for argument %d: ", i); - recovery.dump(WTF::dataFile()); -#endif - inlineCallFrame->arguments[i] = recovery; - } + if (verboseCompilationEnabled()) + dataLog("Bailing compilation.\n"); + m_compileOkay = true; + m_jit.abortWithReason(reason, m_lastGeneratedNode); + clearGenerationInfo(); } -void SpeculativeJIT::compile(BasicBlock& block) +void SpeculativeJIT::compileCurrentBlock() { ASSERT(m_compileOkay); - if (!block.isReachable) + if (!m_block) return; - if (!block.cfaHasVisited) { + ASSERT(m_block->isReachable); + + m_jit.blockHeads()[m_block->index] = m_jit.label(); + + if (!m_block->intersectionOfCFAHasVisited) { // Don't generate code for basic blocks that are unreachable according to CFA. // But to be sure that nobody has generated a jump to this block, drop in a // breakpoint here. -#if !ASSERT_DISABLED - m_jit.breakpoint(); -#endif + m_jit.abortWithReason(DFGUnreachableBasicBlock); return; } - m_blockHeads[m_block] = m_jit.label(); -#if DFG_ENABLE(JIT_BREAK_ON_EVERY_BLOCK) - m_jit.breakpoint(); -#endif - -#if DFG_ENABLE(DEBUG_VERBOSE) - dataLogF("Setting up state for block #%u: ", m_block); -#endif - m_stream->appendAndLog(VariableEvent::reset()); m_jit.jitAssertHasValidCallFrame(); + m_jit.jitAssertTagsInPlace(); + m_jit.jitAssertArgumentCountSane(); - ASSERT(m_arguments.size() == block.variablesAtHead.numberOfArguments()); - for (size_t i = 0; i < m_arguments.size(); ++i) { - ValueSource valueSource = ValueSource(ValueInJSStack); - m_arguments[i] = valueSource; - m_stream->appendAndLog(VariableEvent::setLocal(argumentToOperand(i), valueSource.dataFormat())); - } - m_state.reset(); - m_state.beginBasicBlock(&block); + m_state.beginBasicBlock(m_block); - ASSERT(m_variables.size() == block.variablesAtHead.numberOfLocals()); - for (size_t i = 0; i < m_variables.size(); ++i) { - Node* node = block.variablesAtHead.local(i); - ValueSource valueSource; + for (size_t i = m_block->variablesAtHead.size(); i--;) { + int operand = m_block->variablesAtHead.operandForIndex(i); + Node* node = m_block->variablesAtHead[i]; if (!node) - valueSource = ValueSource(SourceIsDead); - else if (node->variableAccessData()->isArgumentsAlias()) - valueSource = ValueSource(ArgumentsSource); - else if (!node->refCount()) - valueSource = ValueSource(SourceIsDead); - else if (!node->variableAccessData()->shouldUnboxIfPossible()) - valueSource = ValueSource(ValueInJSStack); - else if (node->variableAccessData()->shouldUseDoubleFormat()) - valueSource = ValueSource(DoubleInJSStack); - else - valueSource = ValueSource::forSpeculation(node->variableAccessData()->argumentAwarePrediction()); - m_variables[i] = valueSource; - // FIXME: Don't emit SetLocal(Dead). https://bugs.webkit.org/show_bug.cgi?id=108019 - m_stream->appendAndLog(VariableEvent::setLocal(i, valueSource.dataFormat())); - } - - m_lastSetOperand = std::numeric_limits<int>::max(); - m_codeOriginForOSR = CodeOrigin(); - - if (DFG_ENABLE_EDGE_CODE_VERIFICATION) { - JITCompiler::Jump verificationSucceeded = - m_jit.branch32(JITCompiler::Equal, GPRInfo::regT0, TrustedImm32(m_block)); - m_jit.breakpoint(); - verificationSucceeded.link(&m_jit); - } - -#if DFG_ENABLE(DEBUG_VERBOSE) - dataLogF("\n"); -#endif + continue; // No need to record dead SetLocal's. + + VariableAccessData* variable = node->variableAccessData(); + DataFormat format; + if (!node->refCount()) + continue; // No need to record dead SetLocal's. + format = dataFormatFor(variable->flushFormat()); + m_stream->appendAndLog( + VariableEvent::setLocal( + VirtualRegister(operand), + variable->machineLocal(), + format)); + } + + m_origin = NodeOrigin(); + + for (m_indexInBlock = 0; m_indexInBlock < m_block->size(); ++m_indexInBlock) { + m_currentNode = m_block->at(m_indexInBlock); + + // We may have hit a contradiction that the CFA was aware of but that the JIT + // didn't cause directly. + if (!m_state.isValid()) { + bail(DFGBailedAtTopOfBlock); + return; + } - for (m_indexInBlock = 0; m_indexInBlock < block.size(); ++m_indexInBlock) { - m_currentNode = block[m_indexInBlock]; -#if !ASSERT_DISABLED - m_canExit = m_currentNode->canExit(); -#endif - bool shouldExecuteEffects = m_state.startExecuting(m_currentNode); + m_interpreter.startExecuting(); m_jit.setForNode(m_currentNode); - m_codeOriginForOSR = m_currentNode->codeOrigin; - if (!m_currentNode->shouldGenerate()) { -#if DFG_ENABLE(DEBUG_VERBOSE) - dataLogF("SpeculativeJIT skipping Node @%d (bc#%u) at JIT offset 0x%x ", m_currentNode->index(), m_currentNode->codeOrigin.bytecodeIndex, m_jit.debugOffset()); -#endif - switch (m_currentNode->op()) { - case JSConstant: - m_minifiedGraph->append(MinifiedNode::fromNode(m_currentNode)); - break; - - case WeakJSConstant: - m_jit.addWeakReference(m_currentNode->weakConstant()); - m_minifiedGraph->append(MinifiedNode::fromNode(m_currentNode)); - break; - - case SetLocal: - RELEASE_ASSERT_NOT_REACHED(); - break; - - case MovHint: - compileMovHint(m_currentNode); - break; - - case ZombieHint: { - m_lastSetOperand = m_currentNode->local(); - m_stream->appendAndLog(VariableEvent::setLocal(m_currentNode->local(), DataFormatDead)); - break; - } - - default: - if (belongsInMinifiedGraph(m_currentNode->op())) - m_minifiedGraph->append(MinifiedNode::fromNode(m_currentNode)); - break; - } - } else { - - if (verboseCompilationEnabled()) { - dataLogF( - "SpeculativeJIT generating Node @%d (bc#%u) at JIT offset 0x%x", - (int)m_currentNode->index(), - m_currentNode->codeOrigin.bytecodeIndex, m_jit.debugOffset()); -#if DFG_ENABLE(DEBUG_VERBOSE) - dataLog(" "); -#else - dataLog("\n"); -#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_currentNode->index()), GPRInfo::regT0); - m_jit.xorPtr(JITCompiler::TrustedImm32(m_currentNode->index()), GPRInfo::regT0); -#endif - checkConsistency(); - - m_speculationDirection = (m_currentNode->flags() & NodeExitsForward) ? ForwardSpeculation : BackwardSpeculation; - - compile(m_currentNode); - if (!m_compileOkay) { - m_compileOkay = true; - clearGenerationInfo(); - return; - } - - if (belongsInMinifiedGraph(m_currentNode->op())) { - m_minifiedGraph->append(MinifiedNode::fromNode(m_currentNode)); - noticeOSRBirth(m_currentNode); - } - -#if DFG_ENABLE(DEBUG_VERBOSE) - if (m_currentNode->hasResult()) { - GenerationInfo& info = m_generationInfo[m_currentNode->virtualRegister()]; - dataLogF("-> %s, vr#%d", dataFormatToString(info.registerFormat()), (int)m_currentNode->virtualRegister()); - if (info.registerFormat() != DataFormatNone) { - if (info.registerFormat() == DataFormatDouble) - dataLogF(", %s", FPRInfo::debugName(info.fpr())); -#if USE(JSVALUE32_64) - else if (info.registerFormat() & DataFormatJS) - dataLogF(", %s %s", GPRInfo::debugName(info.tagGPR()), GPRInfo::debugName(info.payloadGPR())); -#endif - else - dataLogF(", %s", GPRInfo::debugName(info.gpr())); - } - dataLogF(" "); - } else - dataLogF(" "); -#endif + m_origin = m_currentNode->origin; + if (validationEnabled()) + m_origin.exitOK &= mayExit(m_jit.graph(), m_currentNode) == Exits; + m_lastGeneratedNode = m_currentNode->op(); + + ASSERT(m_currentNode->shouldGenerate()); + + if (verboseCompilationEnabled()) { + dataLogF( + "SpeculativeJIT generating Node @%d (bc#%u) at JIT offset 0x%x", + (int)m_currentNode->index(), + m_currentNode->origin.semantic.bytecodeIndex, m_jit.debugOffset()); + dataLog("\n"); } + + if (Options::validateDFGExceptionHandling() && mayExit(m_jit.graph(), m_currentNode) != DoesNotExit) + m_jit.jitReleaseAssertNoException(); + + m_jit.pcToCodeOriginMapBuilder().appendItem(m_jit.label(), m_origin.semantic); + + compile(m_currentNode); + + if (belongsInMinifiedGraph(m_currentNode->op())) + m_minifiedGraph->append(MinifiedNode::fromNode(m_currentNode)); -#if DFG_ENABLE(DEBUG_VERBOSE) - dataLogF("\n"); +#if ENABLE(DFG_REGISTER_ALLOCATION_VALIDATION) + m_jit.clearRegisterAllocationOffsets(); #endif - // Make sure that the abstract state is rematerialized for the next node. - if (shouldExecuteEffects) - m_state.executeEffects(m_indexInBlock); + if (!m_compileOkay) { + bail(DFGBailedAtEndOfNode); + return; + } - if (m_currentNode->shouldGenerate()) - checkConsistency(); + // Make sure that the abstract state is rematerialized for the next node. + m_interpreter.executeEffects(m_indexInBlock); } // 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()); + if (!ASSERT_DISABLED) { + for (unsigned index = 0; index < m_generationInfo.size(); ++index) { + GenerationInfo& info = m_generationInfo[index]; + RELEASE_ASSERT(!info.alive()); + } } -#endif } // If we are making type predictions about our arguments then @@ -1850,67 +1540,82 @@ void SpeculativeJIT::compile(BasicBlock& block) void SpeculativeJIT::checkArgumentTypes() { ASSERT(!m_currentNode); - m_isCheckingArgumentTypes = true; - m_speculationDirection = BackwardSpeculation; - m_codeOriginForOSR = CodeOrigin(0); + m_origin = NodeOrigin(CodeOrigin(0), CodeOrigin(0), true); - for (size_t i = 0; i < m_arguments.size(); ++i) - m_arguments[i] = ValueSource(ValueInJSStack); - for (size_t i = 0; i < m_variables.size(); ++i) - m_variables[i] = ValueSource(ValueInJSStack); - for (int i = 0; i < m_jit.codeBlock()->numParameters(); ++i) { Node* node = m_jit.graph().m_arguments[i]; - ASSERT(node->op() == SetArgument); - if (!node->shouldGenerate()) { + if (!node) { // The argument is dead. We don't do any checks for such arguments. continue; } + ASSERT(node->op() == SetArgument); + ASSERT(node->shouldGenerate()); + VariableAccessData* variableAccessData = node->variableAccessData(); - if (!variableAccessData->isProfitableToUnbox()) + FlushFormat format = variableAccessData->flushFormat(); + + if (format == FlushedJSValue) continue; VirtualRegister virtualRegister = variableAccessData->local(); - SpeculatedType predictedType = variableAccessData->prediction(); JSValueSource valueSource = JSValueSource(JITCompiler::addressFor(virtualRegister)); #if USE(JSVALUE64) - if (isInt32Speculation(predictedType)) + switch (format) { + case FlushedInt32: { speculationCheck(BadType, valueSource, node, m_jit.branch64(MacroAssembler::Below, JITCompiler::addressFor(virtualRegister), GPRInfo::tagTypeNumberRegister)); - else if (isBooleanSpeculation(predictedType)) { + break; + } + case FlushedBoolean: { GPRTemporary temp(this); m_jit.load64(JITCompiler::addressFor(virtualRegister), temp.gpr()); m_jit.xor64(TrustedImm32(static_cast<int32_t>(ValueFalse)), temp.gpr()); speculationCheck(BadType, valueSource, node, m_jit.branchTest64(MacroAssembler::NonZero, temp.gpr(), TrustedImm32(static_cast<int32_t>(~1)))); - } else if (isCellSpeculation(predictedType)) + break; + } + case FlushedCell: { speculationCheck(BadType, valueSource, node, m_jit.branchTest64(MacroAssembler::NonZero, JITCompiler::addressFor(virtualRegister), GPRInfo::tagMaskRegister)); + break; + } + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } #else - if (isInt32Speculation(predictedType)) + switch (format) { + case FlushedInt32: { speculationCheck(BadType, valueSource, node, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::Int32Tag))); - else if (isBooleanSpeculation(predictedType)) + break; + } + case FlushedBoolean: { speculationCheck(BadType, valueSource, node, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::BooleanTag))); - else if (isCellSpeculation(predictedType)) + break; + } + case FlushedCell: { speculationCheck(BadType, valueSource, node, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::CellTag))); + break; + } + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } #endif } - m_isCheckingArgumentTypes = false; + + m_origin = NodeOrigin(); } bool SpeculativeJIT::compile() { checkArgumentTypes(); - - if (DFG_ENABLE_EDGE_CODE_VERIFICATION) - m_jit.move(TrustedImm32(0), GPRInfo::regT0); - + ASSERT(!m_currentNode); - for (m_block = 0; m_block < m_jit.graph().m_blocks.size(); ++m_block) { - m_jit.setForBlock(m_block); - BasicBlock* block = m_jit.graph().m_blocks[m_block].get(); - if (block) - compile(*block); + for (BlockIndex blockIndex = 0; blockIndex < m_jit.graph().numBlocks(); ++blockIndex) { + m_jit.setForBlockIndex(blockIndex); + m_block = m_jit.graph().block(blockIndex); + compileCurrentBlock(); } linkBranches(); return true; @@ -1918,33 +1623,24 @@ bool SpeculativeJIT::compile() void SpeculativeJIT::createOSREntries() { - for (BlockIndex blockIndex = 0; blockIndex < m_jit.graph().m_blocks.size(); ++blockIndex) { - BasicBlock* block = m_jit.graph().m_blocks[blockIndex].get(); + for (BlockIndex blockIndex = 0; blockIndex < m_jit.graph().numBlocks(); ++blockIndex) { + BasicBlock* block = m_jit.graph().block(blockIndex); if (!block) continue; if (!block->isOSRTarget) continue; - - // Currently we only need to create OSR entry trampolines when using edge code - // verification. But in the future, we'll need this for other things as well (like - // when we have global reg alloc). - // If we don't need OSR entry trampolin - if (!DFG_ENABLE_EDGE_CODE_VERIFICATION) { - m_osrEntryHeads.append(m_blockHeads[blockIndex]); - continue; - } - m_osrEntryHeads.append(m_jit.label()); - m_jit.move(TrustedImm32(blockIndex), GPRInfo::regT0); - m_jit.jump().linkTo(m_blockHeads[blockIndex], &m_jit); + // Currently we don't have OSR entry trampolines. We could add them + // here if need be. + m_osrEntryHeads.append(m_jit.blockHeads()[blockIndex]); } } void SpeculativeJIT::linkOSREntries(LinkBuffer& linkBuffer) { unsigned osrEntryIndex = 0; - for (BlockIndex blockIndex = 0; blockIndex < m_jit.graph().m_blocks.size(); ++blockIndex) { - BasicBlock* block = m_jit.graph().m_blocks[blockIndex].get(); + for (BlockIndex blockIndex = 0; blockIndex < m_jit.graph().numBlocks(); ++blockIndex) { + BasicBlock* block = m_jit.graph().block(blockIndex); if (!block) continue; if (!block->isOSRTarget) @@ -1952,19 +1648,15 @@ void SpeculativeJIT::linkOSREntries(LinkBuffer& linkBuffer) m_jit.noticeOSREntry(*block, m_osrEntryHeads[osrEntryIndex++], linkBuffer); } ASSERT(osrEntryIndex == m_osrEntryHeads.size()); -} - -ValueRecovery SpeculativeJIT::computeValueRecoveryFor(const ValueSource& valueSource) -{ - if (valueSource.isInJSStack()) - return valueSource.valueRecovery(); - - ASSERT(valueSource.kind() == HaveNode); - Node* node = valueSource.id().node(m_jit.graph()); - if (isConstant(node)) - return ValueRecovery::constant(valueOfJSConstant(node)); - return ValueRecovery(); + if (verboseCompilationEnabled()) { + DumpContext dumpContext; + dataLog("OSR Entries:\n"); + for (OSREntryData& entryData : m_jit.jitCode()->osrEntry) + dataLog(" ", inContext(entryData, &dumpContext), "\n"); + if (!dumpContext.isEmpty()) + dumpContext.dump(WTF::dataFile()); + } } void SpeculativeJIT::compileDoublePutByVal(Node* node, SpeculateCellOperand& base, SpeculateStrictInt32Operand& property) @@ -1982,7 +1674,7 @@ void SpeculativeJIT::compileDoublePutByVal(Node* node, SpeculateCellOperand& bas FPRReg valueReg = value.fpr(); DFG_TYPE_CHECK( - JSValueRegs(), child3, SpecRealNumber, + JSValueRegs(), child3, SpecFullRealNumber, m_jit.branchDouble( MacroAssembler::DoubleNotEqualOrUnordered, valueReg, valueReg)); @@ -2009,7 +1701,7 @@ void SpeculativeJIT::compileDoublePutByVal(Node* node, SpeculateCellOperand& bas if (arrayMode.isInBounds()) { speculationCheck( - StoreToHoleOrOutOfBounds, JSValueRegs(), 0, + OutOfBounds, JSValueRegs(), 0, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); } else { MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())); @@ -2075,7 +1767,7 @@ void SpeculativeJIT::compileGetCharCodeAt(Node* node) cont8Bit.link(&m_jit); - integerResult(scratchReg, m_currentNode); + int32Result(scratchReg, m_currentNode); } void SpeculativeJIT::compileGetByValOnString(Node* node) @@ -2087,13 +1779,26 @@ void SpeculativeJIT::compileGetByValOnString(Node* node) GPRReg propertyReg = property.gpr(); GPRReg storageReg = storage.gpr(); + GPRTemporary scratch(this); + GPRReg scratchReg = scratch.gpr(); +#if USE(JSVALUE32_64) + GPRTemporary resultTag; + GPRReg resultTagReg = InvalidGPRReg; + if (node->arrayMode().isOutOfBounds()) { + GPRTemporary realResultTag(this); + resultTag.adopt(realResultTag); + resultTagReg = resultTag.gpr(); + } +#endif + ASSERT(ArrayMode(Array::String).alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1()))); // unsigned comparison so we can filter out negative indices and indices that are too large - speculationCheck(Uncountable, JSValueRegs(), 0, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSString::offsetOfLength()))); - - GPRTemporary scratch(this); - GPRReg scratchReg = scratch.gpr(); + JITCompiler::Jump outOfBounds = m_jit.branch32( + MacroAssembler::AboveOrEqual, propertyReg, + MacroAssembler::Address(baseReg, JSString::offsetOfLength())); + if (node->arrayMode().isInBounds()) + speculationCheck(OutOfBounds, JSValueRegs(), 0, outOfBounds); m_jit.loadPtr(MacroAssembler::Address(baseReg, JSString::offsetOfValue()), scratchReg); @@ -2107,23 +1812,90 @@ void SpeculativeJIT::compileGetByValOnString(Node* node) m_jit.load16(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesTwo, 0), scratchReg); - // We only support ascii characters - speculationCheck(Uncountable, JSValueRegs(), 0, m_jit.branch32(MacroAssembler::AboveOrEqual, scratchReg, TrustedImm32(0x100))); + JITCompiler::Jump bigCharacter = + 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.vm()->smallStrings.singleCharacterStrings()), smallStringsReg); - m_jit.loadPtr(MacroAssembler::BaseIndex(smallStringsReg, scratchReg, MacroAssembler::ScalePtr, 0), scratchReg); - speculationCheck(Uncountable, JSValueRegs(), 0, m_jit.branchTest32(MacroAssembler::Zero, scratchReg)); - cellResult(scratchReg, m_currentNode); + m_jit.lshift32(MacroAssembler::TrustedImm32(sizeof(void*) == 4 ? 2 : 3), scratchReg); + m_jit.addPtr(MacroAssembler::TrustedImmPtr(m_jit.vm()->smallStrings.singleCharacterStrings()), scratchReg); + m_jit.loadPtr(scratchReg, scratchReg); + + addSlowPathGenerator( + slowPathCall( + bigCharacter, this, operationSingleCharacterString, scratchReg, scratchReg)); + + if (node->arrayMode().isOutOfBounds()) { +#if USE(JSVALUE32_64) + m_jit.move(TrustedImm32(JSValue::CellTag), resultTagReg); +#endif + + JSGlobalObject* globalObject = m_jit.globalObjectFor(node->origin.semantic); + if (globalObject->stringPrototypeChainIsSane()) { + // FIXME: This could be captured using a Speculation mode that means "out-of-bounds + // loads return a trivial value". Something like SaneChainOutOfBounds. This should + // speculate that we don't take negative out-of-bounds, or better yet, it should rely + // on a stringPrototypeChainIsSane() guaranteeing that the prototypes have no negative + // indexed properties either. + // https://bugs.webkit.org/show_bug.cgi?id=144668 + m_jit.graph().watchpoints().addLazily(globalObject->stringPrototype()->structure()->transitionWatchpointSet()); + m_jit.graph().watchpoints().addLazily(globalObject->objectPrototype()->structure()->transitionWatchpointSet()); + +#if USE(JSVALUE64) + addSlowPathGenerator(std::make_unique<SaneStringGetByValSlowPathGenerator>( + outOfBounds, this, JSValueRegs(scratchReg), baseReg, propertyReg)); +#else + addSlowPathGenerator(std::make_unique<SaneStringGetByValSlowPathGenerator>( + outOfBounds, this, JSValueRegs(resultTagReg, scratchReg), + baseReg, propertyReg)); +#endif + } else { +#if USE(JSVALUE64) + addSlowPathGenerator( + slowPathCall( + outOfBounds, this, operationGetByValStringInt, + scratchReg, baseReg, propertyReg)); +#else + addSlowPathGenerator( + slowPathCall( + outOfBounds, this, operationGetByValStringInt, + resultTagReg, scratchReg, baseReg, propertyReg)); +#endif + } + +#if USE(JSVALUE64) + jsValueResult(scratchReg, m_currentNode); +#else + jsValueResult(resultTagReg, scratchReg, m_currentNode); +#endif + } else + cellResult(scratchReg, m_currentNode); } void SpeculativeJIT::compileFromCharCode(Node* node) { - SpeculateStrictInt32Operand property(this, node->child1()); + Edge& child = node->child1(); + if (child.useKind() == UntypedUse) { + JSValueOperand opr(this, child); + JSValueRegs oprRegs = opr.jsValueRegs(); +#if USE(JSVALUE64) + GPRTemporary result(this); + JSValueRegs resultRegs = JSValueRegs(result.gpr()); +#else + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); + JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr()); +#endif + flushRegisters(); + callOperation(operationStringFromCharCodeUntyped, resultRegs, oprRegs); + m_jit.exceptionCheck(); + + jsValueResult(resultRegs, node); + return; + } + + SpeculateStrictInt32Operand property(this, child); GPRReg propertyReg = property.gpr(); GPRTemporary smallStrings(this); GPRTemporary scratch(this); @@ -2142,11 +1914,8 @@ void SpeculativeJIT::compileFromCharCode(Node* node) GeneratedOperandType SpeculativeJIT::checkGeneratedTypeForToInt32(Node* node) { -#if DFG_ENABLE(DEBUG_VERBOSE) - dataLogF("checkGeneratedTypeForToInt32@%d ", node->index()); -#endif VirtualRegister virtualRegister = node->virtualRegister(); - GenerationInfo& info = m_generationInfo[virtualRegister]; + GenerationInfo& info = generationInfoFromVirtualRegister(virtualRegister); switch (info.registerFormat()) { case DataFormatStorage: @@ -2161,16 +1930,13 @@ GeneratedOperandType SpeculativeJIT::checkGeneratedTypeForToInt32(Node* node) case DataFormatJSCell: case DataFormatJS: case DataFormatJSBoolean: + case DataFormatJSDouble: return GeneratedOperandJSValue; - case DataFormatJSInteger: - case DataFormatInteger: + case DataFormatJSInt32: + case DataFormatInt32: return GeneratedOperandInteger; - case DataFormatJSDouble: - case DataFormatDouble: - return GeneratedOperandDouble; - default: RELEASE_ASSERT_NOT_REACHED(); return GeneratedOperandTypeUnknown; @@ -2180,11 +1946,28 @@ GeneratedOperandType SpeculativeJIT::checkGeneratedTypeForToInt32(Node* node) void SpeculativeJIT::compileValueToInt32(Node* node) { switch (node->child1().useKind()) { - case Int32Use: { - SpeculateIntegerOperand op1(this, node->child1()); - GPRTemporary result(this, op1); - m_jit.move(op1.gpr(), result.gpr()); - integerResult(result.gpr(), node, op1.format()); +#if USE(JSVALUE64) + case Int52RepUse: { + SpeculateStrictInt52Operand op1(this, node->child1()); + GPRTemporary result(this, Reuse, op1); + GPRReg op1GPR = op1.gpr(); + GPRReg resultGPR = result.gpr(); + m_jit.zeroExtend32ToPtr(op1GPR, resultGPR); + int32Result(resultGPR, node, DataFormatInt32); + return; + } +#endif // USE(JSVALUE64) + + case DoubleRepUse: { + GPRTemporary result(this); + SpeculateDoubleOperand op1(this, node->child1()); + FPRReg fpr = op1.fpr(); + GPRReg gpr = result.gpr(); + JITCompiler::Jump notTruncatedToInteger = m_jit.branchTruncateDoubleToInt32(fpr, gpr, JITCompiler::BranchIfTruncateFailed); + + addSlowPathGenerator(slowPathCall(notTruncatedToInteger, this, toInt32, gpr, fpr, NeedToSpill, ExceptionCheckRequirement::CheckNotNeeded)); + + int32Result(gpr, node); return; } @@ -2192,22 +1975,10 @@ void SpeculativeJIT::compileValueToInt32(Node* node) case NotCellUse: { switch (checkGeneratedTypeForToInt32(node->child1().node())) { case GeneratedOperandInteger: { - SpeculateIntegerOperand op1(this, node->child1(), ManualOperandSpeculation); - GPRTemporary result(this, op1); + SpeculateInt32Operand op1(this, node->child1(), ManualOperandSpeculation); + GPRTemporary result(this, Reuse, op1); m_jit.move(op1.gpr(), result.gpr()); - integerResult(result.gpr(), node, op1.format()); - return; - } - case GeneratedOperandDouble: { - GPRTemporary result(this); - SpeculateDoubleOperand op1(this, node->child1(), ManualOperandSpeculation); - FPRReg fpr = op1.fpr(); - GPRReg gpr = result.gpr(); - JITCompiler::Jump notTruncatedToInteger = m_jit.branchTruncateDoubleToInt32(fpr, gpr, JITCompiler::BranchIfTruncateFailed); - - addSlowPathGenerator(slowPathCall(notTruncatedToInteger, this, toInt32, gpr, fpr)); - - integerResult(gpr, node); + int32Result(result.gpr(), node, op1.format()); return; } case GeneratedOperandJSValue: { @@ -2225,16 +1996,14 @@ void SpeculativeJIT::compileValueToInt32(Node* node) if (node->child1().useKind() == NumberUse) { DFG_TYPE_CHECK( - JSValueRegs(gpr), node->child1(), SpecNumber, + JSValueRegs(gpr), node->child1(), SpecBytecodeNumber, m_jit.branchTest64( MacroAssembler::Zero, gpr, GPRInfo::tagTypeNumberRegister)); } else { JITCompiler::Jump isNumber = m_jit.branchTest64(MacroAssembler::NonZero, gpr, GPRInfo::tagTypeNumberRegister); DFG_TYPE_CHECK( - JSValueRegs(gpr), node->child1(), ~SpecCell, - m_jit.branchTest64( - JITCompiler::Zero, gpr, GPRInfo::tagMaskRegister)); + JSValueRegs(gpr), node->child1(), ~SpecCell, m_jit.branchIfCell(JSValueRegs(gpr))); // It's not a cell: so true turns into 1 and all else turns into 0. m_jit.compare64(JITCompiler::Equal, gpr, TrustedImm32(ValueTrue), resultGpr); @@ -2244,8 +2013,7 @@ void SpeculativeJIT::compileValueToInt32(Node* node) } // First, if we get here we have a double encoded as a JSValue - m_jit.move(gpr, resultGpr); - unboxDouble(resultGpr, fpr); + unboxDouble(gpr, resultGpr, fpr); silentSpillAllRegisters(resultGpr); callOperation(toInt32, resultGpr, fpr); @@ -2260,7 +2028,7 @@ void SpeculativeJIT::compileValueToInt32(Node* node) #else Node* childNode = node->child1().node(); VirtualRegister virtualRegister = childNode->virtualRegister(); - GenerationInfo& info = m_generationInfo[virtualRegister]; + GenerationInfo& info = generationInfoFromVirtualRegister(virtualRegister); JSValueOperand op1(this, node->child1(), ManualOperandSpeculation); @@ -2269,7 +2037,7 @@ void SpeculativeJIT::compileValueToInt32(Node* node) JITCompiler::JumpList converted; - if (info.registerFormat() == DataFormatJSInteger) + if (info.registerFormat() == DataFormatJSInt32) m_jit.move(payloadGPR, resultGpr); else { GPRReg tagGPR = op1.tagGPR(); @@ -2281,7 +2049,7 @@ void SpeculativeJIT::compileValueToInt32(Node* node) if (node->child1().useKind() == NumberUse) { DFG_TYPE_CHECK( - JSValueRegs(tagGPR, payloadGPR), node->child1(), SpecNumber, + op1.jsValueRegs(), node->child1(), SpecBytecodeNumber, m_jit.branch32( MacroAssembler::AboveOrEqual, tagGPR, TrustedImm32(JSValue::LowestTag))); @@ -2289,9 +2057,8 @@ void SpeculativeJIT::compileValueToInt32(Node* node) JITCompiler::Jump isNumber = m_jit.branch32(MacroAssembler::Below, tagGPR, TrustedImm32(JSValue::LowestTag)); DFG_TYPE_CHECK( - JSValueRegs(tagGPR, payloadGPR), node->child1(), ~SpecCell, - m_jit.branch32( - JITCompiler::Equal, tagGPR, TrustedImm32(JSValue::CellTag))); + op1.jsValueRegs(), node->child1(), ~SpecCell, + m_jit.branchIfCell(op1.jsValueRegs())); // It's not a cell: so true turns into 1 and all else turns into 0. JITCompiler::Jump isBoolean = m_jit.branch32(JITCompiler::Equal, tagGPR, TrustedImm32(JSValue::BooleanTag)); @@ -2319,7 +2086,7 @@ void SpeculativeJIT::compileValueToInt32(Node* node) converted.link(&m_jit); } #endif - integerResult(resultGpr, node); + int32Result(resultGpr, node); return; } case GeneratedOperandTypeUnknown: @@ -2330,17 +2097,6 @@ void SpeculativeJIT::compileValueToInt32(Node* node) return; } - case BooleanUse: { - SpeculateBooleanOperand op1(this, node->child1()); - GPRTemporary result(this, op1); - - m_jit.move(op1.gpr(), result.gpr()); - m_jit.and32(JITCompiler::TrustedImm32(1), result.gpr()); - - integerResult(result.gpr(), node); - return; - } - default: ASSERT(!m_compileOkay); return; @@ -2349,11 +2105,11 @@ void SpeculativeJIT::compileValueToInt32(Node* node) void SpeculativeJIT::compileUInt32ToNumber(Node* node) { - if (!nodeCanSpeculateInteger(node->arithNodeFlags())) { + if (doesOverflow(node->arithMode())) { // 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()); + SpeculateInt32Operand op1(this, node->child1()); FPRTemporary result(this); GPRReg inputGPR = op1.gpr(); @@ -2368,20 +2124,17 @@ void SpeculativeJIT::compileUInt32ToNumber(Node* node) doubleResult(outputFPR, node); return; } + + RELEASE_ASSERT(node->arithMode() == Arith::CheckOverflow); - IntegerOperand op1(this, node->child1()); - GPRTemporary result(this); // For the benefit of OSR exit, force these to be in different registers. In reality the OSR exit compiler could find cases where you have uint32(%r1) followed by int32(%r1) and then use different registers, but that seems like too much effort. + SpeculateInt32Operand op1(this, node->child1()); + GPRTemporary result(this); m_jit.move(op1.gpr(), result.gpr()); - // 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. - forwardSpeculationCheck(Overflow, JSValueRegs(), 0, m_jit.branch32(MacroAssembler::LessThan, result.gpr(), TrustedImm32(0)), ValueRecovery::uint32InGPR(result.gpr())); + speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branch32(MacroAssembler::LessThan, result.gpr(), TrustedImm32(0))); - integerResult(result.gpr(), node, op1.format()); + int32Result(result.gpr(), node, op1.format()); } void SpeculativeJIT::compileDoubleAsInt32(Node* node) @@ -2395,91 +2148,229 @@ void SpeculativeJIT::compileDoubleAsInt32(Node* node) GPRReg resultGPR = result.gpr(); JITCompiler::JumpList failureCases; - bool negZeroCheck = !nodeCanIgnoreNegativeZero(node->arithNodeFlags()); - m_jit.branchConvertDoubleToInt32(valueFPR, resultGPR, failureCases, scratchFPR, negZeroCheck); - forwardSpeculationCheck(Overflow, JSValueRegs(), 0, failureCases, ValueRecovery::inFPR(valueFPR)); + RELEASE_ASSERT(shouldCheckOverflow(node->arithMode())); + m_jit.branchConvertDoubleToInt32( + valueFPR, resultGPR, failureCases, scratchFPR, + shouldCheckNegativeZero(node->arithMode())); + speculationCheck(Overflow, JSValueRegs(), 0, failureCases); - integerResult(resultGPR, node); + int32Result(resultGPR, node); } -void SpeculativeJIT::compileInt32ToDouble(Node* node) +void SpeculativeJIT::compileDoubleRep(Node* node) { - ASSERT(!isInt32Constant(node->child1().node())); // This should have been constant folded. - - if (isInt32Speculation(m_state.forNode(node->child1()).m_type)) { - SpeculateIntegerOperand op1(this, node->child1(), ManualOperandSpeculation); + switch (node->child1().useKind()) { + case RealNumberUse: { + JSValueOperand op1(this, node->child1(), ManualOperandSpeculation); FPRTemporary result(this); - m_jit.convertInt32ToDouble(op1.gpr(), result.fpr()); - doubleResult(result.fpr(), node); + + JSValueRegs op1Regs = op1.jsValueRegs(); + FPRReg resultFPR = result.fpr(); + +#if USE(JSVALUE64) + GPRTemporary temp(this); + GPRReg tempGPR = temp.gpr(); + m_jit.unboxDoubleWithoutAssertions(op1Regs.gpr(), tempGPR, resultFPR); +#else + FPRTemporary temp(this); + FPRReg tempFPR = temp.fpr(); + unboxDouble(op1Regs.tagGPR(), op1Regs.payloadGPR(), resultFPR, tempFPR); +#endif + + JITCompiler::Jump done = m_jit.branchDouble( + JITCompiler::DoubleEqual, resultFPR, resultFPR); + + DFG_TYPE_CHECK( + op1Regs, node->child1(), SpecBytecodeRealNumber, m_jit.branchIfNotInt32(op1Regs)); + m_jit.convertInt32ToDouble(op1Regs.payloadGPR(), resultFPR); + + done.link(&m_jit); + + doubleResult(resultFPR, node); return; } - JSValueOperand op1(this, node->child1(), ManualOperandSpeculation); - FPRTemporary result(this); - + case NotCellUse: + case NumberUse: { + ASSERT(!node->child1()->isNumberConstant()); // This should have been constant folded. + + SpeculatedType possibleTypes = m_state.forNode(node->child1()).m_type; + if (isInt32Speculation(possibleTypes)) { + SpeculateInt32Operand op1(this, node->child1(), ManualOperandSpeculation); + FPRTemporary result(this); + m_jit.convertInt32ToDouble(op1.gpr(), result.fpr()); + doubleResult(result.fpr(), node); + return; + } + + JSValueOperand op1(this, node->child1(), ManualOperandSpeculation); + FPRTemporary result(this); + #if USE(JSVALUE64) - GPRTemporary temp(this); + GPRTemporary temp(this); - GPRReg op1GPR = op1.gpr(); - GPRReg tempGPR = temp.gpr(); - FPRReg resultFPR = result.fpr(); - - JITCompiler::Jump isInteger = m_jit.branch64( - MacroAssembler::AboveOrEqual, op1GPR, GPRInfo::tagTypeNumberRegister); - - if (needsTypeCheck(node->child1(), SpecNumber)) { - if (node->op() == ForwardInt32ToDouble) { - forwardTypeCheck( - JSValueRegs(op1GPR), node->child1(), SpecNumber, - m_jit.branchTest64(MacroAssembler::Zero, op1GPR, GPRInfo::tagTypeNumberRegister), - ValueRecovery::inGPR(op1GPR, DataFormatJS)); - } else { - backwardTypeCheck( - JSValueRegs(op1GPR), node->child1(), SpecNumber, + GPRReg op1GPR = op1.gpr(); + GPRReg tempGPR = temp.gpr(); + FPRReg resultFPR = result.fpr(); + JITCompiler::JumpList done; + + JITCompiler::Jump isInteger = m_jit.branch64( + MacroAssembler::AboveOrEqual, op1GPR, GPRInfo::tagTypeNumberRegister); + + if (node->child1().useKind() == NotCellUse) { + JITCompiler::Jump isNumber = m_jit.branchTest64(MacroAssembler::NonZero, op1GPR, GPRInfo::tagTypeNumberRegister); + JITCompiler::Jump isUndefined = m_jit.branch64(JITCompiler::Equal, op1GPR, TrustedImm64(ValueUndefined)); + + static const double zero = 0; + m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&zero), resultFPR); + + JITCompiler::Jump isNull = m_jit.branch64(JITCompiler::Equal, op1GPR, TrustedImm64(ValueNull)); + done.append(isNull); + + DFG_TYPE_CHECK(JSValueRegs(op1GPR), node->child1(), ~SpecCell, + m_jit.branchTest64(JITCompiler::Zero, op1GPR, TrustedImm32(static_cast<int32_t>(TagBitBool)))); + + JITCompiler::Jump isFalse = m_jit.branch64(JITCompiler::Equal, op1GPR, TrustedImm64(ValueFalse)); + static const double one = 1; + m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&one), resultFPR); + done.append(m_jit.jump()); + done.append(isFalse); + + isUndefined.link(&m_jit); + static const double NaN = PNaN; + m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&NaN), resultFPR); + done.append(m_jit.jump()); + + isNumber.link(&m_jit); + } else if (needsTypeCheck(node->child1(), SpecBytecodeNumber)) { + typeCheck( + JSValueRegs(op1GPR), node->child1(), SpecBytecodeNumber, m_jit.branchTest64(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); + + unboxDouble(op1GPR, tempGPR, resultFPR); + done.append(m_jit.jump()); - GPRReg op1TagGPR = op1.tagGPR(); - GPRReg op1PayloadGPR = op1.payloadGPR(); - FPRReg tempFPR = temp.fpr(); - FPRReg resultFPR = result.fpr(); + isInteger.link(&m_jit); + m_jit.convertInt32ToDouble(op1GPR, resultFPR); + done.link(&m_jit); +#else // USE(JSVALUE64) -> this is the 32_64 case + FPRTemporary temp(this); - JITCompiler::Jump isInteger = m_jit.branch32( - MacroAssembler::Equal, op1TagGPR, TrustedImm32(JSValue::Int32Tag)); + GPRReg op1TagGPR = op1.tagGPR(); + GPRReg op1PayloadGPR = op1.payloadGPR(); + FPRReg tempFPR = temp.fpr(); + FPRReg resultFPR = result.fpr(); + JITCompiler::JumpList done; - if (needsTypeCheck(node->child1(), SpecNumber)) { - if (node->op() == ForwardInt32ToDouble) { - forwardTypeCheck( - JSValueRegs(op1TagGPR, op1PayloadGPR), node->child1(), SpecNumber, - m_jit.branch32(MacroAssembler::AboveOrEqual, op1TagGPR, TrustedImm32(JSValue::LowestTag)), - ValueRecovery::inPair(op1TagGPR, op1PayloadGPR)); - } else { - backwardTypeCheck( - JSValueRegs(op1TagGPR, op1PayloadGPR), node->child1(), SpecNumber, + JITCompiler::Jump isInteger = m_jit.branch32( + MacroAssembler::Equal, op1TagGPR, TrustedImm32(JSValue::Int32Tag)); + + if (node->child1().useKind() == NotCellUse) { + JITCompiler::Jump isNumber = m_jit.branch32(JITCompiler::Below, op1TagGPR, JITCompiler::TrustedImm32(JSValue::LowestTag + 1)); + JITCompiler::Jump isUndefined = m_jit.branch32(JITCompiler::Equal, op1TagGPR, TrustedImm32(JSValue::UndefinedTag)); + + static const double zero = 0; + m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&zero), resultFPR); + + JITCompiler::Jump isNull = m_jit.branch32(JITCompiler::Equal, op1TagGPR, TrustedImm32(JSValue::NullTag)); + done.append(isNull); + + DFG_TYPE_CHECK(JSValueRegs(op1TagGPR, op1PayloadGPR), node->child1(), ~SpecCell, m_jit.branch32(JITCompiler::NotEqual, op1TagGPR, TrustedImm32(JSValue::BooleanTag))); + + JITCompiler::Jump isFalse = m_jit.branchTest32(JITCompiler::Zero, op1PayloadGPR, TrustedImm32(1)); + static const double one = 1; + m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&one), resultFPR); + done.append(m_jit.jump()); + done.append(isFalse); + + isUndefined.link(&m_jit); + static const double NaN = PNaN; + m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&NaN), resultFPR); + done.append(m_jit.jump()); + + isNumber.link(&m_jit); + } else if (needsTypeCheck(node->child1(), SpecBytecodeNumber)) { + typeCheck( + JSValueRegs(op1TagGPR, op1PayloadGPR), node->child1(), SpecBytecodeNumber, m_jit.branch32(MacroAssembler::AboveOrEqual, op1TagGPR, TrustedImm32(JSValue::LowestTag))); } - } - - unboxDouble(op1TagGPR, op1PayloadGPR, resultFPR, tempFPR); - JITCompiler::Jump done = m_jit.jump(); + + unboxDouble(op1TagGPR, op1PayloadGPR, resultFPR, tempFPR); + done.append(m_jit.jump()); - isInteger.link(&m_jit); - m_jit.convertInt32ToDouble(op1PayloadGPR, resultFPR); - done.link(&m_jit); -#endif + isInteger.link(&m_jit); + m_jit.convertInt32ToDouble(op1PayloadGPR, resultFPR); + done.link(&m_jit); +#endif // USE(JSVALUE64) - doubleResult(resultFPR, node); + doubleResult(resultFPR, node); + return; + } + +#if USE(JSVALUE64) + case Int52RepUse: { + SpeculateStrictInt52Operand value(this, node->child1()); + FPRTemporary result(this); + + GPRReg valueGPR = value.gpr(); + FPRReg resultFPR = result.fpr(); + + m_jit.convertInt64ToDouble(valueGPR, resultFPR); + + doubleResult(resultFPR, node); + return; + } +#endif // USE(JSVALUE64) + + default: + RELEASE_ASSERT_NOT_REACHED(); + return; + } +} + +void SpeculativeJIT::compileValueRep(Node* node) +{ + switch (node->child1().useKind()) { + case DoubleRepUse: { + SpeculateDoubleOperand value(this, node->child1()); + JSValueRegsTemporary result(this); + + FPRReg valueFPR = value.fpr(); + JSValueRegs resultRegs = result.regs(); + + // It's very tempting to in-place filter the value to indicate that it's not impure NaN + // anymore. Unfortunately, this would be unsound. If it's a GetLocal or if the value was + // subject to a prior SetLocal, filtering the value would imply that the corresponding + // local was purified. + if (needsTypeCheck(node->child1(), ~SpecDoubleImpureNaN)) + m_jit.purifyNaN(valueFPR); + + boxDouble(valueFPR, resultRegs); + + jsValueResult(resultRegs, node); + return; + } + +#if USE(JSVALUE64) + case Int52RepUse: { + SpeculateStrictInt52Operand value(this, node->child1()); + GPRTemporary result(this); + + GPRReg valueGPR = value.gpr(); + GPRReg resultGPR = result.gpr(); + + boxInt52(valueGPR, resultGPR, DataFormatStrictInt52); + + jsValueResult(resultGPR, node); + return; + } +#endif // USE(JSVALUE64) + + default: + RELEASE_ASSERT_NOT_REACHED(); + return; + } } static double clampDoubleToByte(double d) @@ -2510,12 +2401,12 @@ static void compileClampDoubleToByte(JITCompiler& jit, GPRReg result, FPRReg sou static const double zero = 0; static const double byteMax = 255; static const double half = 0.5; - jit.loadDouble(&zero, scratch); + jit.loadDouble(MacroAssembler::TrustedImmPtr(&zero), scratch); MacroAssembler::Jump tooSmall = jit.branchDouble(MacroAssembler::DoubleLessThanOrEqualOrUnordered, source, scratch); - jit.loadDouble(&byteMax, scratch); + jit.loadDouble(MacroAssembler::TrustedImmPtr(&byteMax), scratch); MacroAssembler::Jump tooBig = jit.branchDouble(MacroAssembler::DoubleGreaterThan, source, scratch); - jit.loadDouble(&half, scratch); + jit.loadDouble(MacroAssembler::TrustedImmPtr(&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); @@ -2534,8 +2425,37 @@ static void compileClampDoubleToByte(JITCompiler& jit, GPRReg result, FPRReg sou } -void SpeculativeJIT::compileGetByValOnIntTypedArray(const TypedArrayDescriptor& descriptor, Node* node, size_t elementSize, TypedArraySignedness signedness) +JITCompiler::Jump SpeculativeJIT::jumpForTypedArrayOutOfBounds(Node* node, GPRReg baseGPR, GPRReg indexGPR) +{ + if (node->op() == PutByValAlias) + return JITCompiler::Jump(); + JSArrayBufferView* view = m_jit.graph().tryGetFoldableView( + m_state.forNode(m_jit.graph().child(node, 0)).m_value, node->arrayMode()); + if (view) { + uint32_t length = view->length(); + Node* indexNode = m_jit.graph().child(node, 1).node(); + if (indexNode->isInt32Constant() && indexNode->asUInt32() < length) + return JITCompiler::Jump(); + return m_jit.branch32( + MacroAssembler::AboveOrEqual, indexGPR, MacroAssembler::Imm32(length)); + } + return m_jit.branch32( + MacroAssembler::AboveOrEqual, indexGPR, + MacroAssembler::Address(baseGPR, JSArrayBufferView::offsetOfLength())); +} + +void SpeculativeJIT::emitTypedArrayBoundsCheck(Node* node, GPRReg baseGPR, GPRReg indexGPR) { + JITCompiler::Jump jump = jumpForTypedArrayOutOfBounds(node, baseGPR, indexGPR); + if (!jump.isSet()) + return; + speculationCheck(OutOfBounds, JSValueRegs(), 0, jump); +} + +void SpeculativeJIT::compileGetByValOnIntTypedArray(Node* node, TypedArrayType type) +{ + ASSERT(isInt(type)); + SpeculateCellOperand base(this, node->child1()); SpeculateStrictInt32Operand property(this, node->child2()); StorageOperand storage(this, node->child3()); @@ -2549,20 +2469,17 @@ void SpeculativeJIT::compileGetByValOnIntTypedArray(const TypedArrayDescriptor& ASSERT(node->arrayMode().alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1()))); - speculationCheck( - Uncountable, JSValueRegs(), 0, - m_jit.branch32( - MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, descriptor.m_lengthOffset))); - switch (elementSize) { + emitTypedArrayBoundsCheck(node, baseReg, propertyReg); + switch (elementSize(type)) { case 1: - if (signedness == SignedTypedArray) - m_jit.load8Signed(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesOne), resultReg); + if (isSigned(type)) + m_jit.load8SignedExtendTo32(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); + if (isSigned(type)) + m_jit.load16SignedExtendTo32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesTwo), resultReg); else m_jit.load16(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesTwo), resultReg); break; @@ -2572,18 +2489,26 @@ void SpeculativeJIT::compileGetByValOnIntTypedArray(const TypedArrayDescriptor& default: CRASH(); } - if (elementSize < 4 || signedness == SignedTypedArray) { - integerResult(resultReg, node); + if (elementSize(type) < 4 || isSigned(type)) { + int32Result(resultReg, node); return; } - ASSERT(elementSize == 4 && signedness == UnsignedTypedArray); - if (node->shouldSpeculateInteger()) { - forwardSpeculationCheck(Overflow, JSValueRegs(), 0, m_jit.branch32(MacroAssembler::LessThan, resultReg, TrustedImm32(0)), ValueRecovery::uint32InGPR(resultReg)); - integerResult(resultReg, node); + ASSERT(elementSize(type) == 4 && !isSigned(type)); + if (node->shouldSpeculateInt32()) { + speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branch32(MacroAssembler::LessThan, resultReg, TrustedImm32(0))); + int32Result(resultReg, node); return; } +#if USE(JSVALUE64) + if (node->shouldSpeculateMachineInt()) { + m_jit.zeroExtend32ToPtr(resultReg, resultReg); + strictInt52Result(resultReg, node); + return; + } +#endif + FPRTemporary fresult(this); m_jit.convertInt32ToDouble(resultReg, fresult.fpr()); JITCompiler::Jump positive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, resultReg, TrustedImm32(0)); @@ -2592,8 +2517,10 @@ void SpeculativeJIT::compileGetByValOnIntTypedArray(const TypedArrayDescriptor& doubleResult(fresult.fpr(), node); } -void SpeculativeJIT::compilePutByValForIntTypedArray(const TypedArrayDescriptor& descriptor, GPRReg base, GPRReg property, Node* node, size_t elementSize, TypedArraySignedness signedness, TypedArrayRounding rounding) +void SpeculativeJIT::compilePutByValForIntTypedArray(GPRReg base, GPRReg property, Node* node, TypedArrayType type) { + ASSERT(isInt(type)); + StorageOperand storage(this, m_jit.graph().varArgChild(node, 3)); GPRReg storageReg = storage.gpr(); @@ -2603,15 +2530,15 @@ void SpeculativeJIT::compilePutByValForIntTypedArray(const TypedArrayDescriptor& GPRReg valueGPR = InvalidGPRReg; if (valueUse->isConstant()) { - JSValue jsValue = valueOfJSConstant(valueUse.node()); + JSValue jsValue = valueUse->asJSValue(); if (!jsValue.isNumber()) { terminateSpeculativeExecution(Uncountable, JSValueRegs(), 0); noResult(node); return; } double d = jsValue.asNumber(); - if (rounding == ClampRounding) { - ASSERT(elementSize == 1); + if (isClamped(type)) { + ASSERT(elementSize(type) == 1); d = clampDoubleToByte(d); } GPRTemporary scratch(this); @@ -2622,12 +2549,12 @@ void SpeculativeJIT::compilePutByValForIntTypedArray(const TypedArrayDescriptor& } else { switch (valueUse.useKind()) { case Int32Use: { - SpeculateIntegerOperand valueOp(this, valueUse); + SpeculateInt32Operand valueOp(this, valueUse); GPRTemporary scratch(this); GPRReg scratchReg = scratch.gpr(); m_jit.move(valueOp.gpr(), scratchReg); - if (rounding == ClampRounding) { - ASSERT(elementSize == 1); + if (isClamped(type)) { + ASSERT(elementSize(type) == 1); compileClampIntegerToByte(m_jit, scratchReg); } value.adopt(scratch); @@ -2635,9 +2562,34 @@ void SpeculativeJIT::compilePutByValForIntTypedArray(const TypedArrayDescriptor& break; } - case NumberUse: { - if (rounding == ClampRounding) { - ASSERT(elementSize == 1); +#if USE(JSVALUE64) + case Int52RepUse: { + SpeculateStrictInt52Operand valueOp(this, valueUse); + GPRTemporary scratch(this); + GPRReg scratchReg = scratch.gpr(); + m_jit.move(valueOp.gpr(), scratchReg); + if (isClamped(type)) { + ASSERT(elementSize(type) == 1); + MacroAssembler::Jump inBounds = m_jit.branch64( + MacroAssembler::BelowOrEqual, scratchReg, JITCompiler::TrustedImm64(0xff)); + MacroAssembler::Jump tooBig = m_jit.branch64( + MacroAssembler::GreaterThan, scratchReg, JITCompiler::TrustedImm64(0xff)); + m_jit.move(TrustedImm32(0), scratchReg); + MacroAssembler::Jump clamped = m_jit.jump(); + tooBig.link(&m_jit); + m_jit.move(JITCompiler::TrustedImm32(255), scratchReg); + clamped.link(&m_jit); + inBounds.link(&m_jit); + } + value.adopt(scratch); + valueGPR = scratchReg; + break; + } +#endif // USE(JSVALUE64) + + case DoubleRepUse: { + if (isClamped(type)) { + ASSERT(elementSize(type) == 1); SpeculateDoubleOperand valueOp(this, valueUse); GPRTemporary result(this); FPRTemporary floatScratch(this); @@ -2656,13 +2608,10 @@ void SpeculativeJIT::compilePutByValForIntTypedArray(const TypedArrayDescriptor& MacroAssembler::Jump fixed = m_jit.jump(); notNaN.link(&m_jit); - MacroAssembler::Jump failed; - if (signedness == SignedTypedArray) - failed = m_jit.branchTruncateDoubleToInt32(fpr, gpr, MacroAssembler::BranchIfTruncateFailed); - else - failed = m_jit.branchTruncateDoubleToUint32(fpr, gpr, MacroAssembler::BranchIfTruncateFailed); + MacroAssembler::Jump failed = m_jit.branchTruncateDoubleToInt32( + fpr, gpr, MacroAssembler::BranchIfTruncateFailed); - addSlowPathGenerator(slowPathCall(failed, this, toInt32, gpr, fpr)); + addSlowPathGenerator(slowPathCall(failed, this, toInt32, gpr, fpr, NeedToSpill, ExceptionCheckRequirement::CheckNotNeeded)); fixed.link(&m_jit); value.adopt(result); @@ -2680,11 +2629,13 @@ void SpeculativeJIT::compilePutByValForIntTypedArray(const TypedArrayDescriptor& ASSERT_UNUSED(valueGPR, valueGPR != property); ASSERT(valueGPR != base); ASSERT(valueGPR != storageReg); - MacroAssembler::Jump outOfBounds; - if (node->op() == PutByVal) - outOfBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, property, MacroAssembler::Address(base, descriptor.m_lengthOffset)); + MacroAssembler::Jump outOfBounds = jumpForTypedArrayOutOfBounds(node, base, property); + if (node->arrayMode().isInBounds() && outOfBounds.isSet()) { + speculationCheck(OutOfBounds, JSValueSource(), 0, outOfBounds); + outOfBounds = MacroAssembler::Jump(); + } - switch (elementSize) { + switch (elementSize(type)) { case 1: m_jit.store8(value.gpr(), MacroAssembler::BaseIndex(storageReg, property, MacroAssembler::TimesOne)); break; @@ -2697,13 +2648,15 @@ void SpeculativeJIT::compilePutByValForIntTypedArray(const TypedArrayDescriptor& default: CRASH(); } - if (node->op() == PutByVal) + if (outOfBounds.isSet()) outOfBounds.link(&m_jit); noResult(node); } -void SpeculativeJIT::compileGetByValOnFloatTypedArray(const TypedArrayDescriptor& descriptor, Node* node, size_t elementSize) +void SpeculativeJIT::compileGetByValOnFloatTypedArray(Node* node, TypedArrayType type) { + ASSERT(isFloat(type)); + SpeculateCellOperand base(this, node->child1()); SpeculateStrictInt32Operand property(this, node->child2()); StorageOperand storage(this, node->child3()); @@ -2716,11 +2669,8 @@ void SpeculativeJIT::compileGetByValOnFloatTypedArray(const TypedArrayDescriptor FPRTemporary result(this); FPRReg resultReg = result.fpr(); - speculationCheck( - Uncountable, JSValueRegs(), 0, - m_jit.branch32( - MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, descriptor.m_lengthOffset))); - switch (elementSize) { + emitTypedArrayBoundsCheck(node, baseReg, propertyReg); + switch (elementSize(type)) { case 4: m_jit.loadFloat(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesFour), resultReg); m_jit.convertFloatToDouble(resultReg, resultReg); @@ -2733,16 +2683,13 @@ void SpeculativeJIT::compileGetByValOnFloatTypedArray(const TypedArrayDescriptor RELEASE_ASSERT_NOT_REACHED(); } - MacroAssembler::Jump notNaN = m_jit.branchDouble(MacroAssembler::DoubleEqual, resultReg, resultReg); - static const double NaN = QNaN; - m_jit.loadDouble(&NaN, resultReg); - notNaN.link(&m_jit); - doubleResult(resultReg, node); } -void SpeculativeJIT::compilePutByValForFloatTypedArray(const TypedArrayDescriptor& descriptor, GPRReg base, GPRReg property, Node* node, size_t elementSize) +void SpeculativeJIT::compilePutByValForFloatTypedArray(GPRReg base, GPRReg property, Node* node, TypedArrayType type) { + ASSERT(isFloat(type)); + StorageOperand storage(this, m_jit.graph().varArgChild(node, 3)); GPRReg storageReg = storage.gpr(); @@ -2756,11 +2703,13 @@ void SpeculativeJIT::compilePutByValForFloatTypedArray(const TypedArrayDescripto ASSERT_UNUSED(baseUse, node->arrayMode().alreadyChecked(m_jit.graph(), node, m_state.forNode(baseUse))); - MacroAssembler::Jump outOfBounds; - if (node->op() == PutByVal) - outOfBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, property, MacroAssembler::Address(base, descriptor.m_lengthOffset)); + MacroAssembler::Jump outOfBounds = jumpForTypedArrayOutOfBounds(node, base, property); + if (node->arrayMode().isInBounds() && outOfBounds.isSet()) { + speculationCheck(OutOfBounds, JSValueSource(), 0, outOfBounds); + outOfBounds = MacroAssembler::Jump(); + } - switch (elementSize) { + switch (elementSize(type)) { case 4: { m_jit.moveDouble(valueFPR, scratchFPR); m_jit.convertDoubleToFloat(valueFPR, scratchFPR); @@ -2773,31 +2722,28 @@ void SpeculativeJIT::compilePutByValForFloatTypedArray(const TypedArrayDescripto default: RELEASE_ASSERT_NOT_REACHED(); } - if (node->op() == PutByVal) + if (outOfBounds.isSet()) outOfBounds.link(&m_jit); noResult(node); } -void SpeculativeJIT::compileInstanceOfForObject(Node*, GPRReg valueReg, GPRReg prototypeReg, GPRReg scratchReg) +void SpeculativeJIT::compileInstanceOfForObject(Node*, GPRReg valueReg, GPRReg prototypeReg, GPRReg scratchReg, GPRReg scratch2Reg) { // Check that prototype is an object. - m_jit.loadPtr(MacroAssembler::Address(prototypeReg, JSCell::structureOffset()), scratchReg); - speculationCheck(BadType, JSValueRegs(), 0, m_jit.branchIfNotObject(scratchReg)); + speculationCheck(BadType, JSValueRegs(), 0, m_jit.branchIfNotObject(prototypeReg)); // 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); + m_jit.emitLoadStructure(scratchReg, scratchReg, scratch2Reg); + m_jit.loadPtr(MacroAssembler::Address(scratchReg, Structure::prototypeOffset() + CellPayloadOffset), scratchReg); + MacroAssembler::Jump isInstance = m_jit.branchPtr(MacroAssembler::Equal, scratchReg, prototypeReg); #if USE(JSVALUE64) - m_jit.load64(MacroAssembler::Address(scratchReg, Structure::prototypeOffset()), scratchReg); - MacroAssembler::Jump isInstance = m_jit.branch64(MacroAssembler::Equal, scratchReg, prototypeReg); - m_jit.branchTest64(MacroAssembler::Zero, scratchReg, GPRInfo::tagMaskRegister).linkTo(loop, &m_jit); + m_jit.branchIfCell(JSValueRegs(scratchReg)).linkTo(loop, &m_jit); #else - m_jit.load32(MacroAssembler::Address(scratchReg, Structure::prototypeOffset() + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), scratchReg); - MacroAssembler::Jump isInstance = m_jit.branchPtr(MacroAssembler::Equal, scratchReg, prototypeReg); - m_jit.branchTest32(MacroAssembler::NonZero, scratchReg).linkTo(loop, &m_jit); + m_jit.branchTestPtr(MacroAssembler::NonZero, scratchReg).linkTo(loop, &m_jit); #endif // No match - result is false. @@ -2818,6 +2764,17 @@ void SpeculativeJIT::compileInstanceOfForObject(Node*, GPRReg valueReg, GPRReg p putResult.link(&m_jit); } +void SpeculativeJIT::compileCheckTypeInfoFlags(Node* node) +{ + SpeculateCellOperand base(this, node->child1()); + + GPRReg baseGPR = base.gpr(); + + speculationCheck(BadTypeInfoFlags, JSValueRegs(), 0, m_jit.branchTest8(MacroAssembler::Zero, MacroAssembler::Address(baseGPR, JSCell::typeInfoFlagsOffset()), MacroAssembler::TrustedImm32(node->typeInfoOperand()))); + + noResult(node); +} + void SpeculativeJIT::compileInstanceOf(Node* node) { if (node->child1().useKind() == UntypedUse) { @@ -2828,34 +2785,25 @@ void SpeculativeJIT::compileInstanceOf(Node* node) JSValueOperand value(this, node->child1()); SpeculateCellOperand prototype(this, node->child2()); GPRTemporary scratch(this); + GPRTemporary scratch2(this); GPRReg prototypeReg = prototype.gpr(); GPRReg scratchReg = scratch.gpr(); + GPRReg scratch2Reg = scratch2.gpr(); -#if USE(JSVALUE64) - GPRReg valueReg = value.gpr(); - MacroAssembler::Jump isCell = m_jit.branchTest64(MacroAssembler::Zero, valueReg, GPRInfo::tagMaskRegister); - m_jit.move(MacroAssembler::TrustedImm64(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 isCell = m_jit.branchIfCell(value.jsValueRegs()); + GPRReg valueReg = value.jsValueRegs().payloadGPR(); + moveFalseTo(scratchReg); MacroAssembler::Jump done = m_jit.jump(); isCell.link(&m_jit); - compileInstanceOfForObject(node, valueReg, prototypeReg, scratchReg); + compileInstanceOfForObject(node, valueReg, prototypeReg, scratchReg, scratch2Reg); done.link(&m_jit); -#if USE(JSVALUE64) - jsValueResult(scratchReg, node, DataFormatJSBoolean); -#else - booleanResult(scratchReg, node); -#endif + blessedBooleanResult(scratchReg, node); return; } @@ -2863,286 +2811,465 @@ void SpeculativeJIT::compileInstanceOf(Node* node) SpeculateCellOperand prototype(this, node->child2()); GPRTemporary scratch(this); + GPRTemporary scratch2(this); GPRReg valueReg = value.gpr(); GPRReg prototypeReg = prototype.gpr(); GPRReg scratchReg = scratch.gpr(); + GPRReg scratch2Reg = scratch2.gpr(); - compileInstanceOfForObject(node, valueReg, prototypeReg, scratchReg); + compileInstanceOfForObject(node, valueReg, prototypeReg, scratchReg, scratch2Reg); + + blessedBooleanResult(scratchReg, node); +} + +template<typename SnippetGenerator, J_JITOperation_EJJ snippetSlowPathFunction> +void SpeculativeJIT::emitUntypedBitOp(Node* node) +{ + Edge& leftChild = node->child1(); + Edge& rightChild = node->child2(); + + if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node())) { + JSValueOperand left(this, leftChild); + JSValueOperand right(this, rightChild); + JSValueRegs leftRegs = left.jsValueRegs(); + JSValueRegs rightRegs = right.jsValueRegs(); +#if USE(JSVALUE64) + GPRTemporary result(this); + JSValueRegs resultRegs = JSValueRegs(result.gpr()); +#else + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); + JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr()); +#endif + flushRegisters(); + callOperation(snippetSlowPathFunction, resultRegs, leftRegs, rightRegs); + m_jit.exceptionCheck(); + + jsValueResult(resultRegs, node); + return; + } + + Optional<JSValueOperand> left; + Optional<JSValueOperand> right; + + JSValueRegs leftRegs; + JSValueRegs rightRegs; #if USE(JSVALUE64) - jsValueResult(scratchReg, node, DataFormatJSBoolean); + GPRTemporary result(this); + JSValueRegs resultRegs = JSValueRegs(result.gpr()); + GPRTemporary scratch(this); + GPRReg scratchGPR = scratch.gpr(); #else - booleanResult(scratchReg, node); + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); + JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr()); + GPRReg scratchGPR = resultTag.gpr(); #endif + + SnippetOperand leftOperand; + SnippetOperand rightOperand; + + // The snippet generator does not support both operands being constant. If the left + // operand is already const, we'll ignore the right operand's constness. + if (leftChild->isInt32Constant()) + leftOperand.setConstInt32(leftChild->asInt32()); + else if (rightChild->isInt32Constant()) + rightOperand.setConstInt32(rightChild->asInt32()); + + RELEASE_ASSERT(!leftOperand.isConst() || !rightOperand.isConst()); + + if (!leftOperand.isConst()) { + left = JSValueOperand(this, leftChild); + leftRegs = left->jsValueRegs(); + } + if (!rightOperand.isConst()) { + right = JSValueOperand(this, rightChild); + rightRegs = right->jsValueRegs(); + } + + SnippetGenerator gen(leftOperand, rightOperand, resultRegs, leftRegs, rightRegs, scratchGPR); + gen.generateFastPath(m_jit); + + ASSERT(gen.didEmitFastPath()); + gen.endJumpList().append(m_jit.jump()); + + gen.slowPathJumpList().link(&m_jit); + silentSpillAllRegisters(resultRegs); + + if (leftOperand.isConst()) { + leftRegs = resultRegs; + m_jit.moveValue(leftChild->asJSValue(), leftRegs); + } else if (rightOperand.isConst()) { + rightRegs = resultRegs; + m_jit.moveValue(rightChild->asJSValue(), rightRegs); + } + + callOperation(snippetSlowPathFunction, resultRegs, leftRegs, rightRegs); + + silentFillAllRegisters(resultRegs); + m_jit.exceptionCheck(); + + gen.endJumpList().link(&m_jit); + jsValueResult(resultRegs, node); } -void SpeculativeJIT::compileSoftModulo(Node* node) +void SpeculativeJIT::compileBitwiseOp(Node* node) { - // 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()); -#if CPU(X86) || CPU(X86_64) - if (isInt32Constant(node->child2().node())) { - int32_t divisor = valueOfInt32Constant(node->child2().node()); - if (divisor) { - GPRReg op1Gpr = op1.gpr(); + NodeType op = node->op(); + Edge& leftChild = node->child1(); + Edge& rightChild = node->child2(); - 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(), 0, m_jit.branch32(JITCompiler::Equal, eax.gpr(), TrustedImm32(-2147483647-1))); - m_jit.assembler().cdq(); - m_jit.assembler().idivl_r(scratchGPR); - if (!nodeCanIgnoreNegativeZero(node->arithNodeFlags())) { - // Check that we're not about to create negative zero. - JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, op1SaveGPR, TrustedImm32(0)); - speculationCheck(NegativeZero, JSValueRegs(), 0, m_jit.branchTest32(JITCompiler::Zero, edx.gpr())); - numeratorPositive.link(&m_jit); - } - if (op1SaveGPR != op1Gpr) - unlock(op1SaveGPR); - - integerResult(edx.gpr(), node); + if (leftChild.useKind() == UntypedUse || rightChild.useKind() == UntypedUse) { + switch (op) { + case BitAnd: + emitUntypedBitOp<JITBitAndGenerator, operationValueBitAnd>(node); + return; + case BitOr: + emitUntypedBitOp<JITBitOrGenerator, operationValueBitOr>(node); return; + case BitXor: + emitUntypedBitOp<JITBitXorGenerator, operationValueBitXor>(node); + return; + default: + RELEASE_ASSERT_NOT_REACHED(); } } -#elif CPU(APPLE_ARMV7S) || CPU(ARM_THUMB2) - if (isInt32Constant(node->child2().node())) { - int32_t divisor = valueOfInt32Constant(node->child2().node()); - if (divisor > 0 && hasOneBitSet(divisor)) { // If power of 2 then just mask - GPRReg dividendGPR = op1.gpr(); - GPRTemporary result(this); - GPRReg resultGPR = result.gpr(); - m_jit.assembler().cmp(dividendGPR, ARMThumbImmediate::makeEncodedImm(0)); - m_jit.assembler().it(ARMv7Assembler::ConditionLT, false); - m_jit.assembler().neg(resultGPR, dividendGPR); - m_jit.assembler().mov(resultGPR, dividendGPR); - m_jit.and32(TrustedImm32(divisor - 1), resultGPR); - m_jit.assembler().it(ARMv7Assembler::ConditionLT); - m_jit.assembler().neg(resultGPR, resultGPR); - - if (!nodeCanIgnoreNegativeZero(node->arithNodeFlags())) { - // Check that we're not about to create negative zero. - JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, dividendGPR, TrustedImm32(0)); - speculationCheck(NegativeZero, JSValueRegs(), 0, m_jit.branchTest32(JITCompiler::Zero, resultGPR)); - numeratorPositive.link(&m_jit); - } - integerResult(resultGPR, node); - return; - } + if (leftChild->isInt32Constant()) { + SpeculateInt32Operand op2(this, rightChild); + GPRTemporary result(this, Reuse, op2); + + bitOp(op, leftChild->asInt32(), op2.gpr(), result.gpr()); + + int32Result(result.gpr(), node); + + } else if (rightChild->isInt32Constant()) { + SpeculateInt32Operand op1(this, leftChild); + GPRTemporary result(this, Reuse, op1); + + bitOp(op, rightChild->asInt32(), op1.gpr(), result.gpr()); + + int32Result(result.gpr(), node); + + } else { + SpeculateInt32Operand op1(this, leftChild); + SpeculateInt32Operand op2(this, rightChild); + GPRTemporary result(this, Reuse, op1, op2); + + GPRReg reg1 = op1.gpr(); + GPRReg reg2 = op2.gpr(); + bitOp(op, reg1, reg2, result.gpr()); + + int32Result(result.gpr(), node); } +} + +void SpeculativeJIT::emitUntypedRightShiftBitOp(Node* node) +{ + J_JITOperation_EJJ snippetSlowPathFunction = node->op() == BitRShift + ? operationValueBitRShift : operationValueBitURShift; + JITRightShiftGenerator::ShiftType shiftType = node->op() == BitRShift + ? JITRightShiftGenerator::SignedShift : JITRightShiftGenerator::UnsignedShift; + + Edge& leftChild = node->child1(); + Edge& rightChild = node->child2(); + + if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node())) { + JSValueOperand left(this, leftChild); + JSValueOperand right(this, rightChild); + JSValueRegs leftRegs = left.jsValueRegs(); + JSValueRegs rightRegs = right.jsValueRegs(); +#if USE(JSVALUE64) + GPRTemporary result(this); + JSValueRegs resultRegs = JSValueRegs(result.gpr()); +#else + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); + JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr()); #endif + flushRegisters(); + callOperation(snippetSlowPathFunction, resultRegs, leftRegs, rightRegs); + m_jit.exceptionCheck(); - SpeculateIntegerOperand op2(this, node->child2()); -#if CPU(X86) || CPU(X86_64) - GPRTemporary eax(this, X86Registers::eax); - GPRTemporary edx(this, X86Registers::edx); - 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; + jsValueResult(resultRegs, node); + return; } - - 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(), 0, m_jit.branchTest32(JITCompiler::Zero, op2GPR)); - speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branch32(JITCompiler::Equal, op1GPR, TrustedImm32(-2147483647-1))); - - safeDenominator.link(&m_jit); - - if (op2TempGPR != InvalidGPRReg) { - m_jit.move(op2GPR, op2TempGPR); - op2GPR = op2TempGPR; + + Optional<JSValueOperand> left; + Optional<JSValueOperand> right; + + JSValueRegs leftRegs; + JSValueRegs rightRegs; + + FPRTemporary leftNumber(this); + FPRReg leftFPR = leftNumber.fpr(); + +#if USE(JSVALUE64) + GPRTemporary result(this); + JSValueRegs resultRegs = JSValueRegs(result.gpr()); + GPRTemporary scratch(this); + GPRReg scratchGPR = scratch.gpr(); + FPRReg scratchFPR = InvalidFPRReg; +#else + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); + JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr()); + GPRReg scratchGPR = resultTag.gpr(); + FPRTemporary fprScratch(this); + FPRReg scratchFPR = fprScratch.fpr(); +#endif + + SnippetOperand leftOperand; + SnippetOperand rightOperand; + + // The snippet generator does not support both operands being constant. If the left + // operand is already const, we'll ignore the right operand's constness. + if (leftChild->isInt32Constant()) + leftOperand.setConstInt32(leftChild->asInt32()); + else if (rightChild->isInt32Constant()) + rightOperand.setConstInt32(rightChild->asInt32()); + + RELEASE_ASSERT(!leftOperand.isConst() || !rightOperand.isConst()); + + if (!leftOperand.isConst()) { + left = JSValueOperand(this, leftChild); + leftRegs = left->jsValueRegs(); + } + if (!rightOperand.isConst()) { + right = JSValueOperand(this, rightChild); + rightRegs = right->jsValueRegs(); } - - m_jit.move(op1GPR, eax.gpr()); - m_jit.assembler().cdq(); - m_jit.assembler().idivl_r(op2GPR); - - if (op2TempGPR != InvalidGPRReg) - unlock(op2TempGPR); - if (!nodeCanIgnoreNegativeZero(node->arithNodeFlags())) { - // Check that we're not about to create negative zero. - JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, op1SaveGPR, TrustedImm32(0)); - speculationCheck(NegativeZero, JSValueRegs(), 0, m_jit.branchTest32(JITCompiler::Zero, edx.gpr())); - numeratorPositive.link(&m_jit); + JITRightShiftGenerator gen(leftOperand, rightOperand, resultRegs, leftRegs, rightRegs, + leftFPR, scratchGPR, scratchFPR, shiftType); + gen.generateFastPath(m_jit); + + ASSERT(gen.didEmitFastPath()); + gen.endJumpList().append(m_jit.jump()); + + gen.slowPathJumpList().link(&m_jit); + silentSpillAllRegisters(resultRegs); + + if (leftOperand.isConst()) { + leftRegs = resultRegs; + m_jit.moveValue(leftChild->asJSValue(), leftRegs); + } else if (rightOperand.isConst()) { + rightRegs = resultRegs; + m_jit.moveValue(rightChild->asJSValue(), rightRegs); } - - if (op1SaveGPR != op1GPR) - unlock(op1SaveGPR); - - integerResult(edx.gpr(), node); -#elif CPU(APPLE_ARMV7S) - GPRTemporary temp(this); - GPRTemporary quotientThenRemainder(this); - GPRTemporary multiplyAnswer(this); - GPRReg dividendGPR = op1.gpr(); - GPRReg divisorGPR = op2.gpr(); - GPRReg quotientThenRemainderGPR = quotientThenRemainder.gpr(); - GPRReg multiplyAnswerGPR = multiplyAnswer.gpr(); - - m_jit.assembler().sdiv(quotientThenRemainderGPR, dividendGPR, divisorGPR); - speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchMul32(JITCompiler::Overflow, quotientThenRemainderGPR, divisorGPR, multiplyAnswerGPR)); - m_jit.assembler().sub(quotientThenRemainderGPR, dividendGPR, multiplyAnswerGPR); - - // If the user cares about negative zero, then speculate that we're not about - // to produce negative zero. - if (!nodeCanIgnoreNegativeZero(node->arithNodeFlags())) { - // Check that we're not about to create negative zero. - JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, dividendGPR, TrustedImm32(0)); - speculationCheck(NegativeZero, JSValueRegs(), 0, m_jit.branchTest32(JITCompiler::Zero, quotientThenRemainderGPR)); - numeratorPositive.link(&m_jit); + callOperation(snippetSlowPathFunction, resultRegs, leftRegs, rightRegs); + + silentFillAllRegisters(resultRegs); + m_jit.exceptionCheck(); + + gen.endJumpList().link(&m_jit); + jsValueResult(resultRegs, node); + return; +} + +void SpeculativeJIT::compileShiftOp(Node* node) +{ + NodeType op = node->op(); + Edge& leftChild = node->child1(); + Edge& rightChild = node->child2(); + + if (leftChild.useKind() == UntypedUse || rightChild.useKind() == UntypedUse) { + switch (op) { + case BitLShift: + emitUntypedBitOp<JITLeftShiftGenerator, operationValueBitLShift>(node); + return; + case BitRShift: + case BitURShift: + emitUntypedRightShiftBitOp(node); + return; + default: + RELEASE_ASSERT_NOT_REACHED(); + } } - integerResult(quotientThenRemainderGPR, node); -#elif CPU(MIPS) - GPRTemporary remainder(this); - GPRReg dividendGPR = op1.gpr(); - GPRReg divisorGPR = op2.gpr(); - GPRReg remainderGPR = remainder.gpr(); + if (rightChild->isInt32Constant()) { + SpeculateInt32Operand op1(this, leftChild); + GPRTemporary result(this, Reuse, op1); - speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branch32(JITCompiler::Equal, dividendGPR, TrustedImm32(-2147483647-1))); - m_jit.assembler().div(dividendGPR, divisorGPR); - m_jit.assembler().mfhi(remainderGPR); + shiftOp(op, op1.gpr(), rightChild->asInt32() & 0x1f, result.gpr()); - if (!nodeCanIgnoreNegativeZero(node->arithNodeFlags())) { - // Check that we're not about to create negative zero. - JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, dividendGPR, TrustedImm32(0)); - speculationCheck(NegativeZero, JSValueRegs(), 0, m_jit.branchTest32(JITCompiler::Zero, remainderGPR)); - numeratorPositive.link(&m_jit); + int32Result(result.gpr(), node); + } else { + // Do not allow shift amount to be used as the result, MacroAssembler does not permit this. + SpeculateInt32Operand op1(this, leftChild); + SpeculateInt32Operand op2(this, rightChild); + GPRTemporary result(this, Reuse, op1); + + GPRReg reg1 = op1.gpr(); + GPRReg reg2 = op2.gpr(); + shiftOp(op, reg1, reg2, result.gpr()); + + int32Result(result.gpr(), node); } +} - integerResult(remainderGPR, node); -#else // not architecture that can do integer division - // 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(); - if (!nodeCanIgnoreNegativeZero(node->arithNodeFlags())) { - // NegativeZero check will need op1GPR and fmod call is likely to clobber it. - m_jit.push(op1GPR); - callOperation(operationFModOnInts, result.fpr(), op1GPR, op2GPR); - m_jit.pop(op1GPR); - } else - callOperation(operationFModOnInts, result.fpr(), op1GPR, op2GPR); +void SpeculativeJIT::compileValueAdd(Node* node) +{ + Edge& leftChild = node->child1(); + Edge& rightChild = node->child2(); + + if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node())) { + JSValueOperand left(this, leftChild); + JSValueOperand right(this, rightChild); + JSValueRegs leftRegs = left.jsValueRegs(); + JSValueRegs rightRegs = right.jsValueRegs(); +#if USE(JSVALUE64) + GPRTemporary result(this); + JSValueRegs resultRegs = JSValueRegs(result.gpr()); +#else + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); + JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr()); +#endif + flushRegisters(); + callOperation(operationValueAddNotNumber, resultRegs, leftRegs, rightRegs); + m_jit.exceptionCheck(); - FPRTemporary scratch(this); - GPRTemporary intResult(this); - JITCompiler::JumpList failureCases; - m_jit.branchConvertDoubleToInt32(result.fpr(), intResult.gpr(), failureCases, scratch.fpr(), false); - speculationCheck(Overflow, JSValueRegs(), 0, failureCases); - if (!nodeCanIgnoreNegativeZero(node->arithNodeFlags())) { - // Check that we're not about to create negative zero. - JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, op1GPR, TrustedImm32(0)); - speculationCheck(NegativeZero, JSValueRegs(), 0, m_jit.branchTest32(JITCompiler::Zero, intResult.gpr())); - numeratorPositive.link(&m_jit); + jsValueResult(resultRegs, node); + return; } - - integerResult(intResult.gpr(), node); -#endif // CPU(X86) || CPU(X86_64) + + Optional<JSValueOperand> left; + Optional<JSValueOperand> right; + + JSValueRegs leftRegs; + JSValueRegs rightRegs; + + FPRTemporary leftNumber(this); + FPRTemporary rightNumber(this); + FPRReg leftFPR = leftNumber.fpr(); + FPRReg rightFPR = rightNumber.fpr(); + +#if USE(JSVALUE64) + GPRTemporary result(this); + JSValueRegs resultRegs = JSValueRegs(result.gpr()); + GPRTemporary scratch(this); + GPRReg scratchGPR = scratch.gpr(); + FPRReg scratchFPR = InvalidFPRReg; +#else + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); + JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr()); + GPRReg scratchGPR = resultTag.gpr(); + FPRTemporary fprScratch(this); + FPRReg scratchFPR = fprScratch.fpr(); +#endif + + SnippetOperand leftOperand(m_state.forNode(leftChild).resultType()); + SnippetOperand rightOperand(m_state.forNode(rightChild).resultType()); + + // The snippet generator does not support both operands being constant. If the left + // operand is already const, we'll ignore the right operand's constness. + if (leftChild->isInt32Constant()) + leftOperand.setConstInt32(leftChild->asInt32()); + else if (rightChild->isInt32Constant()) + rightOperand.setConstInt32(rightChild->asInt32()); + + ASSERT(!leftOperand.isConst() || !rightOperand.isConst()); + + if (!leftOperand.isConst()) { + left = JSValueOperand(this, leftChild); + leftRegs = left->jsValueRegs(); + } + if (!rightOperand.isConst()) { + right = JSValueOperand(this, rightChild); + rightRegs = right->jsValueRegs(); + } + + JITAddGenerator gen(leftOperand, rightOperand, resultRegs, leftRegs, rightRegs, + leftFPR, rightFPR, scratchGPR, scratchFPR); + gen.generateFastPath(m_jit); + + ASSERT(gen.didEmitFastPath()); + gen.endJumpList().append(m_jit.jump()); + + gen.slowPathJumpList().link(&m_jit); + + silentSpillAllRegisters(resultRegs); + + if (leftOperand.isConst()) { + leftRegs = resultRegs; + m_jit.moveValue(leftChild->asJSValue(), leftRegs); + } else if (rightOperand.isConst()) { + rightRegs = resultRegs; + m_jit.moveValue(rightChild->asJSValue(), rightRegs); + } + + callOperation(operationValueAdd, resultRegs, leftRegs, rightRegs); + + silentFillAllRegisters(resultRegs); + m_jit.exceptionCheck(); + + gen.endJumpList().link(&m_jit); + jsValueResult(resultRegs, node); + return; } -void SpeculativeJIT::compileAdd(Node* node) +void SpeculativeJIT::compileInstanceOfCustom(Node* node) +{ + // We could do something smarter here but this case is currently super rare and unless + // Symbol.hasInstance becomes popular will likely remain that way. + + JSValueOperand value(this, node->child1()); + SpeculateCellOperand constructor(this, node->child2()); + JSValueOperand hasInstanceValue(this, node->child3()); + GPRTemporary result(this); + + JSValueRegs valueRegs = value.jsValueRegs(); + GPRReg constructorGPR = constructor.gpr(); + JSValueRegs hasInstanceRegs = hasInstanceValue.jsValueRegs(); + GPRReg resultGPR = result.gpr(); + + MacroAssembler::Jump slowCase = m_jit.jump(); + + addSlowPathGenerator(slowPathCall(slowCase, this, operationInstanceOfCustom, resultGPR, valueRegs, constructorGPR, hasInstanceRegs)); + + unblessedBooleanResult(resultGPR, node); +} + +void SpeculativeJIT::compileArithAdd(Node* node) { switch (node->binaryUseKind()) { case Int32Use: { - if (isNumberConstant(node->child1().node())) { - int32_t imm1 = valueOfInt32Constant(node->child1().node()); - SpeculateIntegerOperand op2(this, node->child2()); - GPRTemporary result(this); + ASSERT(!shouldCheckNegativeZero(node->arithMode())); - if (nodeCanTruncateInteger(node->arithNodeFlags())) { - m_jit.move(op2.gpr(), result.gpr()); - m_jit.add32(Imm32(imm1), result.gpr()); - } else - speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchAdd32(MacroAssembler::Overflow, op2.gpr(), Imm32(imm1), result.gpr())); + if (node->child2()->isInt32Constant()) { + SpeculateInt32Operand op1(this, node->child1()); + int32_t imm2 = node->child2()->asInt32(); + + if (!shouldCheckOverflow(node->arithMode())) { + GPRTemporary result(this, Reuse, op1); + m_jit.add32(Imm32(imm2), op1.gpr(), result.gpr()); + int32Result(result.gpr(), node); + return; + } - integerResult(result.gpr(), node); - return; - } - - if (isNumberConstant(node->child2().node())) { - SpeculateIntegerOperand op1(this, node->child1()); - int32_t imm2 = valueOfInt32Constant(node->child2().node()); GPRTemporary result(this); - - if (nodeCanTruncateInteger(node->arithNodeFlags())) { - m_jit.move(op1.gpr(), result.gpr()); - m_jit.add32(Imm32(imm2), result.gpr()); - } else - speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchAdd32(MacroAssembler::Overflow, op1.gpr(), Imm32(imm2), result.gpr())); + speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchAdd32(MacroAssembler::Overflow, op1.gpr(), Imm32(imm2), result.gpr())); - integerResult(result.gpr(), node); + int32Result(result.gpr(), node); return; } - SpeculateIntegerOperand op1(this, node->child1()); - SpeculateIntegerOperand op2(this, node->child2()); - GPRTemporary result(this, op1, op2); + SpeculateInt32Operand op1(this, node->child1()); + SpeculateInt32Operand op2(this, node->child2()); + GPRTemporary result(this, Reuse, op1, op2); GPRReg gpr1 = op1.gpr(); GPRReg gpr2 = op2.gpr(); GPRReg gprResult = result.gpr(); - if (nodeCanTruncateInteger(node->arithNodeFlags())) { - if (gpr1 == gprResult) - m_jit.add32(gpr2, gprResult); - else { - m_jit.move(gpr2, gprResult); - m_jit.add32(gpr1, gprResult); - } - } else { + if (!shouldCheckOverflow(node->arithMode())) + m_jit.add32(gpr1, gpr2, gprResult); + else { MacroAssembler::Jump check = m_jit.branchAdd32(MacroAssembler::Overflow, gpr1, gpr2, gprResult); if (gpr1 == gprResult) @@ -3153,11 +3280,40 @@ void SpeculativeJIT::compileAdd(Node* node) speculationCheck(Overflow, JSValueRegs(), 0, check); } - integerResult(gprResult, node); + int32Result(gprResult, node); + return; + } + +#if USE(JSVALUE64) + case Int52RepUse: { + ASSERT(shouldCheckOverflow(node->arithMode())); + ASSERT(!shouldCheckNegativeZero(node->arithMode())); + + // Will we need an overflow check? If we can prove that neither input can be + // Int52 then the overflow check will not be necessary. + if (!m_state.forNode(node->child1()).couldBeType(SpecInt52) + && !m_state.forNode(node->child2()).couldBeType(SpecInt52)) { + SpeculateWhicheverInt52Operand op1(this, node->child1()); + SpeculateWhicheverInt52Operand op2(this, node->child2(), op1); + GPRTemporary result(this, Reuse, op1); + m_jit.add64(op1.gpr(), op2.gpr(), result.gpr()); + int52Result(result.gpr(), node, op1.format()); + return; + } + + SpeculateInt52Operand op1(this, node->child1()); + SpeculateInt52Operand op2(this, node->child2()); + GPRTemporary result(this); + m_jit.move(op1.gpr(), result.gpr()); + speculationCheck( + Int52Overflow, JSValueRegs(), 0, + m_jit.branchAdd64(MacroAssembler::Overflow, op2.gpr(), result.gpr())); + int52Result(result.gpr(), node); return; } +#endif // USE(JSVALUE64) - case NumberUse: { + case DoubleRepUse: { SpeculateDoubleOperand op1(this, node->child1()); SpeculateDoubleOperand op2(this, node->child2()); FPRTemporary result(this, op1, op2); @@ -3170,12 +3326,6 @@ void SpeculativeJIT::compileAdd(Node* node) return; } - case UntypedUse: { - RELEASE_ASSERT(node->op() == ValueAdd); - compileValueAdd(node); - return; - } - default: RELEASE_ASSERT_NOT_REACHED(); break; @@ -3211,7 +3361,7 @@ void SpeculativeJIT::compileMakeRope(Node* node) GPRReg scratchGPR = scratch.gpr(); JITCompiler::JumpList slowPath; - MarkedAllocator& markedAllocator = m_jit.vm()->heap.allocatorForObjectWithImmortalStructureDestructor(sizeof(JSRopeString)); + MarkedAllocator& markedAllocator = m_jit.vm()->heap.allocatorForObjectWithDestructor(sizeof(JSRopeString)); m_jit.move(TrustedImmPtr(&markedAllocator), allocatorGPR); emitAllocateJSCell(resultGPR, allocatorGPR, TrustedImmPtr(m_jit.vm()->stringStructure.get()), scratchGPR, slowPath); @@ -3225,13 +3375,13 @@ void SpeculativeJIT::compileMakeRope(Node* node) if (!ASSERT_DISABLED) { JITCompiler::Jump ok = m_jit.branch32( JITCompiler::GreaterThanOrEqual, allocatorGPR, TrustedImm32(0)); - m_jit.breakpoint(); + m_jit.abortWithReason(DFGNegativeStringLength); ok.link(&m_jit); } for (unsigned i = 1; i < numOpGPRs; ++i) { m_jit.and32(JITCompiler::Address(opGPRs[i], JSString::offsetOfFlags()), scratchGPR); speculationCheck( - Uncountable, JSValueSource(), 0, + Uncountable, JSValueSource(), nullptr, m_jit.branchAdd32( JITCompiler::Overflow, JITCompiler::Address(opGPRs[i], JSString::offsetOfLength()), allocatorGPR)); @@ -3241,7 +3391,7 @@ void SpeculativeJIT::compileMakeRope(Node* node) if (!ASSERT_DISABLED) { JITCompiler::Jump ok = m_jit.branch32( JITCompiler::GreaterThanOrEqual, allocatorGPR, TrustedImm32(0)); - m_jit.breakpoint(); + m_jit.abortWithReason(DFGNegativeStringLength); ok.link(&m_jit); } m_jit.store32(allocatorGPR, JITCompiler::Address(resultGPR, JSString::offsetOfLength())); @@ -3263,61 +3413,100 @@ void SpeculativeJIT::compileMakeRope(Node* node) cellResult(resultGPR, node); } +void SpeculativeJIT::compileArithClz32(Node* node) +{ + ASSERT_WITH_MESSAGE(node->child1().useKind() == Int32Use || node->child1().useKind() == KnownInt32Use, "The Fixup phase should have enforced a Int32 operand."); + SpeculateInt32Operand value(this, node->child1()); + GPRTemporary result(this, Reuse, value); + GPRReg valueReg = value.gpr(); + GPRReg resultReg = result.gpr(); + m_jit.countLeadingZeros32(valueReg, resultReg); + int32Result(resultReg, node); +} + void SpeculativeJIT::compileArithSub(Node* node) { switch (node->binaryUseKind()) { case Int32Use: { - if (isNumberConstant(node->child2().node())) { - SpeculateIntegerOperand op1(this, node->child1()); - int32_t imm2 = valueOfInt32Constant(node->child2().node()); + ASSERT(!shouldCheckNegativeZero(node->arithMode())); + + if (node->child2()->isInt32Constant()) { + SpeculateInt32Operand op1(this, node->child1()); + int32_t imm2 = node->child2()->asInt32(); GPRTemporary result(this); - if (nodeCanTruncateInteger(node->arithNodeFlags())) { + if (!shouldCheckOverflow(node->arithMode())) { m_jit.move(op1.gpr(), result.gpr()); m_jit.sub32(Imm32(imm2), result.gpr()); } else { -#if ENABLE(JIT_CONSTANT_BLINDING) GPRTemporary scratch(this); speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchSub32(MacroAssembler::Overflow, op1.gpr(), Imm32(imm2), result.gpr(), scratch.gpr())); -#else - speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchSub32(MacroAssembler::Overflow, op1.gpr(), Imm32(imm2), result.gpr())); -#endif } - integerResult(result.gpr(), node); + int32Result(result.gpr(), node); return; } - if (isNumberConstant(node->child1().node())) { - int32_t imm1 = valueOfInt32Constant(node->child1().node()); - SpeculateIntegerOperand op2(this, node->child2()); + if (node->child1()->isInt32Constant()) { + int32_t imm1 = node->child1()->asInt32(); + SpeculateInt32Operand op2(this, node->child2()); GPRTemporary result(this); m_jit.move(Imm32(imm1), result.gpr()); - if (nodeCanTruncateInteger(node->arithNodeFlags())) + if (!shouldCheckOverflow(node->arithMode())) m_jit.sub32(op2.gpr(), result.gpr()); else speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchSub32(MacroAssembler::Overflow, op2.gpr(), result.gpr())); - integerResult(result.gpr(), node); + int32Result(result.gpr(), node); return; } - SpeculateIntegerOperand op1(this, node->child1()); - SpeculateIntegerOperand op2(this, node->child2()); + SpeculateInt32Operand op1(this, node->child1()); + SpeculateInt32Operand op2(this, node->child2()); GPRTemporary result(this); - if (nodeCanTruncateInteger(node->arithNodeFlags())) { + if (!shouldCheckOverflow(node->arithMode())) { m_jit.move(op1.gpr(), result.gpr()); m_jit.sub32(op2.gpr(), result.gpr()); } else speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchSub32(MacroAssembler::Overflow, op1.gpr(), op2.gpr(), result.gpr())); - integerResult(result.gpr(), node); + int32Result(result.gpr(), node); return; } - case NumberUse: { +#if USE(JSVALUE64) + case Int52RepUse: { + ASSERT(shouldCheckOverflow(node->arithMode())); + ASSERT(!shouldCheckNegativeZero(node->arithMode())); + + // Will we need an overflow check? If we can prove that neither input can be + // Int52 then the overflow check will not be necessary. + if (!m_state.forNode(node->child1()).couldBeType(SpecInt52) + && !m_state.forNode(node->child2()).couldBeType(SpecInt52)) { + SpeculateWhicheverInt52Operand op1(this, node->child1()); + SpeculateWhicheverInt52Operand op2(this, node->child2(), op1); + GPRTemporary result(this, Reuse, op1); + m_jit.move(op1.gpr(), result.gpr()); + m_jit.sub64(op2.gpr(), result.gpr()); + int52Result(result.gpr(), node, op1.format()); + return; + } + + SpeculateInt52Operand op1(this, node->child1()); + SpeculateInt52Operand op2(this, node->child2()); + GPRTemporary result(this); + m_jit.move(op1.gpr(), result.gpr()); + speculationCheck( + Int52Overflow, JSValueRegs(), 0, + m_jit.branchSub64(MacroAssembler::Overflow, op2.gpr(), result.gpr())); + int52Result(result.gpr(), node); + return; + } +#endif // USE(JSVALUE64) + + case DoubleRepUse: { SpeculateDoubleOperand op1(this, node->child1()); SpeculateDoubleOperand op2(this, node->child2()); FPRTemporary result(this, op1); @@ -3329,7 +3518,58 @@ void SpeculativeJIT::compileArithSub(Node* node) doubleResult(result.fpr(), node); return; } - + + case UntypedUse: { + Edge& leftChild = node->child1(); + Edge& rightChild = node->child2(); + + JSValueOperand left(this, leftChild); + JSValueOperand right(this, rightChild); + + JSValueRegs leftRegs = left.jsValueRegs(); + JSValueRegs rightRegs = right.jsValueRegs(); + + FPRTemporary leftNumber(this); + FPRTemporary rightNumber(this); + FPRReg leftFPR = leftNumber.fpr(); + FPRReg rightFPR = rightNumber.fpr(); + +#if USE(JSVALUE64) + GPRTemporary result(this); + JSValueRegs resultRegs = JSValueRegs(result.gpr()); + GPRTemporary scratch(this); + GPRReg scratchGPR = scratch.gpr(); + FPRReg scratchFPR = InvalidFPRReg; +#else + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); + JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr()); + GPRReg scratchGPR = resultTag.gpr(); + FPRTemporary fprScratch(this); + FPRReg scratchFPR = fprScratch.fpr(); +#endif + + SnippetOperand leftOperand(m_state.forNode(leftChild).resultType()); + SnippetOperand rightOperand(m_state.forNode(rightChild).resultType()); + + JITSubGenerator gen(leftOperand, rightOperand, resultRegs, leftRegs, rightRegs, + leftFPR, rightFPR, scratchGPR, scratchFPR); + gen.generateFastPath(m_jit); + + ASSERT(gen.didEmitFastPath()); + gen.endJumpList().append(m_jit.jump()); + + gen.slowPathJumpList().link(&m_jit); + silentSpillAllRegisters(resultRegs); + callOperation(operationValueSub, resultRegs, leftRegs, rightRegs); + silentFillAllRegisters(resultRegs); + m_jit.exceptionCheck(); + + gen.endJumpList().link(&m_jit); + jsValueResult(resultRegs, node); + return; + } + default: RELEASE_ASSERT_NOT_REACHED(); return; @@ -3340,24 +3580,66 @@ void SpeculativeJIT::compileArithNegate(Node* node) { switch (node->child1().useKind()) { case Int32Use: { - SpeculateIntegerOperand op1(this, node->child1()); + SpeculateInt32Operand op1(this, node->child1()); GPRTemporary result(this); m_jit.move(op1.gpr(), result.gpr()); - if (nodeCanTruncateInteger(node->arithNodeFlags())) + // Note: there is no notion of being not used as a number, but someone + // caring about negative zero. + + if (!shouldCheckOverflow(node->arithMode())) m_jit.neg32(result.gpr()); - else { + else if (!shouldCheckNegativeZero(node->arithMode())) speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchNeg32(MacroAssembler::Overflow, result.gpr())); - if (!nodeCanIgnoreNegativeZero(node->arithNodeFlags())) - speculationCheck(NegativeZero, JSValueRegs(), 0, m_jit.branchTest32(MacroAssembler::Zero, result.gpr())); + else { + speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchTest32(MacroAssembler::Zero, result.gpr(), TrustedImm32(0x7fffffff))); + m_jit.neg32(result.gpr()); } - integerResult(result.gpr(), node); + int32Result(result.gpr(), node); return; } + +#if USE(JSVALUE64) + case Int52RepUse: { + ASSERT(shouldCheckOverflow(node->arithMode())); - case NumberUse: { + if (!m_state.forNode(node->child1()).couldBeType(SpecInt52)) { + SpeculateWhicheverInt52Operand op1(this, node->child1()); + GPRTemporary result(this); + GPRReg op1GPR = op1.gpr(); + GPRReg resultGPR = result.gpr(); + m_jit.move(op1GPR, resultGPR); + m_jit.neg64(resultGPR); + if (shouldCheckNegativeZero(node->arithMode())) { + speculationCheck( + NegativeZero, JSValueRegs(), 0, + m_jit.branchTest64(MacroAssembler::Zero, resultGPR)); + } + int52Result(resultGPR, node, op1.format()); + return; + } + + SpeculateInt52Operand op1(this, node->child1()); + GPRTemporary result(this); + GPRReg op1GPR = op1.gpr(); + GPRReg resultGPR = result.gpr(); + m_jit.move(op1GPR, resultGPR); + speculationCheck( + Int52Overflow, JSValueRegs(), 0, + m_jit.branchNeg64(MacroAssembler::Overflow, resultGPR)); + if (shouldCheckNegativeZero(node->arithMode())) { + speculationCheck( + NegativeZero, JSValueRegs(), 0, + m_jit.branchTest64(MacroAssembler::Zero, resultGPR)); + } + int52Result(resultGPR, node); + return; + } +#endif // USE(JSVALUE64) + + case DoubleRepUse: { SpeculateDoubleOperand op1(this, node->child1()); FPRTemporary result(this); @@ -3372,27 +3654,44 @@ void SpeculativeJIT::compileArithNegate(Node* node) return; } } -void SpeculativeJIT::compileArithIMul(Node* node) -{ - SpeculateIntegerOperand op1(this, node->child1()); - SpeculateIntegerOperand op2(this, node->child2()); - GPRTemporary result(this); - - GPRReg reg1 = op1.gpr(); - GPRReg reg2 = op2.gpr(); - - m_jit.move(reg1, result.gpr()); - m_jit.mul32(reg2, result.gpr()); - integerResult(result.gpr(), node); - return; -} - void SpeculativeJIT::compileArithMul(Node* node) { switch (node->binaryUseKind()) { case Int32Use: { - SpeculateIntegerOperand op1(this, node->child1()); - SpeculateIntegerOperand op2(this, node->child2()); + if (node->child2()->isInt32Constant()) { + SpeculateInt32Operand op1(this, node->child1()); + GPRTemporary result(this); + + int32_t imm = node->child2()->asInt32(); + GPRReg op1GPR = op1.gpr(); + GPRReg resultGPR = result.gpr(); + + if (!shouldCheckOverflow(node->arithMode())) + m_jit.mul32(Imm32(imm), op1GPR, resultGPR); + else { + speculationCheck(Overflow, JSValueRegs(), 0, + m_jit.branchMul32(MacroAssembler::Overflow, op1GPR, Imm32(imm), resultGPR)); + } + + // The only way to create negative zero with a constant is: + // -negative-op1 * 0. + // -zero-op1 * negative constant. + if (shouldCheckNegativeZero(node->arithMode())) { + if (!imm) + speculationCheck(NegativeZero, JSValueRegs(), 0, m_jit.branchTest32(MacroAssembler::Signed, op1GPR)); + else if (imm < 0) { + if (shouldCheckOverflow(node->arithMode())) + speculationCheck(NegativeZero, JSValueRegs(), 0, m_jit.branchTest32(MacroAssembler::Zero, resultGPR)); + else + speculationCheck(NegativeZero, JSValueRegs(), 0, m_jit.branchTest32(MacroAssembler::Zero, op1GPR)); + } + } + + int32Result(resultGPR, node); + return; + } + SpeculateInt32Operand op1(this, node->child1()); + SpeculateInt32Operand op2(this, node->child2()); GPRTemporary result(this); GPRReg reg1 = op1.gpr(); @@ -3401,7 +3700,7 @@ void SpeculativeJIT::compileArithMul(Node* node) // We can perform truncated multiplications if we get to this point, because if the // fixup phase could not prove that it would be safe, it would have turned us into // a double multiplication. - if (nodeCanTruncateInteger(node->arithNodeFlags())) { + if (!shouldCheckOverflow(node->arithMode())) { m_jit.move(reg1, result.gpr()); m_jit.mul32(reg2, result.gpr()); } else { @@ -3411,18 +3710,76 @@ void SpeculativeJIT::compileArithMul(Node* node) } // Check for negative zero, if the users of this node care about such things. - if (!nodeCanIgnoreNegativeZero(node->arithNodeFlags())) { + if (shouldCheckNegativeZero(node->arithMode())) { MacroAssembler::Jump resultNonZero = m_jit.branchTest32(MacroAssembler::NonZero, result.gpr()); - speculationCheck(NegativeZero, JSValueRegs(), 0, m_jit.branch32(MacroAssembler::LessThan, reg1, TrustedImm32(0))); - speculationCheck(NegativeZero, JSValueRegs(), 0, m_jit.branch32(MacroAssembler::LessThan, reg2, TrustedImm32(0))); + speculationCheck(NegativeZero, JSValueRegs(), 0, m_jit.branchTest32(MacroAssembler::Signed, reg1)); + speculationCheck(NegativeZero, JSValueRegs(), 0, m_jit.branchTest32(MacroAssembler::Signed, reg2)); resultNonZero.link(&m_jit); } - integerResult(result.gpr(), node); + int32Result(result.gpr(), node); return; } + +#if USE(JSVALUE64) + case Int52RepUse: { + ASSERT(shouldCheckOverflow(node->arithMode())); - case NumberUse: { + // This is super clever. We want to do an int52 multiplication and check the + // int52 overflow bit. There is no direct hardware support for this, but we do + // have the ability to do an int64 multiplication and check the int64 overflow + // bit. We leverage that. Consider that a, b are int52 numbers inside int64 + // registers, with the high 12 bits being sign-extended. We can do: + // + // (a * (b << 12)) + // + // This will give us a left-shifted int52 (value is in high 52 bits, low 16 + // bits are zero) plus the int52 overflow bit. I.e. whether this 64-bit + // multiplication overflows is identical to whether the 'a * b' 52-bit + // multiplication overflows. + // + // In our nomenclature, this is: + // + // strictInt52(a) * int52(b) => int52 + // + // That is "strictInt52" means unshifted and "int52" means left-shifted by 16 + // bits. + // + // We don't care which of op1 or op2 serves as the left-shifted operand, so + // we just do whatever is more convenient for op1 and have op2 do the + // opposite. This ensures that we do at most one shift. + + SpeculateWhicheverInt52Operand op1(this, node->child1()); + SpeculateWhicheverInt52Operand op2(this, node->child2(), OppositeShift, op1); + GPRTemporary result(this); + + GPRReg op1GPR = op1.gpr(); + GPRReg op2GPR = op2.gpr(); + GPRReg resultGPR = result.gpr(); + + m_jit.move(op1GPR, resultGPR); + speculationCheck( + Int52Overflow, JSValueRegs(), 0, + m_jit.branchMul64(MacroAssembler::Overflow, op2GPR, resultGPR)); + + if (shouldCheckNegativeZero(node->arithMode())) { + MacroAssembler::Jump resultNonZero = m_jit.branchTest64( + MacroAssembler::NonZero, resultGPR); + speculationCheck( + NegativeZero, JSValueRegs(), 0, + m_jit.branch64(MacroAssembler::LessThan, op1GPR, TrustedImm64(0))); + speculationCheck( + NegativeZero, JSValueRegs(), 0, + m_jit.branch64(MacroAssembler::LessThan, op2GPR, TrustedImm64(0))); + resultNonZero.link(&m_jit); + } + + int52Result(resultGPR, node); + return; + } +#endif // USE(JSVALUE64) + + case DoubleRepUse: { SpeculateDoubleOperand op1(this, node->child1()); SpeculateDoubleOperand op2(this, node->child2()); FPRTemporary result(this, op1, op2); @@ -3435,182 +3792,615 @@ void SpeculativeJIT::compileArithMul(Node* node) doubleResult(result.fpr(), node); return; } - + + case UntypedUse: { + Edge& leftChild = node->child1(); + Edge& rightChild = node->child2(); + + if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node())) { + JSValueOperand left(this, leftChild); + JSValueOperand right(this, rightChild); + JSValueRegs leftRegs = left.jsValueRegs(); + JSValueRegs rightRegs = right.jsValueRegs(); +#if USE(JSVALUE64) + GPRTemporary result(this); + JSValueRegs resultRegs = JSValueRegs(result.gpr()); +#else + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); + JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr()); +#endif + flushRegisters(); + callOperation(operationValueMul, resultRegs, leftRegs, rightRegs); + m_jit.exceptionCheck(); + + jsValueResult(resultRegs, node); + return; + } + + Optional<JSValueOperand> left; + Optional<JSValueOperand> right; + + JSValueRegs leftRegs; + JSValueRegs rightRegs; + + FPRTemporary leftNumber(this); + FPRTemporary rightNumber(this); + FPRReg leftFPR = leftNumber.fpr(); + FPRReg rightFPR = rightNumber.fpr(); + +#if USE(JSVALUE64) + GPRTemporary result(this); + JSValueRegs resultRegs = JSValueRegs(result.gpr()); + GPRTemporary scratch(this); + GPRReg scratchGPR = scratch.gpr(); + FPRReg scratchFPR = InvalidFPRReg; +#else + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); + JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr()); + GPRReg scratchGPR = resultTag.gpr(); + FPRTemporary fprScratch(this); + FPRReg scratchFPR = fprScratch.fpr(); +#endif + + SnippetOperand leftOperand(m_state.forNode(leftChild).resultType()); + SnippetOperand rightOperand(m_state.forNode(rightChild).resultType()); + + // The snippet generator does not support both operands being constant. If the left + // operand is already const, we'll ignore the right operand's constness. + if (leftChild->isInt32Constant()) + leftOperand.setConstInt32(leftChild->asInt32()); + else if (rightChild->isInt32Constant()) + rightOperand.setConstInt32(rightChild->asInt32()); + + ASSERT(!leftOperand.isConst() || !rightOperand.isConst()); + + if (!leftOperand.isPositiveConstInt32()) { + left = JSValueOperand(this, leftChild); + leftRegs = left->jsValueRegs(); + } + if (!rightOperand.isPositiveConstInt32()) { + right = JSValueOperand(this, rightChild); + rightRegs = right->jsValueRegs(); + } + + JITMulGenerator gen(leftOperand, rightOperand, resultRegs, leftRegs, rightRegs, + leftFPR, rightFPR, scratchGPR, scratchFPR); + gen.generateFastPath(m_jit); + + ASSERT(gen.didEmitFastPath()); + gen.endJumpList().append(m_jit.jump()); + + gen.slowPathJumpList().link(&m_jit); + silentSpillAllRegisters(resultRegs); + + if (leftOperand.isPositiveConstInt32()) { + leftRegs = resultRegs; + m_jit.moveValue(leftChild->asJSValue(), leftRegs); + } else if (rightOperand.isPositiveConstInt32()) { + rightRegs = resultRegs; + m_jit.moveValue(rightChild->asJSValue(), rightRegs); + } + + callOperation(operationValueMul, resultRegs, leftRegs, rightRegs); + + silentFillAllRegisters(resultRegs); + m_jit.exceptionCheck(); + + gen.endJumpList().link(&m_jit); + jsValueResult(resultRegs, node); + return; + } + default: RELEASE_ASSERT_NOT_REACHED(); return; } } -#if CPU(X86) || CPU(X86_64) -void SpeculativeJIT::compileIntegerArithDivForX86(Node* node) +void SpeculativeJIT::compileArithDiv(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; - } + switch (node->binaryUseKind()) { + case Int32Use: { +#if CPU(X86) || CPU(X86_64) + SpeculateInt32Operand op1(this, node->child1()); + SpeculateInt32Operand 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); + ASSERT(temp != op1GPR); + ASSERT(temp != op2GPR); - m_jit.add32(JITCompiler::TrustedImm32(1), op2GPR, temp); + m_jit.add32(JITCompiler::TrustedImm32(1), op2GPR, temp); - JITCompiler::Jump safeDenominator = m_jit.branch32(JITCompiler::Above, temp, JITCompiler::TrustedImm32(1)); + JITCompiler::Jump safeDenominator = m_jit.branch32(JITCompiler::Above, temp, JITCompiler::TrustedImm32(1)); - JITCompiler::JumpList done; - if (nodeUsedAsNumber(node->arithNodeFlags())) { - speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchTest32(JITCompiler::Zero, op2GPR)); - speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branch32(JITCompiler::Equal, op1GPR, TrustedImm32(-2147483647-1))); - } else { - // This is the case where we convert the result to an int after we're done, and we - // already know that the denominator is either -1 or 0. So, if the denominator is - // zero, then the result should be zero. If the denominator is not zero (i.e. it's - // -1) and the numerator is -2^31 then the result should be -2^31. Otherwise we - // are happy to fall through to a normal division, since we're just dividing - // something by negative 1. - - JITCompiler::Jump notZero = m_jit.branchTest32(JITCompiler::NonZero, op2GPR); - m_jit.move(TrustedImm32(0), eax.gpr()); - done.append(m_jit.jump()); + JITCompiler::JumpList done; + if (shouldCheckOverflow(node->arithMode())) { + speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchTest32(JITCompiler::Zero, op2GPR)); + speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branch32(JITCompiler::Equal, op1GPR, TrustedImm32(-2147483647-1))); + } else { + // This is the case where we convert the result to an int after we're done, and we + // already know that the denominator is either -1 or 0. So, if the denominator is + // zero, then the result should be zero. If the denominator is not zero (i.e. it's + // -1) and the numerator is -2^31 then the result should be -2^31. Otherwise we + // are happy to fall through to a normal division, since we're just dividing + // something by negative 1. - notZero.link(&m_jit); - JITCompiler::Jump notNeg2ToThe31 = - m_jit.branch32(JITCompiler::NotEqual, op1GPR, TrustedImm32(-2147483647-1)); - m_jit.move(op1GPR, eax.gpr()); - done.append(m_jit.jump()); + JITCompiler::Jump notZero = m_jit.branchTest32(JITCompiler::NonZero, op2GPR); + m_jit.move(TrustedImm32(0), eax.gpr()); + done.append(m_jit.jump()); - notNeg2ToThe31.link(&m_jit); - } + notZero.link(&m_jit); + JITCompiler::Jump notNeg2ToThe31 = + m_jit.branch32(JITCompiler::NotEqual, op1GPR, TrustedImm32(-2147483647-1)); + m_jit.zeroExtend32ToPtr(op1GPR, eax.gpr()); + done.append(m_jit.jump()); + + notNeg2ToThe31.link(&m_jit); + } - safeDenominator.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(), 0, m_jit.branch32(MacroAssembler::LessThan, op2GPR, TrustedImm32(0))); - numeratorNonZero.link(&m_jit); - } + // If the user cares about negative zero, then speculate that we're not about + // to produce negative zero. + if (shouldCheckNegativeZero(node->arithMode())) { + MacroAssembler::Jump numeratorNonZero = m_jit.branchTest32(MacroAssembler::NonZero, op1GPR); + speculationCheck(NegativeZero, JSValueRegs(), 0, m_jit.branch32(MacroAssembler::LessThan, op2GPR, TrustedImm32(0))); + numeratorNonZero.link(&m_jit); + } - if (op2TempGPR != InvalidGPRReg) { - m_jit.move(op2GPR, op2TempGPR); - op2GPR = op2TempGPR; - } + 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); + m_jit.move(op1GPR, eax.gpr()); + m_jit.x86ConvertToDoubleWord32(); + m_jit.x86Div32(op2GPR); - if (op2TempGPR != InvalidGPRReg) - unlock(op2TempGPR); + 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(), 0, m_jit.branchTest32(JITCompiler::NonZero, edx.gpr())); - else + // Check that there was no remainder. If there had been, then we'd be obligated to + // produce a double result instead. + if (shouldCheckOverflow(node->arithMode())) + speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchTest32(JITCompiler::NonZero, edx.gpr())); + done.link(&m_jit); - - integerResult(eax.gpr(), node); -} -#elif CPU(APPLE_ARMV7S) -void SpeculativeJIT::compileIntegerArithDivForARMv7s(Node* node) -{ - SpeculateIntegerOperand op1(this, node->child1()); - SpeculateIntegerOperand op2(this, node->child2()); - GPRReg op1GPR = op1.gpr(); - GPRReg op2GPR = op2.gpr(); - GPRTemporary quotient(this); - GPRTemporary multiplyAnswer(this); + int32Result(eax.gpr(), node); +#elif HAVE(ARM_IDIV_INSTRUCTIONS) || CPU(ARM64) + SpeculateInt32Operand op1(this, node->child1()); + SpeculateInt32Operand op2(this, node->child2()); + GPRReg op1GPR = op1.gpr(); + GPRReg op2GPR = op2.gpr(); + GPRTemporary quotient(this); + GPRTemporary multiplyAnswer(this); + + // If the user cares about negative zero, then speculate that we're not about + // to produce negative zero. + if (shouldCheckNegativeZero(node->arithMode())) { + MacroAssembler::Jump numeratorNonZero = m_jit.branchTest32(MacroAssembler::NonZero, op1GPR); + speculationCheck(NegativeZero, JSValueRegs(), 0, m_jit.branch32(MacroAssembler::LessThan, op2GPR, TrustedImm32(0))); + numeratorNonZero.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(), 0, m_jit.branch32(MacroAssembler::LessThan, op2GPR, TrustedImm32(0))); - numeratorNonZero.link(&m_jit); - } + if (shouldCheckOverflow(node->arithMode())) + speculationCheck(Overflow, JSValueRegs(), nullptr, m_jit.branchTest32(MacroAssembler::Zero, op2GPR)); - m_jit.assembler().sdiv(quotient.gpr(), op1GPR, op2GPR); + m_jit.assembler().sdiv<32>(quotient.gpr(), op1GPR, op2GPR); + + // Check that there was no remainder. If there had been, then we'd be obligated to + // produce a double result instead. + if (shouldCheckOverflow(node->arithMode())) { + speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchMul32(JITCompiler::Overflow, quotient.gpr(), op2GPR, multiplyAnswer.gpr())); + speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branch32(JITCompiler::NotEqual, multiplyAnswer.gpr(), op1GPR)); + } - // 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(), 0, m_jit.branchMul32(JITCompiler::Overflow, quotient.gpr(), op2GPR, multiplyAnswer.gpr())); - speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branch32(JITCompiler::NotEqual, multiplyAnswer.gpr(), op1GPR)); + int32Result(quotient.gpr(), node); +#else + RELEASE_ASSERT_NOT_REACHED(); +#endif + break; + } + + case DoubleRepUse: { + SpeculateDoubleOperand op1(this, node->child1()); + SpeculateDoubleOperand op2(this, node->child2()); + FPRTemporary result(this, op1); + + FPRReg reg1 = op1.fpr(); + FPRReg reg2 = op2.fpr(); + m_jit.divDouble(reg1, reg2, result.fpr()); + + doubleResult(result.fpr(), node); + break; } - integerResult(quotient.gpr(), node); -} -#elif CPU(MIPS) -void SpeculativeJIT::compileIntegerArithDivForMIPS(Node* node) -{ - SpeculateIntegerOperand op1(this, node->child1()); - SpeculateIntegerOperand op2(this, node->child2()); - GPRTemporary quotient(this); - GPRReg op1GPR = op1.gpr(); - GPRReg op2GPR = op2.gpr(); - GPRReg quotientGPR = quotient.gpr(); - JITCompiler::Jump done; + case UntypedUse: { + Edge& leftChild = node->child1(); + Edge& rightChild = node->child2(); + + if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node())) { + JSValueOperand left(this, leftChild); + JSValueOperand right(this, rightChild); + JSValueRegs leftRegs = left.jsValueRegs(); + JSValueRegs rightRegs = right.jsValueRegs(); +#if USE(JSVALUE64) + GPRTemporary result(this); + JSValueRegs resultRegs = JSValueRegs(result.gpr()); +#else + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); + JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr()); +#endif + flushRegisters(); + callOperation(operationValueDiv, resultRegs, leftRegs, rightRegs); + m_jit.exceptionCheck(); - // 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(), 0, m_jit.branch32(MacroAssembler::LessThan, op2GPR, TrustedImm32(0))); - numeratorNonZero.link(&m_jit); - } + jsValueResult(resultRegs, node); + return; + } - if (nodeUsedAsNumber(node->arithNodeFlags())) { - speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchTest32(JITCompiler::Zero, op2GPR)); - speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branch32(JITCompiler::Equal, op1GPR, TrustedImm32(-2147483647-1))); - } else { - JITCompiler::Jump notZero = m_jit.branchTest32(JITCompiler::NonZero, op2GPR); - m_jit.move(TrustedImm32(0), quotientGPR); - done = m_jit.jump(); - notZero.link(&m_jit); - } + Optional<JSValueOperand> left; + Optional<JSValueOperand> right; - m_jit.assembler().div(op1GPR, op2GPR); - m_jit.assembler().mflo(quotientGPR); + JSValueRegs leftRegs; + JSValueRegs rightRegs; - // 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())) { - GPRTemporary remainder(this); - m_jit.assembler().mfhi(remainder.gpr()); - speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchTest32(MacroAssembler::NonZero, remainder.gpr())); - } else - done.link(&m_jit); + FPRTemporary leftNumber(this); + FPRTemporary rightNumber(this); + FPRReg leftFPR = leftNumber.fpr(); + FPRReg rightFPR = rightNumber.fpr(); + FPRTemporary fprScratch(this); + FPRReg scratchFPR = fprScratch.fpr(); - integerResult(quotientGPR, node); -} +#if USE(JSVALUE64) + GPRTemporary result(this); + JSValueRegs resultRegs = JSValueRegs(result.gpr()); + GPRTemporary scratch(this); + GPRReg scratchGPR = scratch.gpr(); +#else + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); + JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr()); + GPRReg scratchGPR = resultTag.gpr(); #endif + SnippetOperand leftOperand(m_state.forNode(leftChild).resultType()); + SnippetOperand rightOperand(m_state.forNode(rightChild).resultType()); + + if (leftChild->isInt32Constant()) + leftOperand.setConstInt32(leftChild->asInt32()); +#if USE(JSVALUE64) + else if (leftChild->isDoubleConstant()) + leftOperand.setConstDouble(leftChild->asNumber()); +#endif + + if (leftOperand.isConst()) { + // The snippet generator only supports 1 argument as a constant. + // Ignore the rightChild's const-ness. + } else if (rightChild->isInt32Constant()) + rightOperand.setConstInt32(rightChild->asInt32()); +#if USE(JSVALUE64) + else if (rightChild->isDoubleConstant()) + rightOperand.setConstDouble(rightChild->asNumber()); +#endif + + RELEASE_ASSERT(!leftOperand.isConst() || !rightOperand.isConst()); + + if (!leftOperand.isConst()) { + left = JSValueOperand(this, leftChild); + leftRegs = left->jsValueRegs(); + } + if (!rightOperand.isConst()) { + right = JSValueOperand(this, rightChild); + rightRegs = right->jsValueRegs(); + } + + JITDivGenerator gen(leftOperand, rightOperand, resultRegs, leftRegs, rightRegs, + leftFPR, rightFPR, scratchGPR, scratchFPR); + gen.generateFastPath(m_jit); + + ASSERT(gen.didEmitFastPath()); + gen.endJumpList().append(m_jit.jump()); + + gen.slowPathJumpList().link(&m_jit); + silentSpillAllRegisters(resultRegs); + + if (leftOperand.isConst()) { + leftRegs = resultRegs; + m_jit.moveValue(leftChild->asJSValue(), leftRegs); + } + if (rightOperand.isConst()) { + rightRegs = resultRegs; + m_jit.moveValue(rightChild->asJSValue(), rightRegs); + } + + callOperation(operationValueDiv, resultRegs, leftRegs, rightRegs); + + silentFillAllRegisters(resultRegs); + m_jit.exceptionCheck(); + + gen.endJumpList().link(&m_jit); + jsValueResult(resultRegs, node); + return; + } + + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } +} + void SpeculativeJIT::compileArithMod(Node* node) { switch (node->binaryUseKind()) { case Int32Use: { - compileSoftModulo(node); + // 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()); + + if (node->child2()->isInt32Constant()) { + int32_t divisor = node->child2()->asInt32(); + if (divisor > 1 && hasOneBitSet(divisor)) { + unsigned logarithm = WTF::fastLog2(static_cast<uint32_t>(divisor)); + GPRReg dividendGPR = op1.gpr(); + GPRTemporary result(this); + GPRReg resultGPR = result.gpr(); + + // This is what LLVM generates. It's pretty crazy. Here's my + // attempt at understanding it. + + // First, compute either divisor - 1, or 0, depending on whether + // the dividend is negative: + // + // If dividend < 0: resultGPR = divisor - 1 + // If dividend >= 0: resultGPR = 0 + m_jit.move(dividendGPR, resultGPR); + m_jit.rshift32(TrustedImm32(31), resultGPR); + m_jit.urshift32(TrustedImm32(32 - logarithm), resultGPR); + + // Add in the dividend, so that: + // + // If dividend < 0: resultGPR = dividend + divisor - 1 + // If dividend >= 0: resultGPR = dividend + m_jit.add32(dividendGPR, resultGPR); + + // Mask so as to only get the *high* bits. This rounds down + // (towards negative infinity) resultGPR to the nearest multiple + // of divisor, so that: + // + // If dividend < 0: resultGPR = floor((dividend + divisor - 1) / divisor) + // If dividend >= 0: resultGPR = floor(dividend / divisor) + // + // Note that this can be simplified to: + // + // If dividend < 0: resultGPR = ceil(dividend / divisor) + // If dividend >= 0: resultGPR = floor(dividend / divisor) + // + // Note that if the dividend is negative, resultGPR will also be negative. + // Regardless of the sign of dividend, resultGPR will be rounded towards + // zero, because of how things are conditionalized. + m_jit.and32(TrustedImm32(-divisor), resultGPR); + + // Subtract resultGPR from dividendGPR, which yields the remainder: + // + // resultGPR = dividendGPR - resultGPR + m_jit.neg32(resultGPR); + m_jit.add32(dividendGPR, resultGPR); + + if (shouldCheckNegativeZero(node->arithMode())) { + // Check that we're not about to create negative zero. + JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, dividendGPR, TrustedImm32(0)); + speculationCheck(NegativeZero, JSValueRegs(), 0, m_jit.branchTest32(JITCompiler::Zero, resultGPR)); + numeratorPositive.link(&m_jit); + } + + int32Result(resultGPR, node); + return; + } + } + +#if CPU(X86) || CPU(X86_64) + if (node->child2()->isInt32Constant()) { + int32_t divisor = node->child2()->asInt32(); + if (divisor && divisor != -1) { + 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); + m_jit.x86ConvertToDoubleWord32(); + m_jit.x86Div32(scratchGPR); + if (shouldCheckNegativeZero(node->arithMode())) { + JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, op1SaveGPR, TrustedImm32(0)); + speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchTest32(JITCompiler::Zero, edx.gpr())); + numeratorPositive.link(&m_jit); + } + + if (op1SaveGPR != op1Gpr) + unlock(op1SaveGPR); + + int32Result(edx.gpr(), node); + return; + } + } +#endif + + SpeculateInt32Operand op2(this, node->child2()); +#if CPU(X86) || CPU(X86_64) + GPRTemporary eax(this, X86Registers::eax); + GPRTemporary edx(this, X86Registers::edx); + 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 (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::JumpList done; + + // FIXME: -2^31 / -1 will actually yield negative zero, so we could have a + // separate case for that. But it probably doesn't matter so much. + if (shouldCheckOverflow(node->arithMode())) { + speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchTest32(JITCompiler::Zero, op2GPR)); + speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branch32(JITCompiler::Equal, op1GPR, TrustedImm32(-2147483647-1))); + } else { + // This is the case where we convert the result to an int after we're done, and we + // already know that the denominator is either -1 or 0. So, if the denominator is + // zero, then the result should be zero. If the denominator is not zero (i.e. it's + // -1) and the numerator is -2^31 then the result should be 0. Otherwise we are + // happy to fall through to a normal division, since we're just dividing something + // by negative 1. + + JITCompiler::Jump notZero = m_jit.branchTest32(JITCompiler::NonZero, op2GPR); + m_jit.move(TrustedImm32(0), edx.gpr()); + done.append(m_jit.jump()); + + notZero.link(&m_jit); + JITCompiler::Jump notNeg2ToThe31 = + m_jit.branch32(JITCompiler::NotEqual, op1GPR, TrustedImm32(-2147483647-1)); + m_jit.move(TrustedImm32(0), edx.gpr()); + done.append(m_jit.jump()); + + notNeg2ToThe31.link(&m_jit); + } + + safeDenominator.link(&m_jit); + + if (op2TempGPR != InvalidGPRReg) { + m_jit.move(op2GPR, op2TempGPR); + op2GPR = op2TempGPR; + } + + m_jit.move(op1GPR, eax.gpr()); + m_jit.x86ConvertToDoubleWord32(); + m_jit.x86Div32(op2GPR); + + if (op2TempGPR != InvalidGPRReg) + unlock(op2TempGPR); + + // Check that we're not about to create negative zero. + if (shouldCheckNegativeZero(node->arithMode())) { + JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, op1SaveGPR, TrustedImm32(0)); + speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchTest32(JITCompiler::Zero, edx.gpr())); + numeratorPositive.link(&m_jit); + } + + if (op1SaveGPR != op1GPR) + unlock(op1SaveGPR); + + done.link(&m_jit); + int32Result(edx.gpr(), node); + +#elif HAVE(ARM_IDIV_INSTRUCTIONS) || CPU(ARM64) + GPRTemporary temp(this); + GPRTemporary quotientThenRemainder(this); + GPRTemporary multiplyAnswer(this); + GPRReg dividendGPR = op1.gpr(); + GPRReg divisorGPR = op2.gpr(); + GPRReg quotientThenRemainderGPR = quotientThenRemainder.gpr(); + GPRReg multiplyAnswerGPR = multiplyAnswer.gpr(); + + JITCompiler::JumpList done; + + if (shouldCheckOverflow(node->arithMode())) + speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchTest32(JITCompiler::Zero, divisorGPR)); + else { + JITCompiler::Jump denominatorNotZero = m_jit.branchTest32(JITCompiler::NonZero, divisorGPR); + m_jit.move(divisorGPR, quotientThenRemainderGPR); + done.append(m_jit.jump()); + denominatorNotZero.link(&m_jit); + } + + m_jit.assembler().sdiv<32>(quotientThenRemainderGPR, dividendGPR, divisorGPR); + // FIXME: It seems like there are cases where we don't need this? What if we have + // arithMode() == Arith::Unchecked? + // https://bugs.webkit.org/show_bug.cgi?id=126444 + speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchMul32(JITCompiler::Overflow, quotientThenRemainderGPR, divisorGPR, multiplyAnswerGPR)); +#if HAVE(ARM_IDIV_INSTRUCTIONS) + m_jit.assembler().sub(quotientThenRemainderGPR, dividendGPR, multiplyAnswerGPR); +#else + m_jit.assembler().sub<32>(quotientThenRemainderGPR, dividendGPR, multiplyAnswerGPR); +#endif + + // If the user cares about negative zero, then speculate that we're not about + // to produce negative zero. + if (shouldCheckNegativeZero(node->arithMode())) { + // Check that we're not about to create negative zero. + JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, dividendGPR, TrustedImm32(0)); + speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchTest32(JITCompiler::Zero, quotientThenRemainderGPR)); + numeratorPositive.link(&m_jit); + } + + done.link(&m_jit); + + int32Result(quotientThenRemainderGPR, node); +#else // not architecture that can do integer division + RELEASE_ASSERT_NOT_REACHED(); +#endif return; } - case NumberUse: { + case DoubleRepUse: { SpeculateDoubleOperand op1(this, node->child1()); SpeculateDoubleOperand op2(this, node->child2()); @@ -3633,18 +4423,226 @@ void SpeculativeJIT::compileArithMod(Node* node) } } +void SpeculativeJIT::compileArithRounding(Node* node) +{ + ASSERT(node->child1().useKind() == DoubleRepUse); + + SpeculateDoubleOperand value(this, node->child1()); + FPRReg valueFPR = value.fpr(); + + auto setResult = [&] (FPRReg resultFPR) { + if (producesInteger(node->arithRoundingMode())) { + GPRTemporary roundedResultAsInt32(this); + FPRTemporary scratch(this); + FPRReg scratchFPR = scratch.fpr(); + GPRReg resultGPR = roundedResultAsInt32.gpr(); + JITCompiler::JumpList failureCases; + m_jit.branchConvertDoubleToInt32(resultFPR, resultGPR, failureCases, scratchFPR, shouldCheckNegativeZero(node->arithRoundingMode())); + speculationCheck(Overflow, JSValueRegs(), node, failureCases); + + int32Result(resultGPR, node); + } else + doubleResult(resultFPR, node); + }; + + if (m_jit.supportsFloatingPointRounding()) { + switch (node->op()) { + case ArithRound: { + FPRTemporary result(this); + FPRReg resultFPR = result.fpr(); + if (producesInteger(node->arithRoundingMode()) && !shouldCheckNegativeZero(node->arithRoundingMode())) { + static const double halfConstant = 0.5; + m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&halfConstant), resultFPR); + m_jit.addDouble(valueFPR, resultFPR); + m_jit.floorDouble(resultFPR, resultFPR); + } else { + m_jit.ceilDouble(valueFPR, resultFPR); + FPRTemporary realPart(this); + FPRReg realPartFPR = realPart.fpr(); + m_jit.subDouble(resultFPR, valueFPR, realPartFPR); + + FPRTemporary scratch(this); + FPRReg scratchFPR = scratch.fpr(); + static const double halfConstant = 0.5; + m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&halfConstant), scratchFPR); + + JITCompiler::Jump shouldUseCeiled = m_jit.branchDouble(JITCompiler::DoubleLessThanOrEqual, realPartFPR, scratchFPR); + static const double oneConstant = -1.0; + m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&oneConstant), scratchFPR); + m_jit.addDouble(scratchFPR, resultFPR); + shouldUseCeiled.link(&m_jit); + } + setResult(resultFPR); + return; + } + + case ArithFloor: { + FPRTemporary rounded(this); + FPRReg resultFPR = rounded.fpr(); + m_jit.floorDouble(valueFPR, resultFPR); + setResult(resultFPR); + return; + } + + case ArithCeil: { + FPRTemporary rounded(this); + FPRReg resultFPR = rounded.fpr(); + m_jit.ceilDouble(valueFPR, resultFPR); + setResult(resultFPR); + return; + } + + default: + RELEASE_ASSERT_NOT_REACHED(); + } + } else { + flushRegisters(); + FPRResult roundedResultAsDouble(this); + FPRReg resultFPR = roundedResultAsDouble.fpr(); + if (node->op() == ArithRound) + callOperation(jsRound, resultFPR, valueFPR); + else if (node->op() == ArithFloor) + callOperation(floor, resultFPR, valueFPR); + else { + ASSERT(node->op() == ArithCeil); + callOperation(ceil, resultFPR, valueFPR); + } + m_jit.exceptionCheck(); + setResult(resultFPR); + } +} + +void SpeculativeJIT::compileArithSqrt(Node* node) +{ + SpeculateDoubleOperand op1(this, node->child1()); + FPRReg op1FPR = op1.fpr(); + + if (!MacroAssembler::supportsFloatingPointSqrt() || !Options::useArchitectureSpecificOptimizations()) { + flushRegisters(); + FPRResult result(this); + callOperation(sqrt, result.fpr(), op1FPR); + doubleResult(result.fpr(), node); + } else { + FPRTemporary result(this, op1); + m_jit.sqrtDouble(op1.fpr(), result.fpr()); + doubleResult(result.fpr(), node); + } +} + +// For small positive integers , it is worth doing a tiny inline loop to exponentiate the base. +// Every register is clobbered by this helper. +static MacroAssembler::Jump compileArithPowIntegerFastPath(JITCompiler& assembler, FPRReg xOperand, GPRReg yOperand, FPRReg result) +{ + MacroAssembler::JumpList skipFastPath; + skipFastPath.append(assembler.branch32(MacroAssembler::Above, yOperand, MacroAssembler::TrustedImm32(1000))); + + static const double oneConstant = 1.0; + assembler.loadDouble(MacroAssembler::TrustedImmPtr(&oneConstant), result); + + MacroAssembler::Label startLoop(assembler.label()); + MacroAssembler::Jump exponentIsEven = assembler.branchTest32(MacroAssembler::Zero, yOperand, MacroAssembler::TrustedImm32(1)); + assembler.mulDouble(xOperand, result); + exponentIsEven.link(&assembler); + assembler.mulDouble(xOperand, xOperand); + assembler.rshift32(MacroAssembler::TrustedImm32(1), yOperand); + assembler.branchTest32(MacroAssembler::NonZero, yOperand).linkTo(startLoop, &assembler); + + MacroAssembler::Jump skipSlowPath = assembler.jump(); + skipFastPath.link(&assembler); + + return skipSlowPath; +} + +void SpeculativeJIT::compileArithPow(Node* node) +{ + if (node->child2().useKind() == Int32Use) { + SpeculateDoubleOperand xOperand(this, node->child1()); + SpeculateInt32Operand yOperand(this, node->child2()); + FPRReg xOperandfpr = xOperand.fpr(); + GPRReg yOperandGpr = yOperand.gpr(); + FPRTemporary yOperandfpr(this); + + flushRegisters(); + + FPRResult result(this); + FPRReg resultFpr = result.fpr(); + + FPRTemporary xOperandCopy(this); + FPRReg xOperandCopyFpr = xOperandCopy.fpr(); + m_jit.moveDouble(xOperandfpr, xOperandCopyFpr); + + GPRTemporary counter(this); + GPRReg counterGpr = counter.gpr(); + m_jit.move(yOperandGpr, counterGpr); + + MacroAssembler::Jump skipFallback = compileArithPowIntegerFastPath(m_jit, xOperandCopyFpr, counterGpr, resultFpr); + m_jit.convertInt32ToDouble(yOperandGpr, yOperandfpr.fpr()); + callOperation(operationMathPow, resultFpr, xOperandfpr, yOperandfpr.fpr()); + + skipFallback.link(&m_jit); + doubleResult(resultFpr, node); + return; + } + + SpeculateDoubleOperand xOperand(this, node->child1()); + SpeculateDoubleOperand yOperand(this, node->child2()); + FPRReg xOperandfpr = xOperand.fpr(); + FPRReg yOperandfpr = yOperand.fpr(); + + flushRegisters(); + + FPRResult result(this); + FPRReg resultFpr = result.fpr(); + + FPRTemporary xOperandCopy(this); + FPRReg xOperandCopyFpr = xOperandCopy.fpr(); + + FPRTemporary scratch(this); + FPRReg scratchFpr = scratch.fpr(); + + GPRTemporary yOperandInteger(this); + GPRReg yOperandIntegerGpr = yOperandInteger.gpr(); + MacroAssembler::JumpList failedExponentConversionToInteger; + m_jit.branchConvertDoubleToInt32(yOperandfpr, yOperandIntegerGpr, failedExponentConversionToInteger, scratchFpr, false); + + m_jit.moveDouble(xOperandfpr, xOperandCopyFpr); + MacroAssembler::Jump skipFallback = compileArithPowIntegerFastPath(m_jit, xOperandCopyFpr, yOperandInteger.gpr(), resultFpr); + failedExponentConversionToInteger.link(&m_jit); + + callOperation(operationMathPow, resultFpr, xOperandfpr, yOperandfpr); + skipFallback.link(&m_jit); + doubleResult(resultFpr, node); +} + +void SpeculativeJIT::compileArithLog(Node* node) +{ + SpeculateDoubleOperand op1(this, node->child1()); + FPRReg op1FPR = op1.fpr(); + flushRegisters(); + FPRResult result(this); + callOperation(log, result.fpr(), op1FPR); + doubleResult(result.fpr(), node); +} + // 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) +bool SpeculativeJIT::compare(Node* node, MacroAssembler::RelationalCondition condition, MacroAssembler::DoubleCondition doubleCondition, S_JITOperation_EJJ operation) { if (compilePeepHoleBranch(node, condition, doubleCondition, operation)) return true; if (node->isBinaryUseKind(Int32Use)) { - compileIntegerCompare(node, condition); + compileInt32Compare(node, condition); return false; } - - if (node->isBinaryUseKind(NumberUse)) { + +#if USE(JSVALUE64) + if (node->isBinaryUseKind(Int52RepUse)) { + compileInt52Compare(node, condition); + return false; + } +#endif // USE(JSVALUE64) + + if (node->isBinaryUseKind(DoubleRepUse)) { compileDoubleCompare(node, doubleCondition); return false; } @@ -3660,109 +4658,58 @@ bool SpeculativeJIT::compare(Node* node, MacroAssembler::RelationalCondition con return false; } + if (node->isBinaryUseKind(StringIdentUse)) { + compileStringIdentEquality(node); + return false; + } + + if (node->isBinaryUseKind(SymbolUse)) { + compileSymbolEquality(node); + return false; + } + if (node->isBinaryUseKind(ObjectUse)) { compileObjectEquality(node); return false; } - if (node->child1().useKind() == ObjectUse && node->child2().useKind() == ObjectOrOtherUse) { + if (node->isBinaryUseKind(ObjectUse, ObjectOrOtherUse)) { compileObjectToObjectOrOtherEquality(node->child1(), node->child2()); return false; } - if (node->child1().useKind() == ObjectOrOtherUse && node->child2().useKind() == ObjectUse) { + if (node->isBinaryUseKind(ObjectOrOtherUse, ObjectUse)) { compileObjectToObjectOrOtherEquality(node->child2(), node->child1()); return false; } - } - - nonSpeculativeNonPeepholeCompare(node, condition, operation); - return false; -} -bool SpeculativeJIT::compileStrictEqForConstant(Node* node, Edge value, JSValue constant) -{ - JSValueOperand op1(this, value); - - // FIXME: This code is wrong for the case that the constant is null or undefined, - // and the value is an object that MasqueradesAsUndefined. - // https://bugs.webkit.org/show_bug.cgi?id=109487 - - unsigned branchIndexInBlock = detectPeepHoleBranch(); - if (branchIndexInBlock != UINT_MAX) { - Node* branchNode = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); - 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 == nextBlock()) { - condition = MacroAssembler::NotEqual; - BlockIndex tmp = taken; - taken = notTaken; - notTaken = tmp; + if (!needsTypeCheck(node->child1(), SpecOther)) { + nonSpeculativeNonPeepholeCompareNullOrUndefined(node->child2()); + return false; } -#if USE(JSVALUE64) - branch64(condition, op1.gpr(), MacroAssembler::TrustedImm64(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())); - branch32(MacroAssembler::Equal, payloadGPR, MacroAssembler::Imm32(constant.payload()), taken); - notEqual.link(&m_jit); - } else { - // Drop down if equal, go elsehwere if not equal. - branch32(MacroAssembler::NotEqual, tagGPR, MacroAssembler::Imm32(constant.tag()), taken); - branch32(MacroAssembler::NotEqual, payloadGPR, MacroAssembler::Imm32(constant.payload()), taken); + if (!needsTypeCheck(node->child2(), SpecOther)) { + nonSpeculativeNonPeepholeCompareNullOrUndefined(node->child1()); + return false; } -#endif - - jump(notTaken); - - use(node->child1()); - use(node->child2()); - m_indexInBlock = branchIndexInBlock; - m_currentNode = branchNode; - return true; } - - GPRTemporary result(this); - -#if USE(JSVALUE64) - GPRReg op1GPR = op1.gpr(); - GPRReg resultGPR = result.gpr(); - m_jit.move(MacroAssembler::TrustedImm64(ValueFalse), resultGPR); - MacroAssembler::Jump notEqual = m_jit.branch64(MacroAssembler::NotEqual, op1GPR, MacroAssembler::TrustedImm64(JSValue::encode(constant))); - m_jit.or32(MacroAssembler::TrustedImm32(1), resultGPR); - notEqual.link(&m_jit); - jsValueResult(resultGPR, node, DataFormatJSBoolean); -#else - GPRReg op1PayloadGPR = op1.payloadGPR(); - GPRReg op1TagGPR = op1.tagGPR(); - GPRReg resultGPR = result.gpr(); - m_jit.move(TrustedImm32(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(TrustedImm32(1), resultGPR); - notEqual.link(&m_jit); - booleanResult(resultGPR, node); -#endif - + + nonSpeculativeNonPeepholeCompare(node, condition, operation); return false; } bool SpeculativeJIT::compileStrictEq(Node* node) { - switch (node->binaryUseKind()) { - case BooleanUse: { + // FIXME: Currently, we have op_jless, op_jgreater etc. But we don't have op_jeq, op_jstricteq etc. + // `==` and `===` operations with branching will be compiled to op_{eq,stricteq} and op_{jfalse,jtrue}. + // In DFG bytecodes, between op_eq and op_jfalse, we have MovHint to store the result of op_eq. + // As a result, detectPeepHoleBranch() never detects peep hole for that case. + // https://bugs.webkit.org/show_bug.cgi?id=149713 + + if (node->isBinaryUseKind(BooleanUse)) { unsigned branchIndexInBlock = detectPeepHoleBranch(); if (branchIndexInBlock != UINT_MAX) { - Node* branchNode = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); + Node* branchNode = m_block->at(branchIndexInBlock); compilePeepHoleBooleanBranch(node, branchNode, MacroAssembler::Equal); use(node->child1()); use(node->child2()); @@ -3774,25 +4721,42 @@ bool SpeculativeJIT::compileStrictEq(Node* node) return false; } - case Int32Use: { + if (node->isBinaryUseKind(Int32Use)) { unsigned branchIndexInBlock = detectPeepHoleBranch(); if (branchIndexInBlock != UINT_MAX) { - Node* branchNode = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); - compilePeepHoleIntegerBranch(node, branchNode, MacroAssembler::Equal); + Node* branchNode = m_block->at(branchIndexInBlock); + compilePeepHoleInt32Branch(node, branchNode, MacroAssembler::Equal); use(node->child1()); use(node->child2()); m_indexInBlock = branchIndexInBlock; m_currentNode = branchNode; return true; } - compileIntegerCompare(node, MacroAssembler::Equal); + compileInt32Compare(node, MacroAssembler::Equal); return false; } - - case NumberUse: { + +#if USE(JSVALUE64) + if (node->isBinaryUseKind(Int52RepUse)) { + unsigned branchIndexInBlock = detectPeepHoleBranch(); + if (branchIndexInBlock != UINT_MAX) { + Node* branchNode = m_block->at(branchIndexInBlock); + compilePeepHoleInt52Branch(node, branchNode, MacroAssembler::Equal); + use(node->child1()); + use(node->child2()); + m_indexInBlock = branchIndexInBlock; + m_currentNode = branchNode; + return true; + } + compileInt52Compare(node, MacroAssembler::Equal); + return false; + } +#endif // USE(JSVALUE64) + + if (node->isBinaryUseKind(DoubleRepUse)) { unsigned branchIndexInBlock = detectPeepHoleBranch(); if (branchIndexInBlock != UINT_MAX) { - Node* branchNode = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); + Node* branchNode = m_block->at(branchIndexInBlock); compilePeepHoleDoubleBranch(node, branchNode, MacroAssembler::DoubleEqual); use(node->child1()); use(node->child2()); @@ -3803,16 +4767,66 @@ bool SpeculativeJIT::compileStrictEq(Node* node) compileDoubleCompare(node, MacroAssembler::DoubleEqual); return false; } - - case StringUse: { + + if (node->isBinaryUseKind(SymbolUse)) { + unsigned branchIndexInBlock = detectPeepHoleBranch(); + if (branchIndexInBlock != UINT_MAX) { + Node* branchNode = m_block->at(branchIndexInBlock); + compilePeepHoleSymbolEquality(node, branchNode); + use(node->child1()); + use(node->child2()); + m_indexInBlock = branchIndexInBlock; + m_currentNode = branchNode; + return true; + } + compileSymbolEquality(node); + return false; + } + + if (node->isBinaryUseKind(StringUse)) { compileStringEquality(node); return false; } - - case ObjectUse: { + + if (node->isBinaryUseKind(StringIdentUse)) { + compileStringIdentEquality(node); + return false; + } + + if (node->isBinaryUseKind(ObjectUse, UntypedUse)) { unsigned branchIndexInBlock = detectPeepHoleBranch(); if (branchIndexInBlock != UINT_MAX) { - Node* branchNode = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); + Node* branchNode = m_block->at(branchIndexInBlock); + compilePeepHoleObjectStrictEquality(node->child1(), node->child2(), branchNode); + use(node->child1()); + use(node->child2()); + m_indexInBlock = branchIndexInBlock; + m_currentNode = branchNode; + return true; + } + compileObjectStrictEquality(node->child1(), node->child2()); + return false; + } + + if (node->isBinaryUseKind(UntypedUse, ObjectUse)) { + unsigned branchIndexInBlock = detectPeepHoleBranch(); + if (branchIndexInBlock != UINT_MAX) { + Node* branchNode = m_block->at(branchIndexInBlock); + compilePeepHoleObjectStrictEquality(node->child2(), node->child1(), branchNode); + use(node->child1()); + use(node->child2()); + m_indexInBlock = branchIndexInBlock; + m_currentNode = branchNode; + return true; + } + compileObjectStrictEquality(node->child2(), node->child1()); + return false; + } + + if (node->isBinaryUseKind(ObjectUse)) { + unsigned branchIndexInBlock = detectPeepHoleBranch(); + if (branchIndexInBlock != UINT_MAX) { + Node* branchNode = m_block->at(branchIndexInBlock); compilePeepHoleObjectEquality(node, branchNode); use(node->child1()); use(node->child2()); @@ -3823,15 +4837,35 @@ bool SpeculativeJIT::compileStrictEq(Node* node) compileObjectEquality(node); return false; } - - case UntypedUse: { - return nonSpeculativeStrictEq(node); + + if (node->isBinaryUseKind(MiscUse, UntypedUse) + || node->isBinaryUseKind(UntypedUse, MiscUse)) { + compileMiscStrictEq(node); + return false; } - - default: - RELEASE_ASSERT_NOT_REACHED(); + + if (node->isBinaryUseKind(StringIdentUse, NotStringVarUse)) { + compileStringIdentToNotStringVarEquality(node, node->child1(), node->child2()); return false; } + + if (node->isBinaryUseKind(NotStringVarUse, StringIdentUse)) { + compileStringIdentToNotStringVarEquality(node, node->child2(), node->child1()); + return false; + } + + if (node->isBinaryUseKind(StringUse, UntypedUse)) { + compileStringToUntypedEquality(node, node->child1(), node->child2()); + return false; + } + + if (node->isBinaryUseKind(UntypedUse, StringUse)) { + compileStringToUntypedEquality(node, node->child2(), node->child1()); + return false; + } + + RELEASE_ASSERT(node->isBinaryUseKind(UntypedUse)); + return nonSpeculativeStrictEq(node); } void SpeculativeJIT::compileBooleanCompare(Node* node, MacroAssembler::RelationalCondition condition) @@ -3842,52 +4876,66 @@ void SpeculativeJIT::compileBooleanCompare(Node* node, MacroAssembler::Relationa m_jit.compare32(condition, op1.gpr(), op2.gpr(), result.gpr()); - // If we add a DataFormatBool, we should use it here. -#if USE(JSVALUE32_64) - booleanResult(result.gpr(), node); -#else - m_jit.or32(TrustedImm32(ValueFalse), result.gpr()); - jsValueResult(result.gpr(), m_currentNode, DataFormatJSBoolean); -#endif + unblessedBooleanResult(result.gpr(), node); } -void SpeculativeJIT::compileStringEquality(Node* node) +template<typename Functor> +void SpeculativeJIT::extractStringImplFromBinarySymbols(Edge leftSymbolEdge, Edge rightSymbolEdge, const Functor& functor) { - SpeculateCellOperand left(this, node->child1()); - SpeculateCellOperand right(this, node->child2()); - GPRTemporary length(this); + SpeculateCellOperand left(this, leftSymbolEdge); + SpeculateCellOperand right(this, rightSymbolEdge); GPRTemporary leftTemp(this); GPRTemporary rightTemp(this); - GPRTemporary leftTemp2(this, left); - GPRTemporary rightTemp2(this, right); - + GPRReg leftGPR = left.gpr(); GPRReg rightGPR = right.gpr(); - GPRReg lengthGPR = length.gpr(); GPRReg leftTempGPR = leftTemp.gpr(); GPRReg rightTempGPR = rightTemp.gpr(); - GPRReg leftTemp2GPR = leftTemp2.gpr(); - GPRReg rightTemp2GPR = rightTemp2.gpr(); - + + speculateSymbol(leftSymbolEdge, leftGPR); + speculateSymbol(rightSymbolEdge, rightGPR); + + m_jit.loadPtr(JITCompiler::Address(leftGPR, Symbol::offsetOfPrivateName()), leftTempGPR); + m_jit.loadPtr(JITCompiler::Address(rightGPR, Symbol::offsetOfPrivateName()), rightTempGPR); + + functor(leftTempGPR, rightTempGPR); +} + +void SpeculativeJIT::compileSymbolEquality(Node* node) +{ + extractStringImplFromBinarySymbols(node->child1(), node->child2(), [&] (GPRReg leftStringImpl, GPRReg rightStringImpl) { + m_jit.comparePtr(JITCompiler::Equal, leftStringImpl, rightStringImpl, leftStringImpl); + unblessedBooleanResult(leftStringImpl, node); + }); +} + +void SpeculativeJIT::compilePeepHoleSymbolEquality(Node* node, Node* branchNode) +{ + BasicBlock* taken = branchNode->branchData()->taken.block; + BasicBlock* notTaken = branchNode->branchData()->notTaken.block; + + extractStringImplFromBinarySymbols(node->child1(), node->child2(), [&] (GPRReg leftStringImpl, GPRReg rightStringImpl) { + if (taken == nextBlock()) { + branchPtr(JITCompiler::NotEqual, leftStringImpl, rightStringImpl, notTaken); + jump(taken); + } else { + branchPtr(JITCompiler::Equal, leftStringImpl, rightStringImpl, taken); + jump(notTaken); + } + }); +} + +void SpeculativeJIT::compileStringEquality( + Node* node, GPRReg leftGPR, GPRReg rightGPR, GPRReg lengthGPR, GPRReg leftTempGPR, + GPRReg rightTempGPR, GPRReg leftTemp2GPR, GPRReg rightTemp2GPR, + JITCompiler::JumpList fastTrue, JITCompiler::JumpList fastFalse) +{ JITCompiler::JumpList trueCase; JITCompiler::JumpList falseCase; JITCompiler::JumpList slowCase; - DFG_TYPE_CHECK( - JSValueSource::unboxedCell(leftGPR), node->child1(), SpecString, m_jit.branchPtr( - MacroAssembler::NotEqual, - MacroAssembler::Address(leftGPR, JSCell::structureOffset()), - MacroAssembler::TrustedImmPtr(m_jit.vm()->stringStructure.get()))); - - // It's safe to branch around the type check below, since proving that the values are - // equal does indeed prove that the right value is a string. - trueCase.append(m_jit.branchPtr(MacroAssembler::Equal, leftGPR, rightGPR)); - - DFG_TYPE_CHECK( - JSValueSource::unboxedCell(rightGPR), node->child2(), SpecString, m_jit.branchPtr( - MacroAssembler::NotEqual, - MacroAssembler::Address(rightGPR, JSCell::structureOffset()), - MacroAssembler::TrustedImmPtr(m_jit.vm()->stringStructure.get()))); + trueCase.append(fastTrue); + falseCase.append(fastFalse); m_jit.load32(MacroAssembler::Address(leftGPR, JSString::offsetOfLength()), lengthGPR); @@ -3929,31 +4977,217 @@ void SpeculativeJIT::compileStringEquality(Node* node) m_jit.branchTest32(MacroAssembler::NonZero, lengthGPR).linkTo(loop, &m_jit); trueCase.link(&m_jit); -#if USE(JSVALUE64) - m_jit.move(TrustedImm64(ValueTrue), leftTempGPR); -#else - m_jit.move(TrustedImm32(true), leftTempGPR); -#endif + moveTrueTo(leftTempGPR); JITCompiler::Jump done = m_jit.jump(); falseCase.link(&m_jit); -#if USE(JSVALUE64) - m_jit.move(TrustedImm64(ValueFalse), leftTempGPR); -#else - m_jit.move(TrustedImm32(false), leftTempGPR); -#endif + moveFalseTo(leftTempGPR); done.link(&m_jit); addSlowPathGenerator( slowPathCall( slowCase, this, operationCompareStringEq, leftTempGPR, leftGPR, rightGPR)); -#if USE(JSVALUE64) - jsValueResult(leftTempGPR, node, DataFormatJSBoolean); -#else - booleanResult(leftTempGPR, node); -#endif + blessedBooleanResult(leftTempGPR, node); +} + +void SpeculativeJIT::compileStringEquality(Node* node) +{ + SpeculateCellOperand left(this, node->child1()); + SpeculateCellOperand right(this, node->child2()); + GPRTemporary length(this); + GPRTemporary leftTemp(this); + GPRTemporary rightTemp(this); + GPRTemporary leftTemp2(this, Reuse, left); + GPRTemporary rightTemp2(this, Reuse, right); + + GPRReg leftGPR = left.gpr(); + GPRReg rightGPR = right.gpr(); + GPRReg lengthGPR = length.gpr(); + GPRReg leftTempGPR = leftTemp.gpr(); + GPRReg rightTempGPR = rightTemp.gpr(); + GPRReg leftTemp2GPR = leftTemp2.gpr(); + GPRReg rightTemp2GPR = rightTemp2.gpr(); + + speculateString(node->child1(), leftGPR); + + // It's safe to branch around the type check below, since proving that the values are + // equal does indeed prove that the right value is a string. + JITCompiler::Jump fastTrue = m_jit.branchPtr(MacroAssembler::Equal, leftGPR, rightGPR); + + speculateString(node->child2(), rightGPR); + + compileStringEquality( + node, leftGPR, rightGPR, lengthGPR, leftTempGPR, rightTempGPR, leftTemp2GPR, + rightTemp2GPR, fastTrue, JITCompiler::Jump()); +} + +void SpeculativeJIT::compileStringToUntypedEquality(Node* node, Edge stringEdge, Edge untypedEdge) +{ + SpeculateCellOperand left(this, stringEdge); + JSValueOperand right(this, untypedEdge, ManualOperandSpeculation); + GPRTemporary length(this); + GPRTemporary leftTemp(this); + GPRTemporary rightTemp(this); + GPRTemporary leftTemp2(this, Reuse, left); + GPRTemporary rightTemp2(this); + + GPRReg leftGPR = left.gpr(); + JSValueRegs rightRegs = right.jsValueRegs(); + GPRReg lengthGPR = length.gpr(); + GPRReg leftTempGPR = leftTemp.gpr(); + GPRReg rightTempGPR = rightTemp.gpr(); + GPRReg leftTemp2GPR = leftTemp2.gpr(); + GPRReg rightTemp2GPR = rightTemp2.gpr(); + + speculateString(stringEdge, leftGPR); + + JITCompiler::JumpList fastTrue; + JITCompiler::JumpList fastFalse; + + fastFalse.append(m_jit.branchIfNotCell(rightRegs)); + + // It's safe to branch around the type check below, since proving that the values are + // equal does indeed prove that the right value is a string. + fastTrue.append(m_jit.branchPtr( + MacroAssembler::Equal, leftGPR, rightRegs.payloadGPR())); + + fastFalse.append(m_jit.branchIfNotString(rightRegs.payloadGPR())); + + compileStringEquality( + node, leftGPR, rightRegs.payloadGPR(), lengthGPR, leftTempGPR, rightTempGPR, leftTemp2GPR, + rightTemp2GPR, fastTrue, fastFalse); +} + +void SpeculativeJIT::compileStringIdentEquality(Node* node) +{ + SpeculateCellOperand left(this, node->child1()); + SpeculateCellOperand right(this, node->child2()); + GPRTemporary leftTemp(this); + GPRTemporary rightTemp(this); + + GPRReg leftGPR = left.gpr(); + GPRReg rightGPR = right.gpr(); + GPRReg leftTempGPR = leftTemp.gpr(); + GPRReg rightTempGPR = rightTemp.gpr(); + + speculateString(node->child1(), leftGPR); + speculateString(node->child2(), rightGPR); + + speculateStringIdentAndLoadStorage(node->child1(), leftGPR, leftTempGPR); + speculateStringIdentAndLoadStorage(node->child2(), rightGPR, rightTempGPR); + + m_jit.comparePtr(MacroAssembler::Equal, leftTempGPR, rightTempGPR, leftTempGPR); + + unblessedBooleanResult(leftTempGPR, node); +} + +void SpeculativeJIT::compileStringIdentToNotStringVarEquality( + Node* node, Edge stringEdge, Edge notStringVarEdge) +{ + SpeculateCellOperand left(this, stringEdge); + JSValueOperand right(this, notStringVarEdge, ManualOperandSpeculation); + GPRTemporary leftTemp(this); + GPRTemporary rightTemp(this); + GPRReg leftTempGPR = leftTemp.gpr(); + GPRReg rightTempGPR = rightTemp.gpr(); + GPRReg leftGPR = left.gpr(); + JSValueRegs rightRegs = right.jsValueRegs(); + + speculateString(stringEdge, leftGPR); + speculateStringIdentAndLoadStorage(stringEdge, leftGPR, leftTempGPR); + + moveFalseTo(rightTempGPR); + JITCompiler::JumpList notString; + notString.append(m_jit.branchIfNotCell(rightRegs)); + notString.append(m_jit.branchIfNotString(rightRegs.payloadGPR())); + + speculateStringIdentAndLoadStorage(notStringVarEdge, rightRegs.payloadGPR(), rightTempGPR); + + m_jit.comparePtr(MacroAssembler::Equal, leftTempGPR, rightTempGPR, rightTempGPR); + notString.link(&m_jit); + + unblessedBooleanResult(rightTempGPR, node); +} + +void SpeculativeJIT::compileStringZeroLength(Node* node) +{ + SpeculateCellOperand str(this, node->child1()); + GPRReg strGPR = str.gpr(); + + // Make sure that this is a string. + speculateString(node->child1(), strGPR); + + GPRTemporary eq(this); + GPRReg eqGPR = eq.gpr(); + + // Fetch the length field from the string object. + m_jit.test32(MacroAssembler::Zero, MacroAssembler::Address(strGPR, JSString::offsetOfLength()), MacroAssembler::TrustedImm32(-1), eqGPR); + + unblessedBooleanResult(eqGPR, node); +} + +void SpeculativeJIT::compileLogicalNotStringOrOther(Node* node) +{ + JSValueOperand value(this, node->child1(), ManualOperandSpeculation); + GPRTemporary temp(this); + JSValueRegs valueRegs = value.jsValueRegs(); + GPRReg tempGPR = temp.gpr(); + + JITCompiler::Jump notCell = m_jit.branchIfNotCell(valueRegs); + GPRReg cellGPR = valueRegs.payloadGPR(); + DFG_TYPE_CHECK( + valueRegs, node->child1(), (~SpecCell) | SpecString, m_jit.branchIfNotString(cellGPR)); + m_jit.test32( + JITCompiler::Zero, JITCompiler::Address(cellGPR, JSString::offsetOfLength()), + JITCompiler::TrustedImm32(-1), tempGPR); + JITCompiler::Jump done = m_jit.jump(); + notCell.link(&m_jit); + DFG_TYPE_CHECK( + valueRegs, node->child1(), SpecCell | SpecOther, m_jit.branchIfNotOther(valueRegs, tempGPR)); + m_jit.move(TrustedImm32(1), tempGPR); + done.link(&m_jit); + + unblessedBooleanResult(tempGPR, node); +} + +void SpeculativeJIT::emitStringBranch(Edge nodeUse, BasicBlock* taken, BasicBlock* notTaken) +{ + SpeculateCellOperand str(this, nodeUse); + speculateString(nodeUse, str.gpr()); + branchTest32(JITCompiler::NonZero, MacroAssembler::Address(str.gpr(), JSString::offsetOfLength()), taken); + jump(notTaken); + noResult(m_currentNode); +} + +void SpeculativeJIT::emitStringOrOtherBranch(Edge nodeUse, BasicBlock* taken, BasicBlock* notTaken) +{ + JSValueOperand value(this, nodeUse, ManualOperandSpeculation); + GPRTemporary temp(this); + JSValueRegs valueRegs = value.jsValueRegs(); + GPRReg tempGPR = temp.gpr(); + + JITCompiler::Jump notCell = m_jit.branchIfNotCell(valueRegs); + GPRReg cellGPR = valueRegs.payloadGPR(); + DFG_TYPE_CHECK(valueRegs, nodeUse, (~SpecCell) | SpecString, m_jit.branchIfNotString(cellGPR)); + branchTest32( + JITCompiler::Zero, JITCompiler::Address(cellGPR, JSString::offsetOfLength()), + JITCompiler::TrustedImm32(-1), notTaken); + jump(taken, ForceJump); + notCell.link(&m_jit); + DFG_TYPE_CHECK( + valueRegs, nodeUse, SpecCell | SpecOther, m_jit.branchIfNotOther(valueRegs, tempGPR)); + jump(notTaken); + noResult(m_currentNode); +} + +void SpeculativeJIT::compileConstantStoragePointer(Node* node) +{ + GPRTemporary storage(this); + GPRReg storageGPR = storage.gpr(); + m_jit.move(TrustedImmPtr(node->storagePointer()), storageGPR); + storageResult(storageGPR, node); } void SpeculativeJIT::compileGetIndexedPropertyStorage(Node* node) @@ -3964,8 +5198,6 @@ void SpeculativeJIT::compileGetIndexedPropertyStorage(Node* node) GPRTemporary storage(this); GPRReg storageReg = storage.gpr(); - const TypedArrayDescriptor* descriptor = typedArrayDescriptor(node->arrayMode()); - switch (node->arrayMode().type()) { case Array::String: m_jit.loadPtr(MacroAssembler::Address(baseReg, JSString::offsetOfValue()), storageReg); @@ -3979,15 +5211,56 @@ void SpeculativeJIT::compileGetIndexedPropertyStorage(Node* node) break; default: - ASSERT(descriptor); - m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor->m_storageOffset), storageReg); + ASSERT(isTypedView(node->arrayMode().typedArrayType())); + + JITCompiler::Jump fail = m_jit.loadTypedArrayVector(baseReg, storageReg); + + addSlowPathGenerator( + slowPathCall(fail, this, operationGetArrayBufferVector, storageReg, baseReg)); break; } storageResult(storageReg, node); } -void SpeculativeJIT::compileGetByValOnArguments(Node* node) +void SpeculativeJIT::compileGetTypedArrayByteOffset(Node* node) +{ + SpeculateCellOperand base(this, node->child1()); + GPRTemporary vector(this); + GPRTemporary data(this); + + GPRReg baseGPR = base.gpr(); + GPRReg vectorGPR = vector.gpr(); + GPRReg dataGPR = data.gpr(); + + JITCompiler::Jump emptyByteOffset = m_jit.branch32( + MacroAssembler::NotEqual, + MacroAssembler::Address(baseGPR, JSArrayBufferView::offsetOfMode()), + TrustedImm32(WastefulTypedArray)); + + m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), dataGPR); + m_jit.removeSpaceBits(dataGPR); + m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSArrayBufferView::offsetOfVector()), vectorGPR); + JITCompiler::JumpList vectorReady; + vectorReady.append(m_jit.branchIfToSpace(vectorGPR)); + vectorReady.append(m_jit.branchIfNotFastTypedArray(baseGPR)); + m_jit.removeSpaceBits(vectorGPR); + vectorReady.link(&m_jit); + m_jit.loadPtr(MacroAssembler::Address(dataGPR, Butterfly::offsetOfArrayBuffer()), dataGPR); + m_jit.loadPtr(MacroAssembler::Address(dataGPR, ArrayBuffer::offsetOfData()), dataGPR); + m_jit.subPtr(dataGPR, vectorGPR); + + JITCompiler::Jump done = m_jit.jump(); + + emptyByteOffset.link(&m_jit); + m_jit.move(TrustedImmPtr(0), vectorGPR); + + done.link(&m_jit); + + int32Result(vectorGPR, node); +} + +void SpeculativeJIT::compileGetByValOnDirectArguments(Node* node) { SpeculateCellOperand base(this, node->child1()); SpeculateStrictInt32Operand property(this, node->child2()); @@ -3995,261 +5268,995 @@ void SpeculativeJIT::compileGetByValOnArguments(Node* node) #if USE(JSVALUE32_64) GPRTemporary resultTag(this); #endif - GPRTemporary scratch(this); GPRReg baseReg = base.gpr(); GPRReg propertyReg = property.gpr(); GPRReg resultReg = result.gpr(); #if USE(JSVALUE32_64) GPRReg resultTagReg = resultTag.gpr(); + JSValueRegs resultRegs = JSValueRegs(resultTagReg, resultReg); +#else + JSValueRegs resultRegs = JSValueRegs(resultReg); #endif - GPRReg scratchReg = scratch.gpr(); if (!m_compileOkay) return; - - ASSERT(ArrayMode(Array::Arguments).alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1()))); - // Two really lame checks. - speculationCheck( - Uncountable, JSValueSource(), 0, - m_jit.branch32( - MacroAssembler::AboveOrEqual, propertyReg, - MacroAssembler::Address(baseReg, OBJECT_OFFSETOF(Arguments, m_numArguments)))); + ASSERT(ArrayMode(Array::DirectArguments).alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1()))); + speculationCheck( - Uncountable, JSValueSource(), 0, + ExoticObjectMode, JSValueSource(), 0, m_jit.branchTestPtr( MacroAssembler::NonZero, - MacroAssembler::Address( - baseReg, OBJECT_OFFSETOF(Arguments, m_slowArguments)))); - - m_jit.move(propertyReg, resultReg); - m_jit.neg32(resultReg); - m_jit.signExtend32ToPtr(resultReg, resultReg); - m_jit.loadPtr( - MacroAssembler::Address(baseReg, OBJECT_OFFSETOF(Arguments, m_registers)), - scratchReg); + MacroAssembler::Address(baseReg, DirectArguments::offsetOfOverrides()))); + speculationCheck( + ExoticObjectMode, JSValueSource(), 0, + m_jit.branch32( + MacroAssembler::AboveOrEqual, propertyReg, + MacroAssembler::Address(baseReg, DirectArguments::offsetOfLength()))); -#if USE(JSVALUE32_64) - m_jit.load32( - MacroAssembler::BaseIndex( - scratchReg, resultReg, MacroAssembler::TimesEight, - CallFrame::thisArgumentOffset() * sizeof(Register) - sizeof(Register) + - OBJECT_OFFSETOF(JSValue, u.asBits.tag)), - resultTagReg); - m_jit.load32( - MacroAssembler::BaseIndex( - scratchReg, resultReg, MacroAssembler::TimesEight, - CallFrame::thisArgumentOffset() * sizeof(Register) - sizeof(Register) + - OBJECT_OFFSETOF(JSValue, u.asBits.payload)), - resultReg); - jsValueResult(resultTagReg, resultReg, node); -#else - m_jit.load64( + m_jit.loadValue( MacroAssembler::BaseIndex( - scratchReg, resultReg, MacroAssembler::TimesEight, - CallFrame::thisArgumentOffset() * sizeof(Register) - sizeof(Register)), - resultReg); - jsValueResult(resultReg, node); -#endif + baseReg, propertyReg, MacroAssembler::TimesEight, DirectArguments::storageOffset()), + resultRegs); + + jsValueResult(resultRegs, node); } -void SpeculativeJIT::compileGetArgumentsLength(Node* node) +void SpeculativeJIT::compileGetByValOnScopedArguments(Node* node) { SpeculateCellOperand base(this, node->child1()); - GPRTemporary result(this, base); + SpeculateStrictInt32Operand property(this, node->child2()); + GPRTemporary result(this); +#if USE(JSVALUE32_64) + GPRTemporary resultTag(this); +#endif + GPRTemporary scratch(this); + GPRTemporary scratch2(this); GPRReg baseReg = base.gpr(); + GPRReg propertyReg = property.gpr(); GPRReg resultReg = result.gpr(); +#if USE(JSVALUE32_64) + GPRReg resultTagReg = resultTag.gpr(); + JSValueRegs resultRegs = JSValueRegs(resultTagReg, resultReg); +#else + JSValueRegs resultRegs = JSValueRegs(resultReg); +#endif + GPRReg scratchReg = scratch.gpr(); + GPRReg scratch2Reg = scratch2.gpr(); if (!m_compileOkay) return; - ASSERT(ArrayMode(Array::Arguments).alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1()))); + ASSERT(ArrayMode(Array::ScopedArguments).alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1()))); speculationCheck( - Uncountable, JSValueSource(), 0, - m_jit.branchTest8( - MacroAssembler::NonZero, - MacroAssembler::Address(baseReg, OBJECT_OFFSETOF(Arguments, m_overrodeLength)))); + ExoticObjectMode, JSValueSource(), nullptr, + m_jit.branch32( + MacroAssembler::AboveOrEqual, propertyReg, + MacroAssembler::Address(baseReg, ScopedArguments::offsetOfTotalLength()))); + + m_jit.loadPtr(MacroAssembler::Address(baseReg, ScopedArguments::offsetOfTable()), scratchReg); + m_jit.load32( + MacroAssembler::Address(scratchReg, ScopedArgumentsTable::offsetOfLength()), scratch2Reg); + + MacroAssembler::Jump overflowArgument = m_jit.branch32( + MacroAssembler::AboveOrEqual, propertyReg, scratch2Reg); + m_jit.loadPtr(MacroAssembler::Address(baseReg, ScopedArguments::offsetOfScope()), scratch2Reg); + + m_jit.loadPtr( + MacroAssembler::Address(scratchReg, ScopedArgumentsTable::offsetOfArguments()), + scratchReg); m_jit.load32( - MacroAssembler::Address(baseReg, OBJECT_OFFSETOF(Arguments, m_numArguments)), - resultReg); - integerResult(resultReg, node); + MacroAssembler::BaseIndex(scratchReg, propertyReg, MacroAssembler::TimesFour), + scratchReg); + + speculationCheck( + ExoticObjectMode, JSValueSource(), nullptr, + m_jit.branch32( + MacroAssembler::Equal, scratchReg, TrustedImm32(ScopeOffset::invalidOffset))); + + m_jit.loadValue( + MacroAssembler::BaseIndex( + scratch2Reg, propertyReg, MacroAssembler::TimesEight, + JSEnvironmentRecord::offsetOfVariables()), + resultRegs); + + MacroAssembler::Jump done = m_jit.jump(); + overflowArgument.link(&m_jit); + + m_jit.sub32(propertyReg, scratch2Reg); + m_jit.neg32(scratch2Reg); + + m_jit.loadValue( + MacroAssembler::BaseIndex( + baseReg, scratch2Reg, MacroAssembler::TimesEight, + ScopedArguments::overflowStorageOffset()), + resultRegs); + speculationCheck(ExoticObjectMode, JSValueSource(), nullptr, m_jit.branchIfEmpty(resultRegs)); + + done.link(&m_jit); + + jsValueResult(resultRegs, node); } -void SpeculativeJIT::compileGetArrayLength(Node* node) +void SpeculativeJIT::compileGetScope(Node* node) { - const TypedArrayDescriptor* descriptor = typedArrayDescriptor(node->arrayMode()); + SpeculateCellOperand function(this, node->child1()); + GPRTemporary result(this, Reuse, function); + m_jit.loadPtr(JITCompiler::Address(function.gpr(), JSFunction::offsetOfScopeChain()), result.gpr()); + cellResult(result.gpr(), node); +} + +void SpeculativeJIT::compileSkipScope(Node* node) +{ + SpeculateCellOperand scope(this, node->child1()); + GPRTemporary result(this, Reuse, scope); + m_jit.loadPtr(JITCompiler::Address(scope.gpr(), JSScope::offsetOfNext()), result.gpr()); + cellResult(result.gpr(), node); +} +void SpeculativeJIT::compileGetArrayLength(Node* node) +{ switch (node->arrayMode().type()) { case Array::Int32: case Array::Double: case Array::Contiguous: { StorageOperand storage(this, node->child2()); - GPRTemporary result(this, storage); + GPRTemporary result(this, Reuse, storage); GPRReg storageReg = storage.gpr(); GPRReg resultReg = result.gpr(); m_jit.load32(MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()), resultReg); - integerResult(resultReg, node); + int32Result(resultReg, node); break; } case Array::ArrayStorage: case Array::SlowPutArrayStorage: { StorageOperand storage(this, node->child2()); - GPRTemporary result(this, storage); + GPRTemporary result(this, Reuse, storage); GPRReg storageReg = storage.gpr(); GPRReg resultReg = result.gpr(); m_jit.load32(MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()), resultReg); speculationCheck(Uncountable, JSValueRegs(), 0, m_jit.branch32(MacroAssembler::LessThan, resultReg, MacroAssembler::TrustedImm32(0))); - integerResult(resultReg, node); + int32Result(resultReg, node); break; } case Array::String: { SpeculateCellOperand base(this, node->child1()); - GPRTemporary result(this, base); + GPRTemporary result(this, Reuse, base); GPRReg baseGPR = base.gpr(); GPRReg resultGPR = result.gpr(); m_jit.load32(MacroAssembler::Address(baseGPR, JSString::offsetOfLength()), resultGPR); - integerResult(resultGPR, node); + int32Result(resultGPR, node); break; } - case Array::Arguments: { - compileGetArgumentsLength(node); + case Array::DirectArguments: { + SpeculateCellOperand base(this, node->child1()); + GPRTemporary result(this, Reuse, base); + + GPRReg baseReg = base.gpr(); + GPRReg resultReg = result.gpr(); + + if (!m_compileOkay) + return; + + ASSERT(ArrayMode(Array::DirectArguments).alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1()))); + + speculationCheck( + ExoticObjectMode, JSValueSource(), 0, + m_jit.branchTestPtr( + MacroAssembler::NonZero, + MacroAssembler::Address(baseReg, DirectArguments::offsetOfOverrides()))); + + m_jit.load32( + MacroAssembler::Address(baseReg, DirectArguments::offsetOfLength()), resultReg); + + int32Result(resultReg, node); break; } - default: + case Array::ScopedArguments: { SpeculateCellOperand base(this, node->child1()); - GPRTemporary result(this, base); + GPRTemporary result(this, Reuse, base); + + GPRReg baseReg = base.gpr(); + GPRReg resultReg = result.gpr(); + + if (!m_compileOkay) + return; + + ASSERT(ArrayMode(Array::ScopedArguments).alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1()))); + + speculationCheck( + ExoticObjectMode, JSValueSource(), 0, + m_jit.branchTest8( + MacroAssembler::NonZero, + MacroAssembler::Address(baseReg, ScopedArguments::offsetOfOverrodeThings()))); + + m_jit.load32( + MacroAssembler::Address(baseReg, ScopedArguments::offsetOfTotalLength()), resultReg); + + int32Result(resultReg, node); + break; + } + default: { + ASSERT(node->arrayMode().isSomeTypedArrayView()); + SpeculateCellOperand base(this, node->child1()); + GPRTemporary result(this, Reuse, base); GPRReg baseGPR = base.gpr(); GPRReg resultGPR = result.gpr(); - ASSERT(descriptor); - m_jit.load32(MacroAssembler::Address(baseGPR, descriptor->m_lengthOffset), resultGPR); - integerResult(resultGPR, node); + m_jit.load32(MacroAssembler::Address(baseGPR, JSArrayBufferView::offsetOfLength()), resultGPR); + int32Result(resultGPR, node); break; + } } +} + +void SpeculativeJIT::compileCheckIdent(Node* node) +{ + SpeculateCellOperand operand(this, node->child1()); + UniquedStringImpl* uid = node->uidOperand(); + if (uid->isSymbol()) { + speculateSymbol(node->child1(), operand.gpr()); + speculationCheck( + BadIdent, JSValueSource(), nullptr, + m_jit.branchPtr( + JITCompiler::NotEqual, + JITCompiler::Address(operand.gpr(), Symbol::offsetOfPrivateName()), + TrustedImmPtr(uid))); + } else { + speculateString(node->child1(), operand.gpr()); + speculateStringIdent(node->child1(), operand.gpr()); + speculationCheck( + BadIdent, JSValueSource(), nullptr, + m_jit.branchPtr( + JITCompiler::NotEqual, + JITCompiler::Address(operand.gpr(), JSString::offsetOfValue()), + TrustedImmPtr(uid))); + } + noResult(node); +} + +template <typename ClassType> void SpeculativeJIT::compileNewFunctionCommon(GPRReg resultGPR, Structure* structure, GPRReg scratch1GPR, GPRReg scratch2GPR, GPRReg scopeGPR, MacroAssembler::JumpList& slowPath, size_t size, FunctionExecutable* executable, ptrdiff_t offsetOfScopeChain, ptrdiff_t offsetOfExecutable, ptrdiff_t offsetOfRareData) +{ + emitAllocateJSObjectWithKnownSize<ClassType>(resultGPR, TrustedImmPtr(structure), TrustedImmPtr(0), scratch1GPR, scratch2GPR, slowPath, size); + + m_jit.storePtr(scopeGPR, JITCompiler::Address(resultGPR, offsetOfScopeChain)); + m_jit.storePtr(TrustedImmPtr(executable), JITCompiler::Address(resultGPR, offsetOfExecutable)); + m_jit.storePtr(TrustedImmPtr(0), JITCompiler::Address(resultGPR, offsetOfRareData)); +} + +void SpeculativeJIT::compileNewFunction(Node* node) +{ + NodeType nodeType = node->op(); + ASSERT(nodeType == NewFunction || nodeType == NewArrowFunction || nodeType == NewGeneratorFunction); + + SpeculateCellOperand scope(this, node->child1()); + GPRReg scopeGPR = scope.gpr(); + + FunctionExecutable* executable = node->castOperand<FunctionExecutable*>(); + + if (executable->singletonFunction()->isStillValid()) { + GPRFlushedCallResult result(this); + GPRReg resultGPR = result.gpr(); + + flushRegisters(); + + if (nodeType == NewGeneratorFunction) + callOperation(operationNewGeneratorFunction, resultGPR, scopeGPR, executable); + else + callOperation(operationNewFunction, resultGPR, scopeGPR, executable); + m_jit.exceptionCheck(); + cellResult(resultGPR, node); + return; + } + + Structure* structure = + nodeType == NewGeneratorFunction ? m_jit.graph().globalObjectFor(node->origin.semantic)->generatorFunctionStructure() : + m_jit.graph().globalObjectFor(node->origin.semantic)->functionStructure(); + + GPRTemporary result(this); + GPRTemporary scratch1(this); + GPRTemporary scratch2(this); + + GPRReg resultGPR = result.gpr(); + GPRReg scratch1GPR = scratch1.gpr(); + GPRReg scratch2GPR = scratch2.gpr(); + + JITCompiler::JumpList slowPath; + + if (nodeType == NewFunction || nodeType == NewArrowFunction) { + compileNewFunctionCommon<JSFunction>(resultGPR, structure, scratch1GPR, scratch2GPR, scopeGPR, slowPath, JSFunction::allocationSize(0), executable, JSFunction::offsetOfScopeChain(), JSFunction::offsetOfExecutable(), JSFunction::offsetOfRareData()); + + addSlowPathGenerator(slowPathCall(slowPath, this, operationNewFunctionWithInvalidatedReallocationWatchpoint, resultGPR, scopeGPR, executable)); + } + + if (nodeType == NewGeneratorFunction) { + compileNewFunctionCommon<JSGeneratorFunction>(resultGPR, structure, scratch1GPR, scratch2GPR, scopeGPR, slowPath, JSGeneratorFunction::allocationSize(0), executable, JSGeneratorFunction::offsetOfScopeChain(), JSGeneratorFunction::offsetOfExecutable(), JSGeneratorFunction::offsetOfRareData()); + + addSlowPathGenerator(slowPathCall(slowPath, this, operationNewGeneratorFunctionWithInvalidatedReallocationWatchpoint, resultGPR, scopeGPR, executable)); + } + + cellResult(resultGPR, node); +} + +void SpeculativeJIT::compileForwardVarargs(Node* node) +{ + LoadVarargsData* data = node->loadVarargsData(); + InlineCallFrame* inlineCallFrame = node->child1()->origin.semantic.inlineCallFrame; + + GPRTemporary length(this); + JSValueRegsTemporary temp(this); + GPRReg lengthGPR = length.gpr(); + JSValueRegs tempRegs = temp.regs(); + + emitGetLength(inlineCallFrame, lengthGPR, /* includeThis = */ true); + if (data->offset) + m_jit.sub32(TrustedImm32(data->offset), lengthGPR); + + speculationCheck( + VarargsOverflow, JSValueSource(), Edge(), m_jit.branch32( + MacroAssembler::Above, + lengthGPR, TrustedImm32(data->limit))); + + m_jit.store32(lengthGPR, JITCompiler::payloadFor(data->machineCount)); + + VirtualRegister sourceStart = JITCompiler::argumentsStart(inlineCallFrame) + data->offset; + VirtualRegister targetStart = data->machineStart; + + m_jit.sub32(TrustedImm32(1), lengthGPR); + + // First have a loop that fills in the undefined slots in case of an arity check failure. + m_jit.move(TrustedImm32(data->mandatoryMinimum), tempRegs.payloadGPR()); + JITCompiler::Jump done = m_jit.branch32(JITCompiler::BelowOrEqual, tempRegs.payloadGPR(), lengthGPR); + + JITCompiler::Label loop = m_jit.label(); + m_jit.sub32(TrustedImm32(1), tempRegs.payloadGPR()); + m_jit.storeTrustedValue( + jsUndefined(), + JITCompiler::BaseIndex( + GPRInfo::callFrameRegister, tempRegs.payloadGPR(), JITCompiler::TimesEight, + targetStart.offset() * sizeof(EncodedJSValue))); + m_jit.branch32(JITCompiler::Above, tempRegs.payloadGPR(), lengthGPR).linkTo(loop, &m_jit); + done.link(&m_jit); + + // And then fill in the actual argument values. + done = m_jit.branchTest32(JITCompiler::Zero, lengthGPR); + + loop = m_jit.label(); + m_jit.sub32(TrustedImm32(1), lengthGPR); + m_jit.loadValue( + JITCompiler::BaseIndex( + GPRInfo::callFrameRegister, lengthGPR, JITCompiler::TimesEight, + sourceStart.offset() * sizeof(EncodedJSValue)), + tempRegs); + m_jit.storeValue( + tempRegs, + JITCompiler::BaseIndex( + GPRInfo::callFrameRegister, lengthGPR, JITCompiler::TimesEight, + targetStart.offset() * sizeof(EncodedJSValue))); + m_jit.branchTest32(JITCompiler::NonZero, lengthGPR).linkTo(loop, &m_jit); + + done.link(&m_jit); + + noResult(node); +} + +void SpeculativeJIT::compileCreateActivation(Node* node) +{ + SymbolTable* table = node->castOperand<SymbolTable*>(); + Structure* structure = m_jit.graph().globalObjectFor( + node->origin.semantic)->activationStructure(); + + SpeculateCellOperand scope(this, node->child1()); + GPRReg scopeGPR = scope.gpr(); + JSValue initializationValue = node->initializationValueForActivation(); + ASSERT(initializationValue == jsUndefined() || initializationValue == jsTDZValue()); + + if (table->singletonScope()->isStillValid()) { + GPRFlushedCallResult result(this); + GPRReg resultGPR = result.gpr(); + + flushRegisters(); + +#if USE(JSVALUE64) + callOperation(operationCreateActivationDirect, + resultGPR, structure, scopeGPR, table, TrustedImm64(JSValue::encode(initializationValue))); +#else + callOperation(operationCreateActivationDirect, + resultGPR, structure, scopeGPR, table, TrustedImm32(initializationValue.tag()), TrustedImm32(initializationValue.payload())); +#endif + m_jit.exceptionCheck(); + cellResult(resultGPR, node); + return; + } + + GPRTemporary result(this); + GPRTemporary scratch1(this); + GPRTemporary scratch2(this); + GPRReg resultGPR = result.gpr(); + GPRReg scratch1GPR = scratch1.gpr(); + GPRReg scratch2GPR = scratch2.gpr(); + + JITCompiler::JumpList slowPath; + emitAllocateJSObjectWithKnownSize<JSLexicalEnvironment>( + resultGPR, TrustedImmPtr(structure), TrustedImmPtr(0), scratch1GPR, scratch2GPR, + slowPath, JSLexicalEnvironment::allocationSize(table)); + + // Don't need a memory barriers since we just fast-created the activation, so the + // activation must be young. + m_jit.storePtr(scopeGPR, JITCompiler::Address(resultGPR, JSScope::offsetOfNext())); + m_jit.storePtr( + TrustedImmPtr(table), + JITCompiler::Address(resultGPR, JSLexicalEnvironment::offsetOfSymbolTable())); + + // Must initialize all members to undefined or the TDZ empty value. + for (unsigned i = 0; i < table->scopeSize(); ++i) { + m_jit.storeTrustedValue( + initializationValue, + JITCompiler::Address( + resultGPR, JSLexicalEnvironment::offsetOfVariable(ScopeOffset(i)))); } + +#if USE(JSVALUE64) + addSlowPathGenerator( + slowPathCall( + slowPath, this, operationCreateActivationDirect, resultGPR, structure, scopeGPR, table, TrustedImm64(JSValue::encode(initializationValue)))); +#else + addSlowPathGenerator( + slowPathCall( + slowPath, this, operationCreateActivationDirect, resultGPR, structure, scopeGPR, table, TrustedImm32(initializationValue.tag()), TrustedImm32(initializationValue.payload()))); +#endif + + cellResult(resultGPR, node); } -void SpeculativeJIT::compileNewFunctionNoCheck(Node* node) +void SpeculativeJIT::compileCreateDirectArguments(Node* node) { - GPRResult result(this); + // FIXME: A more effective way of dealing with the argument count and callee is to have + // them be explicit arguments to this node. + // https://bugs.webkit.org/show_bug.cgi?id=142207 + + GPRTemporary result(this); + GPRTemporary scratch1(this); + GPRTemporary scratch2(this); + GPRTemporary length; + GPRReg resultGPR = result.gpr(); + GPRReg scratch1GPR = scratch1.gpr(); + GPRReg scratch2GPR = scratch2.gpr(); + GPRReg lengthGPR = InvalidGPRReg; + JSValueRegs valueRegs = JSValueRegs::withTwoAvailableRegs(scratch1GPR, scratch2GPR); + + unsigned minCapacity = m_jit.graph().baselineCodeBlockFor(node->origin.semantic)->numParameters() - 1; + + unsigned knownLength; + bool lengthIsKnown; // if false, lengthGPR will have the length. + if (node->origin.semantic.inlineCallFrame + && !node->origin.semantic.inlineCallFrame->isVarargs()) { + knownLength = node->origin.semantic.inlineCallFrame->arguments.size() - 1; + lengthIsKnown = true; + } else { + knownLength = UINT_MAX; + lengthIsKnown = false; + + GPRTemporary realLength(this); + length.adopt(realLength); + lengthGPR = length.gpr(); + + VirtualRegister argumentCountRegister; + if (!node->origin.semantic.inlineCallFrame) + argumentCountRegister = VirtualRegister(JSStack::ArgumentCount); + else + argumentCountRegister = node->origin.semantic.inlineCallFrame->argumentCountRegister; + m_jit.load32(JITCompiler::payloadFor(argumentCountRegister), lengthGPR); + m_jit.sub32(TrustedImm32(1), lengthGPR); + } + + Structure* structure = + m_jit.graph().globalObjectFor(node->origin.semantic)->directArgumentsStructure(); + + // Use a different strategy for allocating the object depending on whether we know its + // size statically. + JITCompiler::JumpList slowPath; + if (lengthIsKnown) { + emitAllocateJSObjectWithKnownSize<DirectArguments>( + resultGPR, TrustedImmPtr(structure), TrustedImmPtr(0), scratch1GPR, scratch2GPR, + slowPath, DirectArguments::allocationSize(std::max(knownLength, minCapacity))); + + m_jit.store32( + TrustedImm32(knownLength), + JITCompiler::Address(resultGPR, DirectArguments::offsetOfLength())); + } else { + JITCompiler::Jump tooFewArguments; + if (minCapacity) { + tooFewArguments = + m_jit.branch32(JITCompiler::Below, lengthGPR, TrustedImm32(minCapacity)); + } + m_jit.lshift32(lengthGPR, TrustedImm32(3), scratch1GPR); + m_jit.add32(TrustedImm32(DirectArguments::storageOffset()), scratch1GPR); + if (minCapacity) { + JITCompiler::Jump done = m_jit.jump(); + tooFewArguments.link(&m_jit); + m_jit.move(TrustedImm32(DirectArguments::allocationSize(minCapacity)), scratch1GPR); + done.link(&m_jit); + } + + emitAllocateVariableSizedJSObject<DirectArguments>( + resultGPR, TrustedImmPtr(structure), scratch1GPR, scratch1GPR, scratch2GPR, + slowPath); + + m_jit.store32( + lengthGPR, JITCompiler::Address(resultGPR, DirectArguments::offsetOfLength())); + } + + m_jit.store32( + TrustedImm32(minCapacity), + JITCompiler::Address(resultGPR, DirectArguments::offsetOfMinCapacity())); + + m_jit.storePtr( + TrustedImmPtr(0), JITCompiler::Address(resultGPR, DirectArguments::offsetOfOverrides())); + + if (lengthIsKnown) { + addSlowPathGenerator( + slowPathCall( + slowPath, this, operationCreateDirectArguments, resultGPR, structure, + knownLength, minCapacity)); + } else { + auto generator = std::make_unique<CallCreateDirectArgumentsSlowPathGenerator>( + slowPath, this, resultGPR, structure, lengthGPR, minCapacity); + addSlowPathGenerator(WTFMove(generator)); + } + + if (node->origin.semantic.inlineCallFrame) { + if (node->origin.semantic.inlineCallFrame->isClosureCall) { + m_jit.loadPtr( + JITCompiler::addressFor( + node->origin.semantic.inlineCallFrame->calleeRecovery.virtualRegister()), + scratch1GPR); + } else { + m_jit.move( + TrustedImmPtr( + node->origin.semantic.inlineCallFrame->calleeRecovery.constant().asCell()), + scratch1GPR); + } + } else + m_jit.loadPtr(JITCompiler::addressFor(JSStack::Callee), scratch1GPR); + + // Don't need a memory barriers since we just fast-created the activation, so the + // activation must be young. + m_jit.storePtr( + scratch1GPR, JITCompiler::Address(resultGPR, DirectArguments::offsetOfCallee())); + + VirtualRegister start = m_jit.argumentsStart(node->origin.semantic); + if (lengthIsKnown) { + for (unsigned i = 0; i < std::max(knownLength, minCapacity); ++i) { + m_jit.loadValue(JITCompiler::addressFor(start + i), valueRegs); + m_jit.storeValue( + valueRegs, JITCompiler::Address(resultGPR, DirectArguments::offsetOfSlot(i))); + } + } else { + JITCompiler::Jump done; + if (minCapacity) { + JITCompiler::Jump startLoop = m_jit.branch32( + JITCompiler::AboveOrEqual, lengthGPR, TrustedImm32(minCapacity)); + m_jit.move(TrustedImm32(minCapacity), lengthGPR); + startLoop.link(&m_jit); + } else + done = m_jit.branchTest32(MacroAssembler::Zero, lengthGPR); + JITCompiler::Label loop = m_jit.label(); + m_jit.sub32(TrustedImm32(1), lengthGPR); + m_jit.loadValue( + JITCompiler::BaseIndex( + GPRInfo::callFrameRegister, lengthGPR, JITCompiler::TimesEight, + start.offset() * static_cast<int>(sizeof(Register))), + valueRegs); + m_jit.storeValue( + valueRegs, + JITCompiler::BaseIndex( + resultGPR, lengthGPR, JITCompiler::TimesEight, + DirectArguments::storageOffset())); + m_jit.branchTest32(MacroAssembler::NonZero, lengthGPR).linkTo(loop, &m_jit); + if (done.isSet()) + done.link(&m_jit); + } + + cellResult(resultGPR, node); +} + +void SpeculativeJIT::compileGetFromArguments(Node* node) +{ + SpeculateCellOperand arguments(this, node->child1()); + JSValueRegsTemporary result(this); + + GPRReg argumentsGPR = arguments.gpr(); + JSValueRegs resultRegs = result.regs(); + + m_jit.loadValue(JITCompiler::Address(argumentsGPR, DirectArguments::offsetOfSlot(node->capturedArgumentsOffset().offset())), resultRegs); + jsValueResult(resultRegs, node); +} + +void SpeculativeJIT::compilePutToArguments(Node* node) +{ + SpeculateCellOperand arguments(this, node->child1()); + JSValueOperand value(this, node->child2()); + + GPRReg argumentsGPR = arguments.gpr(); + JSValueRegs valueRegs = value.jsValueRegs(); + + m_jit.storeValue(valueRegs, JITCompiler::Address(argumentsGPR, DirectArguments::offsetOfSlot(node->capturedArgumentsOffset().offset()))); + noResult(node); +} + +void SpeculativeJIT::compileCreateScopedArguments(Node* node) +{ + SpeculateCellOperand scope(this, node->child1()); + GPRReg scopeGPR = scope.gpr(); + + GPRFlushedCallResult result(this); GPRReg resultGPR = result.gpr(); flushRegisters(); - callOperation( - operationNewFunctionNoCheck, resultGPR, m_jit.codeBlock()->functionDecl(node->functionDeclIndex())); + + // We set up the arguments ourselves, because we have the whole register file and we can + // set them up directly into the argument registers. This also means that we don't have to + // invent a four-argument-register shuffle. + + // Arguments: 0:exec, 1:structure, 2:start, 3:length, 4:callee, 5:scope + + // Do the scopeGPR first, since it might alias an argument register. + m_jit.setupArgument(5, [&] (GPRReg destGPR) { m_jit.move(scopeGPR, destGPR); }); + + // These other things could be done in any order. + m_jit.setupArgument(4, [&] (GPRReg destGPR) { emitGetCallee(node->origin.semantic, destGPR); }); + m_jit.setupArgument(3, [&] (GPRReg destGPR) { emitGetLength(node->origin.semantic, destGPR); }); + m_jit.setupArgument(2, [&] (GPRReg destGPR) { emitGetArgumentStart(node->origin.semantic, destGPR); }); + m_jit.setupArgument( + 1, [&] (GPRReg destGPR) { + m_jit.move( + TrustedImmPtr(m_jit.globalObjectFor(node->origin.semantic)->scopedArgumentsStructure()), + destGPR); + }); + m_jit.setupArgument(0, [&] (GPRReg destGPR) { m_jit.move(GPRInfo::callFrameRegister, destGPR); }); + + appendCallSetResult(operationCreateScopedArguments, resultGPR); + m_jit.exceptionCheck(); + cellResult(resultGPR, node); } -void SpeculativeJIT::compileNewFunctionExpression(Node* node) +void SpeculativeJIT::compileCreateClonedArguments(Node* node) { - GPRResult result(this); + GPRFlushedCallResult result(this); GPRReg resultGPR = result.gpr(); flushRegisters(); - callOperation( - operationNewFunctionExpression, - resultGPR, - m_jit.codeBlock()->functionExpr(node->functionExprIndex())); + + // We set up the arguments ourselves, because we have the whole register file and we can + // set them up directly into the argument registers. + + // Arguments: 0:exec, 1:structure, 2:start, 3:length, 4:callee + m_jit.setupArgument(4, [&] (GPRReg destGPR) { emitGetCallee(node->origin.semantic, destGPR); }); + m_jit.setupArgument(3, [&] (GPRReg destGPR) { emitGetLength(node->origin.semantic, destGPR); }); + m_jit.setupArgument(2, [&] (GPRReg destGPR) { emitGetArgumentStart(node->origin.semantic, destGPR); }); + m_jit.setupArgument( + 1, [&] (GPRReg destGPR) { + m_jit.move( + TrustedImmPtr( + m_jit.globalObjectFor(node->origin.semantic)->outOfBandArgumentsStructure()), + destGPR); + }); + m_jit.setupArgument(0, [&] (GPRReg destGPR) { m_jit.move(GPRInfo::callFrameRegister, destGPR); }); + + appendCallSetResult(operationCreateClonedArguments, resultGPR); + m_jit.exceptionCheck(); + cellResult(resultGPR, node); } -bool SpeculativeJIT::compileRegExpExec(Node* node) +void SpeculativeJIT::compileCopyRest(Node* node) { - unsigned branchIndexInBlock = detectPeepHoleBranch(); - if (branchIndexInBlock == UINT_MAX) - return false; - Node* branchNode = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); - ASSERT(node->adjustedRefCount() == 1); + ASSERT(node->op() == CopyRest); + + SpeculateCellOperand array(this, node->child1()); + GPRTemporary argumentsStart(this); + SpeculateStrictInt32Operand arrayLength(this, node->child2()); + + GPRReg arrayGPR = array.gpr(); + GPRReg argumentsStartGPR = argumentsStart.gpr(); + GPRReg arrayLengthGPR = arrayLength.gpr(); - BlockIndex taken = branchNode->takenBlockIndex(); - BlockIndex notTaken = branchNode->notTakenBlockIndex(); + CCallHelpers::Jump done = m_jit.branch32(MacroAssembler::Equal, arrayLengthGPR, TrustedImm32(0)); + + emitGetArgumentStart(node->origin.semantic, argumentsStartGPR); + silentSpillAllRegisters(argumentsStartGPR); + // Arguments: 0:exec, 1:JSCell* array, 2:arguments start, 3:number of arguments to skip, 4:array length + callOperation(operationCopyRest, arrayGPR, argumentsStartGPR, Imm32(node->numberOfArgumentsToSkip()), arrayLengthGPR); + silentFillAllRegisters(argumentsStartGPR); + m_jit.exceptionCheck(); + + done.link(&m_jit); + + noResult(node); +} + +void SpeculativeJIT::compileGetRestLength(Node* node) +{ + ASSERT(node->op() == GetRestLength); + + GPRTemporary result(this); + GPRReg resultGPR = result.gpr(); + + emitGetLength(node->origin.semantic, resultGPR); + CCallHelpers::Jump hasNonZeroLength = m_jit.branch32(MacroAssembler::Above, resultGPR, Imm32(node->numberOfArgumentsToSkip())); + m_jit.move(TrustedImm32(0), resultGPR); + CCallHelpers::Jump done = m_jit.jump(); + hasNonZeroLength.link(&m_jit); + if (node->numberOfArgumentsToSkip()) + m_jit.sub32(TrustedImm32(node->numberOfArgumentsToSkip()), resultGPR); + done.link(&m_jit); + int32Result(resultGPR, node); +} + +void SpeculativeJIT::compileNotifyWrite(Node* node) +{ + WatchpointSet* set = node->watchpointSet(); - bool invert = false; - if (taken == nextBlock()) { - invert = true; - BlockIndex tmp = taken; - taken = notTaken; - notTaken = tmp; - } + JITCompiler::Jump slowCase = m_jit.branch8( + JITCompiler::NotEqual, + JITCompiler::AbsoluteAddress(set->addressOfState()), + TrustedImm32(IsInvalidated)); + + addSlowPathGenerator( + slowPathCall(slowCase, this, operationNotifyWrite, NoResult, set, NeedToSpill, ExceptionCheckRequirement::CheckNotNeeded)); + + noResult(node); +} - SpeculateCellOperand base(this, node->child1()); - SpeculateCellOperand argument(this, node->child2()); - GPRReg baseGPR = base.gpr(); - GPRReg argumentGPR = argument.gpr(); +void SpeculativeJIT::compileIsObjectOrNull(Node* node) +{ + JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic); - flushRegisters(); - GPRResult result(this); - callOperation(operationRegExpTest, result.gpr(), baseGPR, argumentGPR); + JSValueOperand value(this, node->child1()); + JSValueRegs valueRegs = value.jsValueRegs(); + + GPRTemporary result(this); + GPRReg resultGPR = result.gpr(); + + JITCompiler::Jump isCell = m_jit.branchIfCell(valueRegs); + + JITCompiler::Jump isNull = m_jit.branchIfEqual(valueRegs, jsNull()); + JITCompiler::Jump isNonNullNonCell = m_jit.jump(); + + isCell.link(&m_jit); + JITCompiler::Jump isFunction = m_jit.branchIfFunction(valueRegs.payloadGPR()); + JITCompiler::Jump notObject = m_jit.branchIfNotObject(valueRegs.payloadGPR()); + + JITCompiler::Jump slowPath = m_jit.branchTest8( + JITCompiler::NonZero, + JITCompiler::Address(valueRegs.payloadGPR(), JSCell::typeInfoFlagsOffset()), + TrustedImm32(MasqueradesAsUndefined | TypeOfShouldCallGetCallData)); + + isNull.link(&m_jit); + m_jit.move(TrustedImm32(1), resultGPR); + JITCompiler::Jump done = m_jit.jump(); + + isNonNullNonCell.link(&m_jit); + isFunction.link(&m_jit); + notObject.link(&m_jit); + m_jit.move(TrustedImm32(0), resultGPR); + + addSlowPathGenerator( + slowPathCall( + slowPath, this, operationObjectIsObject, resultGPR, globalObject, + valueRegs.payloadGPR())); + + done.link(&m_jit); + + unblessedBooleanResult(resultGPR, node); +} - branchTest32(invert ? JITCompiler::Zero : JITCompiler::NonZero, result.gpr(), taken); - jump(notTaken); +void SpeculativeJIT::compileIsFunction(Node* node) +{ + JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic); + + JSValueOperand value(this, node->child1()); + JSValueRegs valueRegs = value.jsValueRegs(); + + GPRTemporary result(this); + GPRReg resultGPR = result.gpr(); + + JITCompiler::Jump notCell = m_jit.branchIfNotCell(valueRegs); + JITCompiler::Jump isFunction = m_jit.branchIfFunction(valueRegs.payloadGPR()); + JITCompiler::Jump notObject = m_jit.branchIfNotObject(valueRegs.payloadGPR()); + + JITCompiler::Jump slowPath = m_jit.branchTest8( + JITCompiler::NonZero, + JITCompiler::Address(valueRegs.payloadGPR(), JSCell::typeInfoFlagsOffset()), + TrustedImm32(MasqueradesAsUndefined | TypeOfShouldCallGetCallData)); + + notCell.link(&m_jit); + notObject.link(&m_jit); + m_jit.move(TrustedImm32(0), resultGPR); + JITCompiler::Jump done = m_jit.jump(); + + isFunction.link(&m_jit); + m_jit.move(TrustedImm32(1), resultGPR); + + addSlowPathGenerator( + slowPathCall( + slowPath, this, operationObjectIsFunction, resultGPR, globalObject, + valueRegs.payloadGPR())); + + done.link(&m_jit); + + unblessedBooleanResult(resultGPR, node); +} - use(node->child1()); - use(node->child2()); - m_indexInBlock = branchIndexInBlock; - m_currentNode = branchNode; +void SpeculativeJIT::compileTypeOf(Node* node) +{ + JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic); + + JSValueOperand value(this, node->child1()); + JSValueRegs valueRegs = value.jsValueRegs(); + + GPRTemporary result(this); + GPRReg resultGPR = result.gpr(); + + JITCompiler::JumpList done; + JITCompiler::Jump slowPath; + m_jit.emitTypeOf( + valueRegs, resultGPR, + [&] (TypeofType type, bool fallsThrough) { + m_jit.move(TrustedImmPtr(m_jit.vm()->smallStrings.typeString(type)), resultGPR); + if (!fallsThrough) + done.append(m_jit.jump()); + }, + [&] (JITCompiler::Jump theSlowPath) { + slowPath = theSlowPath; + }); + done.link(&m_jit); - return true; + addSlowPathGenerator( + slowPathCall( + slowPath, this, operationTypeOfObject, resultGPR, globalObject, + valueRegs.payloadGPR())); + + cellResult(resultGPR, node); +} + +void SpeculativeJIT::compileCheckStructure(Node* node, GPRReg cellGPR, GPRReg tempGPR) +{ + ASSERT(node->structureSet().size()); + + if (node->structureSet().size() == 1) { + speculationCheck( + BadCache, JSValueSource::unboxedCell(cellGPR), 0, + m_jit.branchWeakStructure( + JITCompiler::NotEqual, + JITCompiler::Address(cellGPR, JSCell::structureIDOffset()), + node->structureSet()[0])); + } else { + std::unique_ptr<GPRTemporary> structure; + GPRReg structureGPR; + + if (tempGPR == InvalidGPRReg) { + structure = std::make_unique<GPRTemporary>(this); + structureGPR = structure->gpr(); + } else + structureGPR = tempGPR; + + m_jit.load32(JITCompiler::Address(cellGPR, JSCell::structureIDOffset()), structureGPR); + + JITCompiler::JumpList done; + + for (size_t i = 0; i < node->structureSet().size() - 1; ++i) { + done.append( + m_jit.branchWeakStructure(JITCompiler::Equal, structureGPR, node->structureSet()[i])); + } + + speculationCheck( + BadCache, JSValueSource::unboxedCell(cellGPR), 0, + m_jit.branchWeakStructure( + JITCompiler::NotEqual, structureGPR, node->structureSet().last())); + + done.link(&m_jit); + } +} + +void SpeculativeJIT::compileCheckStructure(Node* node) +{ + switch (node->child1().useKind()) { + case CellUse: + case KnownCellUse: { + SpeculateCellOperand cell(this, node->child1()); + compileCheckStructure(node, cell.gpr(), InvalidGPRReg); + noResult(node); + return; + } + + case CellOrOtherUse: { + JSValueOperand value(this, node->child1(), ManualOperandSpeculation); + GPRTemporary temp(this); + + JSValueRegs valueRegs = value.jsValueRegs(); + GPRReg tempGPR = temp.gpr(); + + JITCompiler::Jump cell = m_jit.branchIfCell(valueRegs); + DFG_TYPE_CHECK( + valueRegs, node->child1(), SpecCell | SpecOther, + m_jit.branchIfNotOther(valueRegs, tempGPR)); + JITCompiler::Jump done = m_jit.jump(); + cell.link(&m_jit); + compileCheckStructure(node, valueRegs.payloadGPR(), tempGPR); + done.link(&m_jit); + noResult(node); + return; + } + + default: + DFG_CRASH(m_jit.graph(), node, "Bad use kind"); + return; + } } void SpeculativeJIT::compileAllocatePropertyStorage(Node* node) { - if (hasIndexingHeader(node->structureTransitionData().previousStructure->indexingType())) { + if (node->transition()->previous->couldHaveIndexingHeader()) { SpeculateCellOperand base(this, node->child1()); GPRReg baseGPR = base.gpr(); flushRegisters(); - GPRResult result(this); + GPRFlushedCallResult result(this); callOperation(operationReallocateButterflyToHavePropertyStorageWithInitialCapacity, result.gpr(), baseGPR); + m_jit.exceptionCheck(); storageResult(result.gpr(), node); return; } SpeculateCellOperand base(this, node->child1()); - GPRTemporary scratch(this); + GPRTemporary scratch1(this); GPRReg baseGPR = base.gpr(); - GPRReg scratchGPR = scratch.gpr(); + GPRReg scratchGPR1 = scratch1.gpr(); - ASSERT(!node->structureTransitionData().previousStructure->outOfLineCapacity()); - ASSERT(initialOutOfLineCapacity == node->structureTransitionData().newStructure->outOfLineCapacity()); + ASSERT(!node->transition()->previous->outOfLineCapacity()); + ASSERT(initialOutOfLineCapacity == node->transition()->next->outOfLineCapacity()); JITCompiler::Jump slowPath = emitAllocateBasicStorage( - TrustedImm32(initialOutOfLineCapacity * sizeof(JSValue)), scratchGPR); + TrustedImm32(initialOutOfLineCapacity * sizeof(JSValue)), scratchGPR1); - m_jit.addPtr(JITCompiler::TrustedImm32(sizeof(JSValue)), scratchGPR); + m_jit.addPtr(JITCompiler::TrustedImm32(sizeof(IndexingHeader)), scratchGPR1); addSlowPathGenerator( - slowPathCall(slowPath, this, operationAllocatePropertyStorageWithInitialCapacity, scratchGPR)); - - m_jit.storePtr(scratchGPR, JITCompiler::Address(baseGPR, JSObject::butterflyOffset())); - - storageResult(scratchGPR, node); + slowPathCall(slowPath, this, operationAllocatePropertyStorageWithInitialCapacity, scratchGPR1)); + + m_jit.storePtr(scratchGPR1, JITCompiler::Address(baseGPR, JSObject::butterflyOffset())); + + storageResult(scratchGPR1, node); } void SpeculativeJIT::compileReallocatePropertyStorage(Node* node) { - size_t oldSize = node->structureTransitionData().previousStructure->outOfLineCapacity() * sizeof(JSValue); + size_t oldSize = node->transition()->previous->outOfLineCapacity() * sizeof(JSValue); size_t newSize = oldSize * outOfLineGrowthFactor; - ASSERT(newSize == node->structureTransitionData().newStructure->outOfLineCapacity() * sizeof(JSValue)); + ASSERT(newSize == node->transition()->next->outOfLineCapacity() * sizeof(JSValue)); - if (hasIndexingHeader(node->structureTransitionData().previousStructure->indexingType())) { + if (node->transition()->previous->couldHaveIndexingHeader()) { SpeculateCellOperand base(this, node->child1()); GPRReg baseGPR = base.gpr(); flushRegisters(); - GPRResult result(this); + GPRFlushedCallResult result(this); callOperation(operationReallocateButterflyToGrowPropertyStorage, result.gpr(), baseGPR, newSize / sizeof(JSValue)); - + m_jit.exceptionCheck(); + storageResult(result.gpr(), node); return; } @@ -4265,20 +6272,51 @@ void SpeculativeJIT::compileReallocatePropertyStorage(Node* node) GPRReg scratchGPR2 = scratch2.gpr(); JITCompiler::Jump slowPath = - emitAllocateBasicStorage(TrustedImm32(newSize), scratchGPR2); + emitAllocateBasicStorage(TrustedImm32(newSize), scratchGPR1); - m_jit.addPtr(JITCompiler::TrustedImm32(sizeof(JSValue)), scratchGPR2); + m_jit.addPtr(JITCompiler::TrustedImm32(sizeof(IndexingHeader)), scratchGPR1); addSlowPathGenerator( - slowPathCall(slowPath, this, operationAllocatePropertyStorage, scratchGPR2, newSize / sizeof(JSValue))); - // We have scratchGPR2 = new storage, scratchGPR1 = scratch + slowPathCall(slowPath, this, operationAllocatePropertyStorage, scratchGPR1, newSize / sizeof(JSValue))); + + // We have scratchGPR1 = new storage, scratchGPR2 = scratch for (ptrdiff_t offset = 0; offset < static_cast<ptrdiff_t>(oldSize); offset += sizeof(void*)) { - m_jit.loadPtr(JITCompiler::Address(oldStorageGPR, -(offset + sizeof(JSValue) + sizeof(void*))), scratchGPR1); - m_jit.storePtr(scratchGPR1, JITCompiler::Address(scratchGPR2, -(offset + sizeof(JSValue) + sizeof(void*)))); + m_jit.loadPtr(JITCompiler::Address(oldStorageGPR, -(offset + sizeof(JSValue) + sizeof(void*))), scratchGPR2); + m_jit.storePtr(scratchGPR2, JITCompiler::Address(scratchGPR1, -(offset + sizeof(JSValue) + sizeof(void*)))); } - m_jit.storePtr(scratchGPR2, JITCompiler::Address(baseGPR, JSObject::butterflyOffset())); + m_jit.storePtr(scratchGPR1, JITCompiler::Address(baseGPR, JSObject::butterflyOffset())); + + storageResult(scratchGPR1, node); +} + +void SpeculativeJIT::compileGetButterfly(Node* node) +{ + SpeculateCellOperand base(this, node->child1()); + GPRTemporary result(this, Reuse, base); - storageResult(scratchGPR2, node); + GPRReg baseGPR = base.gpr(); + GPRReg resultGPR = result.gpr(); + + m_jit.loadPtr(JITCompiler::Address(baseGPR, JSObject::butterflyOffset()), resultGPR); + + switch (node->op()) { + case GetButterfly: + addSlowPathGenerator( + slowPathCall( + m_jit.branchIfNotToSpace(resultGPR), + this, operationGetButterfly, resultGPR, baseGPR)); + break; + + case GetButterflyReadOnly: + m_jit.removeSpaceBits(resultGPR); + break; + + default: + DFG_CRASH(m_jit.graph(), node, "Bad node type"); + break; + } + + storageResult(resultGPR, node); } GPRReg SpeculativeJIT::temporaryRegisterForPutByVal(GPRTemporary& temporary, ArrayMode arrayMode) @@ -4291,7 +6329,7 @@ GPRReg SpeculativeJIT::temporaryRegisterForPutByVal(GPRTemporary& temporary, Arr return temporary.gpr(); } -void SpeculativeJIT::compileToStringOnCell(Node* node) +void SpeculativeJIT::compileToStringOrCallStringConstructorOnCell(Node* node) { SpeculateCellOperand op1(this, node->child1()); GPRReg op1GPR = op1.gpr(); @@ -4302,7 +6340,8 @@ void SpeculativeJIT::compileToStringOnCell(Node* node) GPRReg resultGPR = result.gpr(); speculateStringObject(node->child1(), op1GPR); - m_state.forNode(node->child1()).filter(SpecStringObject); + m_interpreter.filter(node->child1(), SpecStringObject); + m_jit.loadPtr(JITCompiler::Address(op1GPR, JSWrapperObject::internalValueCellOffset()), resultGPR); cellResult(resultGPR, node); break; @@ -4311,11 +6350,13 @@ void SpeculativeJIT::compileToStringOnCell(Node* node) case StringOrStringObjectUse: { GPRTemporary result(this); GPRReg resultGPR = result.gpr(); - - m_jit.loadPtr(JITCompiler::Address(op1GPR, JSCell::structureOffset()), resultGPR); - JITCompiler::Jump isString = m_jit.branchPtr( - JITCompiler::Equal, resultGPR, TrustedImmPtr(m_jit.vm()->stringStructure.get())); - + + m_jit.load32(JITCompiler::Address(op1GPR, JSCell::structureIDOffset()), resultGPR); + JITCompiler::Jump isString = m_jit.branchStructure( + JITCompiler::Equal, + resultGPR, + m_jit.vm()->stringStructure.get()); + speculateStringObjectForStructure(node->child1(), resultGPR); m_jit.loadPtr(JITCompiler::Address(op1GPR, JSWrapperObject::internalValueCellOffset()), resultGPR); @@ -4325,14 +6366,14 @@ void SpeculativeJIT::compileToStringOnCell(Node* node) m_jit.move(op1GPR, resultGPR); done.link(&m_jit); - m_state.forNode(node->child1()).filter(SpecString | SpecStringObject); + m_interpreter.filter(node->child1(), SpecString | SpecStringObject); cellResult(resultGPR, node); break; } case CellUse: { - GPRResult result(this); + GPRFlushedCallResult result(this); GPRReg resultGPR = result.gpr(); // We flush registers instead of silent spill/fill because in this mode we @@ -4341,15 +6382,18 @@ void SpeculativeJIT::compileToStringOnCell(Node* node) flushRegisters(); JITCompiler::Jump done; if (node->child1()->prediction() & SpecString) { - JITCompiler::Jump needCall = m_jit.branchPtr( - JITCompiler::NotEqual, - JITCompiler::Address(op1GPR, JSCell::structureOffset()), - TrustedImmPtr(m_jit.vm()->stringStructure.get())); + JITCompiler::Jump needCall = m_jit.branchIfNotString(op1GPR); m_jit.move(op1GPR, resultGPR); done = m_jit.jump(); needCall.link(&m_jit); } - callOperation(operationToStringOnCell, resultGPR, op1GPR); + if (node->op() == ToString) + callOperation(operationToStringOnCell, resultGPR, op1GPR); + else { + ASSERT(node->op() == CallStringConstructor); + callOperation(operationCallStringConstructorOnCell, resultGPR, op1GPR); + } + m_jit.exceptionCheck(); if (done.isSet()) done.link(&m_jit); cellResult(resultGPR, node); @@ -4381,7 +6425,7 @@ void SpeculativeJIT::compileNewStringObject(Node* node) slowPath); m_jit.storePtr( - TrustedImmPtr(&StringObject::s_info), + TrustedImmPtr(StringObject::info()), JITCompiler::Address(resultGPR, JSDestructibleObject::classInfoOffset())); #if USE(JSVALUE64) m_jit.store64( @@ -4401,31 +6445,174 @@ void SpeculativeJIT::compileNewStringObject(Node* node) cellResult(resultGPR, node); } +void SpeculativeJIT::compileNewTypedArray(Node* node) +{ + JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic); + TypedArrayType type = node->typedArrayType(); + Structure* structure = globalObject->typedArrayStructure(type); + + SpeculateInt32Operand size(this, node->child1()); + GPRReg sizeGPR = size.gpr(); + + GPRTemporary result(this); + GPRTemporary storage(this); + GPRTemporary scratch(this); + GPRTemporary scratch2(this); + GPRReg resultGPR = result.gpr(); + GPRReg storageGPR = storage.gpr(); + GPRReg scratchGPR = scratch.gpr(); + GPRReg scratchGPR2 = scratch2.gpr(); + + JITCompiler::JumpList slowCases; + + slowCases.append(m_jit.branch32( + MacroAssembler::Above, sizeGPR, TrustedImm32(JSArrayBufferView::fastSizeLimit))); + slowCases.append(m_jit.branchTest32(MacroAssembler::Zero, sizeGPR)); + + m_jit.move(sizeGPR, scratchGPR); + m_jit.lshift32(TrustedImm32(logElementSize(type)), scratchGPR); + if (elementSize(type) < 8) { + m_jit.add32(TrustedImm32(7), scratchGPR); + m_jit.and32(TrustedImm32(~7), scratchGPR); + } + slowCases.append( + emitAllocateBasicStorage(scratchGPR, storageGPR)); + + m_jit.subPtr(scratchGPR, storageGPR); + + emitAllocateJSObject<JSArrayBufferView>( + resultGPR, TrustedImmPtr(structure), TrustedImmPtr(0), scratchGPR, scratchGPR2, + slowCases); + + m_jit.storePtr( + storageGPR, + MacroAssembler::Address(resultGPR, JSArrayBufferView::offsetOfVector())); + m_jit.store32( + sizeGPR, + MacroAssembler::Address(resultGPR, JSArrayBufferView::offsetOfLength())); + m_jit.store32( + TrustedImm32(FastTypedArray), + MacroAssembler::Address(resultGPR, JSArrayBufferView::offsetOfMode())); + +#if USE(JSVALUE32_64) + MacroAssembler::Jump done = m_jit.branchTest32(MacroAssembler::Zero, sizeGPR); + m_jit.move(sizeGPR, scratchGPR); + if (elementSize(type) != 4) { + if (elementSize(type) > 4) + m_jit.lshift32(TrustedImm32(logElementSize(type) - 2), scratchGPR); + else { + if (elementSize(type) > 1) + m_jit.lshift32(TrustedImm32(logElementSize(type)), scratchGPR); + m_jit.add32(TrustedImm32(3), scratchGPR); + m_jit.urshift32(TrustedImm32(2), scratchGPR); + } + } + MacroAssembler::Label loop = m_jit.label(); + m_jit.sub32(TrustedImm32(1), scratchGPR); + m_jit.store32( + TrustedImm32(0), + MacroAssembler::BaseIndex(storageGPR, scratchGPR, MacroAssembler::TimesFour)); + m_jit.branchTest32(MacroAssembler::NonZero, scratchGPR).linkTo(loop, &m_jit); + done.link(&m_jit); +#endif // USE(JSVALUE32_64) + + addSlowPathGenerator(slowPathCall( + slowCases, this, operationNewTypedArrayWithSizeForType(type), + resultGPR, structure, sizeGPR)); + + cellResult(resultGPR, node); +} + +void SpeculativeJIT::speculateCellTypeWithoutTypeFiltering( + Edge edge, GPRReg cellGPR, JSType jsType) +{ + speculationCheck( + BadType, JSValueSource::unboxedCell(cellGPR), edge, + m_jit.branch8( + MacroAssembler::NotEqual, + MacroAssembler::Address(cellGPR, JSCell::typeInfoTypeOffset()), + MacroAssembler::TrustedImm32(jsType))); +} + +void SpeculativeJIT::speculateCellType( + Edge edge, GPRReg cellGPR, SpeculatedType specType, JSType jsType) +{ + DFG_TYPE_CHECK( + JSValueSource::unboxedCell(cellGPR), edge, specType, + m_jit.branch8( + MacroAssembler::NotEqual, + MacroAssembler::Address(cellGPR, JSCell::typeInfoTypeOffset()), + TrustedImm32(jsType))); +} + void SpeculativeJIT::speculateInt32(Edge edge) { if (!needsTypeCheck(edge, SpecInt32)) return; - (SpeculateIntegerOperand(this, edge)).gpr(); + (SpeculateInt32Operand(this, edge)).gpr(); } void SpeculativeJIT::speculateNumber(Edge edge) { - if (!needsTypeCheck(edge, SpecNumber)) + if (!needsTypeCheck(edge, SpecBytecodeNumber)) return; - (SpeculateDoubleOperand(this, edge)).fpr(); + JSValueOperand value(this, edge, ManualOperandSpeculation); +#if USE(JSVALUE64) + GPRReg gpr = value.gpr(); + typeCheck( + JSValueRegs(gpr), edge, SpecBytecodeNumber, + m_jit.branchTest64(MacroAssembler::Zero, gpr, GPRInfo::tagTypeNumberRegister)); +#else + GPRReg tagGPR = value.tagGPR(); + DFG_TYPE_CHECK( + value.jsValueRegs(), edge, ~SpecInt32, + m_jit.branch32(MacroAssembler::Equal, tagGPR, TrustedImm32(JSValue::Int32Tag))); + DFG_TYPE_CHECK( + value.jsValueRegs(), edge, SpecBytecodeNumber, + m_jit.branch32(MacroAssembler::AboveOrEqual, tagGPR, TrustedImm32(JSValue::LowestTag))); +#endif } void SpeculativeJIT::speculateRealNumber(Edge edge) { - if (!needsTypeCheck(edge, SpecRealNumber)) + if (!needsTypeCheck(edge, SpecBytecodeRealNumber)) + return; + + JSValueOperand op1(this, edge, ManualOperandSpeculation); + FPRTemporary result(this); + + JSValueRegs op1Regs = op1.jsValueRegs(); + FPRReg resultFPR = result.fpr(); + +#if USE(JSVALUE64) + GPRTemporary temp(this); + GPRReg tempGPR = temp.gpr(); + m_jit.unboxDoubleWithoutAssertions(op1Regs.gpr(), tempGPR, resultFPR); +#else + FPRTemporary temp(this); + FPRReg tempFPR = temp.fpr(); + unboxDouble(op1Regs.tagGPR(), op1Regs.payloadGPR(), resultFPR, tempFPR); +#endif + + JITCompiler::Jump done = m_jit.branchDouble( + JITCompiler::DoubleEqual, resultFPR, resultFPR); + + typeCheck(op1Regs, edge, SpecBytecodeRealNumber, m_jit.branchIfNotInt32(op1Regs)); + + done.link(&m_jit); +} + +void SpeculativeJIT::speculateDoubleRepReal(Edge edge) +{ + if (!needsTypeCheck(edge, SpecDoubleReal)) return; SpeculateDoubleOperand operand(this, edge); FPRReg fpr = operand.fpr(); - DFG_TYPE_CHECK( - JSValueRegs(), edge, SpecRealNumber, + typeCheck( + JSValueRegs(), edge, SpecDoubleReal, m_jit.branchDouble( MacroAssembler::DoubleNotEqualOrUnordered, fpr, fpr)); } @@ -4446,6 +6633,22 @@ void SpeculativeJIT::speculateCell(Edge edge) (SpeculateCellOperand(this, edge)).gpr(); } +void SpeculativeJIT::speculateCellOrOther(Edge edge) +{ + if (!needsTypeCheck(edge, SpecCell | SpecOther)) + return; + + JSValueOperand operand(this, edge, ManualOperandSpeculation); + GPRTemporary temp(this); + GPRReg tempGPR = temp.gpr(); + + MacroAssembler::Jump ok = m_jit.branchIfCell(operand.jsValueRegs()); + DFG_TYPE_CHECK( + operand.jsValueRegs(), edge, SpecCell | SpecOther, + m_jit.branchIfNotOther(operand.jsValueRegs(), tempGPR)); + ok.link(&m_jit); +} + void SpeculativeJIT::speculateObject(Edge edge) { if (!needsTypeCheck(edge, SpecObject)) @@ -4454,10 +6657,34 @@ void SpeculativeJIT::speculateObject(Edge edge) SpeculateCellOperand operand(this, edge); GPRReg gpr = operand.gpr(); DFG_TYPE_CHECK( - JSValueSource::unboxedCell(gpr), edge, SpecObject, m_jit.branchPtr( - MacroAssembler::Equal, - MacroAssembler::Address(gpr, JSCell::structureOffset()), - MacroAssembler::TrustedImmPtr(m_jit.vm()->stringStructure.get()))); + JSValueSource::unboxedCell(gpr), edge, SpecObject, m_jit.branchIfNotObject(gpr)); +} + +void SpeculativeJIT::speculateFunction(Edge edge) +{ + if (!needsTypeCheck(edge, SpecFunction)) + return; + + SpeculateCellOperand operand(this, edge); + speculateCellType(edge, operand.gpr(), SpecFunction, JSFunctionType); +} + +void SpeculativeJIT::speculateFinalObject(Edge edge) +{ + if (!needsTypeCheck(edge, SpecFinalObject)) + return; + + SpeculateCellOperand operand(this, edge); + speculateCellType(edge, operand.gpr(), SpecFinalObject, FinalObjectType); +} + +void SpeculativeJIT::speculateRegExpObject(Edge edge) +{ + if (!needsTypeCheck(edge, SpecRegExpObject)) + return; + + SpeculateCellOperand operand(this, edge); + speculateCellType(edge, operand.gpr(), SpecRegExpObject, RegExpObjectType); } void SpeculativeJIT::speculateObjectOrOther(Edge edge) @@ -4468,52 +6695,84 @@ void SpeculativeJIT::speculateObjectOrOther(Edge edge) JSValueOperand operand(this, edge, ManualOperandSpeculation); GPRTemporary temp(this); GPRReg tempGPR = temp.gpr(); -#if USE(JSVALUE64) - GPRReg gpr = operand.gpr(); - MacroAssembler::Jump notCell = m_jit.branchTest64( - MacroAssembler::NonZero, gpr, GPRInfo::tagMaskRegister); + MacroAssembler::Jump notCell = m_jit.branchIfNotCell(operand.jsValueRegs()); + GPRReg gpr = operand.jsValueRegs().payloadGPR(); DFG_TYPE_CHECK( - JSValueRegs(gpr), edge, (~SpecCell) | SpecObject, m_jit.branchPtr( - MacroAssembler::Equal, - MacroAssembler::Address(gpr, JSCell::structureOffset()), - MacroAssembler::TrustedImmPtr(m_jit.vm()->stringStructure.get()))); + operand.jsValueRegs(), edge, (~SpecCell) | SpecObject, m_jit.branchIfNotObject(gpr)); MacroAssembler::Jump done = m_jit.jump(); notCell.link(&m_jit); - if (needsTypeCheck(edge, SpecCell | SpecOther)) { - m_jit.move(gpr, tempGPR); - m_jit.and64(MacroAssembler::TrustedImm32(~TagBitUndefined), tempGPR); - - typeCheck( - JSValueRegs(gpr), edge, SpecCell | SpecOther, - m_jit.branch64( - MacroAssembler::NotEqual, tempGPR, - MacroAssembler::TrustedImm64(ValueNull))); - } + DFG_TYPE_CHECK( + operand.jsValueRegs(), edge, SpecCell | SpecOther, + m_jit.branchIfNotOther(operand.jsValueRegs(), tempGPR)); done.link(&m_jit); -#else - GPRReg tagGPR = operand.tagGPR(); - GPRReg payloadGPR = operand.payloadGPR(); - MacroAssembler::Jump notCell = - m_jit.branch32(MacroAssembler::NotEqual, tagGPR, TrustedImm32(JSValue::CellTag)); +} + +void SpeculativeJIT::speculateString(Edge edge, GPRReg cell) +{ DFG_TYPE_CHECK( - JSValueRegs(tagGPR, payloadGPR), edge, (~SpecCell) | SpecObject, m_jit.branchPtr( - MacroAssembler::Equal, - MacroAssembler::Address(payloadGPR, JSCell::structureOffset()), - MacroAssembler::TrustedImmPtr(m_jit.vm()->stringStructure.get()))); - MacroAssembler::Jump done = m_jit.jump(); + JSValueSource::unboxedCell(cell), edge, SpecString | ~SpecCell, m_jit.branchIfNotString(cell)); +} + +void SpeculativeJIT::speculateStringOrOther(Edge edge, JSValueRegs regs, GPRReg scratch) +{ + JITCompiler::Jump notCell = m_jit.branchIfNotCell(regs); + GPRReg cell = regs.payloadGPR(); + DFG_TYPE_CHECK(regs, edge, (~SpecCell) | SpecString, m_jit.branchIfNotString(cell)); + JITCompiler::Jump done = m_jit.jump(); notCell.link(&m_jit); - if (needsTypeCheck(edge, SpecCell | SpecOther)) { - m_jit.move(tagGPR, tempGPR); - m_jit.or32(TrustedImm32(1), tempGPR); - - typeCheck( - JSValueRegs(tagGPR, payloadGPR), edge, SpecCell | SpecOther, - m_jit.branch32( - MacroAssembler::NotEqual, tempGPR, - MacroAssembler::TrustedImm32(JSValue::NullTag))); - } + DFG_TYPE_CHECK(regs, edge, SpecCell | SpecOther, m_jit.branchIfNotOther(regs, scratch)); done.link(&m_jit); -#endif +} + +void SpeculativeJIT::speculateStringOrOther(Edge edge) +{ + if (!needsTypeCheck(edge, SpecString | SpecOther)) + return; + + JSValueOperand operand(this, edge, ManualOperandSpeculation); + GPRTemporary temp(this); + JSValueRegs regs = operand.jsValueRegs(); + GPRReg tempGPR = temp.gpr(); + speculateStringOrOther(edge, regs, tempGPR); +} + +void SpeculativeJIT::speculateStringIdentAndLoadStorage(Edge edge, GPRReg string, GPRReg storage) +{ + m_jit.loadPtr(MacroAssembler::Address(string, JSString::offsetOfValue()), storage); + + if (!needsTypeCheck(edge, SpecStringIdent | ~SpecString)) + return; + + speculationCheck( + BadType, JSValueSource::unboxedCell(string), edge, + m_jit.branchTestPtr(MacroAssembler::Zero, storage)); + speculationCheck( + BadType, JSValueSource::unboxedCell(string), edge, m_jit.branchTest32( + MacroAssembler::Zero, + MacroAssembler::Address(storage, StringImpl::flagsOffset()), + MacroAssembler::TrustedImm32(StringImpl::flagIsAtomic()))); + + m_interpreter.filter(edge, SpecStringIdent | ~SpecString); +} + +void SpeculativeJIT::speculateStringIdent(Edge edge, GPRReg string) +{ + if (!needsTypeCheck(edge, SpecStringIdent)) + return; + + GPRTemporary temp(this); + speculateStringIdentAndLoadStorage(edge, string, temp.gpr()); +} + +void SpeculativeJIT::speculateStringIdent(Edge edge) +{ + if (!needsTypeCheck(edge, SpecStringIdent)) + return; + + SpeculateCellOperand operand(this, edge); + GPRReg gpr = operand.gpr(); + speculateString(edge, gpr); + speculateStringIdent(edge, gpr); } void SpeculativeJIT::speculateString(Edge edge) @@ -4522,17 +6781,12 @@ void SpeculativeJIT::speculateString(Edge edge) return; SpeculateCellOperand operand(this, edge); - GPRReg gpr = operand.gpr(); - DFG_TYPE_CHECK( - JSValueSource::unboxedCell(gpr), edge, SpecString, m_jit.branchPtr( - MacroAssembler::NotEqual, - MacroAssembler::Address(gpr, JSCell::structureOffset()), - MacroAssembler::TrustedImmPtr(m_jit.vm()->stringStructure.get()))); + speculateString(edge, operand.gpr()); } void SpeculativeJIT::speculateStringObject(Edge edge, GPRReg gpr) { - speculateStringObjectForStructure(edge, JITCompiler::Address(gpr, JSCell::structureOffset())); + speculateStringObjectForStructure(edge, JITCompiler::Address(gpr, JSCell::structureIDOffset())); } void SpeculativeJIT::speculateStringObject(Edge edge) @@ -4546,7 +6800,7 @@ void SpeculativeJIT::speculateStringObject(Edge edge) return; speculateStringObject(edge, gpr); - m_state.forNode(edge).filter(SpecStringObject); + m_interpreter.filter(edge, SpecStringObject); } void SpeculativeJIT::speculateStringOrStringObject(Edge edge) @@ -4558,20 +6812,52 @@ void SpeculativeJIT::speculateStringOrStringObject(Edge edge) GPRReg gpr = operand.gpr(); if (!needsTypeCheck(edge, SpecString | SpecStringObject)) return; + + GPRTemporary structureID(this); + GPRReg structureIDGPR = structureID.gpr(); + + m_jit.load32(JITCompiler::Address(gpr, JSCell::structureIDOffset()), structureIDGPR); + JITCompiler::Jump isString = m_jit.branchStructure( + JITCompiler::Equal, + structureIDGPR, + m_jit.vm()->stringStructure.get()); - GPRTemporary structure(this); - GPRReg structureGPR = structure.gpr(); + speculateStringObjectForStructure(edge, structureIDGPR); - m_jit.loadPtr(JITCompiler::Address(gpr, JSCell::structureOffset()), structureGPR); + isString.link(&m_jit); - JITCompiler::Jump isString = m_jit.branchPtr( - JITCompiler::Equal, structureGPR, TrustedImmPtr(m_jit.vm()->stringStructure.get())); + m_interpreter.filter(edge, SpecString | SpecStringObject); +} + +void SpeculativeJIT::speculateNotStringVar(Edge edge) +{ + JSValueOperand operand(this, edge, ManualOperandSpeculation); + GPRTemporary temp(this); + GPRReg tempGPR = temp.gpr(); - speculateStringObjectForStructure(edge, structureGPR); + JITCompiler::Jump notCell = m_jit.branchIfNotCell(operand.jsValueRegs()); + GPRReg cell = operand.jsValueRegs().payloadGPR(); - isString.link(&m_jit); + JITCompiler::Jump notString = m_jit.branchIfNotString(cell); + + speculateStringIdentAndLoadStorage(edge, cell, tempGPR); - m_state.forNode(edge).filter(SpecString | SpecStringObject); + notString.link(&m_jit); + notCell.link(&m_jit); +} + +void SpeculativeJIT::speculateSymbol(Edge edge, GPRReg cell) +{ + DFG_TYPE_CHECK(JSValueSource::unboxedCell(cell), edge, SpecSymbol, m_jit.branchIfNotSymbol(cell)); +} + +void SpeculativeJIT::speculateSymbol(Edge edge) +{ + if (!needsTypeCheck(edge, SpecSymbol)) + return; + + SpeculateCellOperand operand(this, edge); + speculateSymbol(edge, operand.gpr()); } void SpeculativeJIT::speculateNotCell(Edge edge) @@ -4579,18 +6865,8 @@ void SpeculativeJIT::speculateNotCell(Edge edge) if (!needsTypeCheck(edge, ~SpecCell)) return; - JSValueOperand operand(this, edge, ManualOperandSpeculation); -#if USE(JSVALUE64) - typeCheck( - JSValueRegs(operand.gpr()), edge, ~SpecCell, - m_jit.branchTest64( - JITCompiler::Zero, operand.gpr(), GPRInfo::tagMaskRegister)); -#else - typeCheck( - JSValueRegs(operand.tagGPR(), operand.payloadGPR()), edge, ~SpecCell, - m_jit.branch32( - JITCompiler::Equal, operand.tagGPR(), TrustedImm32(JSValue::CellTag))); -#endif + JSValueOperand operand(this, edge, ManualOperandSpeculation); + typeCheck(operand.jsValueRegs(), edge, ~SpecCell, m_jit.branchIfCell(operand.jsValueRegs())); } void SpeculativeJIT::speculateOther(Edge edge) @@ -4601,21 +6877,34 @@ void SpeculativeJIT::speculateOther(Edge edge) JSValueOperand operand(this, edge, ManualOperandSpeculation); GPRTemporary temp(this); GPRReg tempGPR = temp.gpr(); -#if USE(JSVALUE64) - m_jit.move(operand.gpr(), tempGPR); - m_jit.and64(MacroAssembler::TrustedImm32(~TagBitUndefined), tempGPR); typeCheck( - JSValueRegs(operand.gpr()), edge, SpecOther, - m_jit.branch64( - MacroAssembler::NotEqual, tempGPR, - MacroAssembler::TrustedImm64(ValueNull))); + operand.jsValueRegs(), edge, SpecOther, + m_jit.branchIfNotOther(operand.jsValueRegs(), tempGPR)); +} + +void SpeculativeJIT::speculateMisc(Edge edge, JSValueRegs regs) +{ +#if USE(JSVALUE64) + DFG_TYPE_CHECK( + regs, edge, SpecMisc, + m_jit.branch64(MacroAssembler::Above, regs.gpr(), MacroAssembler::TrustedImm64(TagBitTypeOther | TagBitBool | TagBitUndefined))); #else - m_jit.move(operand.tagGPR(), tempGPR); - m_jit.or32(TrustedImm32(1), tempGPR); - typeCheck( - JSValueRegs(operand.tagGPR(), operand.payloadGPR()), edge, SpecOther, - m_jit.branch32(MacroAssembler::NotEqual, tempGPR, TrustedImm32(JSValue::NullTag))); -#endif + DFG_TYPE_CHECK( + regs, edge, ~SpecInt32, + m_jit.branch32(MacroAssembler::Equal, regs.tagGPR(), MacroAssembler::TrustedImm32(JSValue::Int32Tag))); + DFG_TYPE_CHECK( + regs, edge, SpecMisc, + m_jit.branch32(MacroAssembler::Below, regs.tagGPR(), MacroAssembler::TrustedImm32(JSValue::UndefinedTag))); +#endif +} + +void SpeculativeJIT::speculateMisc(Edge edge) +{ + if (!needsTypeCheck(edge, SpecMisc)) + return; + + JSValueOperand operand(this, edge, ManualOperandSpeculation); + speculateMisc(edge, operand.jsValueRegs()); } void SpeculativeJIT::speculate(Node*, Edge edge) @@ -4626,8 +6915,11 @@ void SpeculativeJIT::speculate(Node*, Edge edge) case KnownInt32Use: ASSERT(!needsTypeCheck(edge, SpecInt32)); break; - case KnownNumberUse: - ASSERT(!needsTypeCheck(edge, SpecNumber)); + case DoubleRepUse: + ASSERT(!needsTypeCheck(edge, SpecFullDouble)); + break; + case Int52RepUse: + ASSERT(!needsTypeCheck(edge, SpecMachineInt)); break; case KnownCellUse: ASSERT(!needsTypeCheck(edge, SpecCell)); @@ -4635,48 +6927,671 @@ void SpeculativeJIT::speculate(Node*, Edge edge) case KnownStringUse: ASSERT(!needsTypeCheck(edge, SpecString)); break; + case KnownPrimitiveUse: + ASSERT(!needsTypeCheck(edge, SpecHeapTop & ~SpecObject)); + break; case Int32Use: speculateInt32(edge); break; + case NumberUse: + speculateNumber(edge); + break; case RealNumberUse: speculateRealNumber(edge); break; - case NumberUse: - speculateNumber(edge); + case DoubleRepRealUse: + speculateDoubleRepReal(edge); + break; +#if USE(JSVALUE64) + case MachineIntUse: + speculateMachineInt(edge); break; + case DoubleRepMachineIntUse: + speculateDoubleRepMachineInt(edge); + break; +#endif case BooleanUse: speculateBoolean(edge); break; + case KnownBooleanUse: + ASSERT(!needsTypeCheck(edge, SpecBoolean)); + break; case CellUse: speculateCell(edge); break; + case CellOrOtherUse: + speculateCellOrOther(edge); + break; case ObjectUse: speculateObject(edge); break; + case FunctionUse: + speculateFunction(edge); + break; + case FinalObjectUse: + speculateFinalObject(edge); + break; + case RegExpObjectUse: + speculateRegExpObject(edge); + break; case ObjectOrOtherUse: speculateObjectOrOther(edge); break; + case StringIdentUse: + speculateStringIdent(edge); + break; case StringUse: speculateString(edge); break; + case StringOrOtherUse: + speculateStringOrOther(edge); + break; + case SymbolUse: + speculateSymbol(edge); + break; case StringObjectUse: speculateStringObject(edge); break; case StringOrStringObjectUse: speculateStringOrStringObject(edge); break; + case NotStringVarUse: + speculateNotStringVar(edge); + break; case NotCellUse: speculateNotCell(edge); break; case OtherUse: speculateOther(edge); break; + case MiscUse: + speculateMisc(edge); + break; default: RELEASE_ASSERT_NOT_REACHED(); break; } } +void SpeculativeJIT::emitSwitchIntJump( + SwitchData* data, GPRReg value, GPRReg scratch) +{ + SimpleJumpTable& table = m_jit.codeBlock()->switchJumpTable(data->switchTableIndex); + table.ensureCTITable(); + m_jit.sub32(Imm32(table.min), value); + addBranch( + m_jit.branch32(JITCompiler::AboveOrEqual, value, Imm32(table.ctiOffsets.size())), + data->fallThrough.block); + m_jit.move(TrustedImmPtr(table.ctiOffsets.begin()), scratch); + m_jit.loadPtr(JITCompiler::BaseIndex(scratch, value, JITCompiler::timesPtr()), scratch); + m_jit.jump(scratch); + data->didUseJumpTable = true; +} + +void SpeculativeJIT::emitSwitchImm(Node* node, SwitchData* data) +{ + switch (node->child1().useKind()) { + case Int32Use: { + SpeculateInt32Operand value(this, node->child1()); + GPRTemporary temp(this); + emitSwitchIntJump(data, value.gpr(), temp.gpr()); + noResult(node); + break; + } + + case UntypedUse: { + JSValueOperand value(this, node->child1()); + GPRTemporary temp(this); + JSValueRegs valueRegs = value.jsValueRegs(); + GPRReg scratch = temp.gpr(); + + value.use(); + +#if USE(JSVALUE64) + JITCompiler::Jump notInt = m_jit.branch64( + JITCompiler::Below, valueRegs.gpr(), GPRInfo::tagTypeNumberRegister); + emitSwitchIntJump(data, valueRegs.gpr(), scratch); + notInt.link(&m_jit); + addBranch( + m_jit.branchTest64( + JITCompiler::Zero, valueRegs.gpr(), GPRInfo::tagTypeNumberRegister), + data->fallThrough.block); + silentSpillAllRegisters(scratch); + callOperation(operationFindSwitchImmTargetForDouble, scratch, valueRegs.gpr(), data->switchTableIndex); + silentFillAllRegisters(scratch); + m_jit.jump(scratch); +#else + JITCompiler::Jump notInt = m_jit.branch32( + JITCompiler::NotEqual, valueRegs.tagGPR(), TrustedImm32(JSValue::Int32Tag)); + emitSwitchIntJump(data, valueRegs.payloadGPR(), scratch); + notInt.link(&m_jit); + addBranch( + m_jit.branch32( + JITCompiler::AboveOrEqual, valueRegs.tagGPR(), + TrustedImm32(JSValue::LowestTag)), + data->fallThrough.block); + silentSpillAllRegisters(scratch); + callOperation(operationFindSwitchImmTargetForDouble, scratch, valueRegs, data->switchTableIndex); + silentFillAllRegisters(scratch); + + m_jit.jump(scratch); +#endif + noResult(node, UseChildrenCalledExplicitly); + break; + } + + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } +} + +void SpeculativeJIT::emitSwitchCharStringJump( + SwitchData* data, GPRReg value, GPRReg scratch) +{ + addBranch( + m_jit.branch32( + MacroAssembler::NotEqual, + MacroAssembler::Address(value, JSString::offsetOfLength()), + TrustedImm32(1)), + data->fallThrough.block); + + m_jit.loadPtr(MacroAssembler::Address(value, JSString::offsetOfValue()), scratch); + + addSlowPathGenerator( + slowPathCall( + m_jit.branchTestPtr(MacroAssembler::Zero, scratch), + this, operationResolveRope, scratch, value)); + + m_jit.loadPtr(MacroAssembler::Address(scratch, StringImpl::dataOffset()), value); + + JITCompiler::Jump is8Bit = m_jit.branchTest32( + MacroAssembler::NonZero, + MacroAssembler::Address(scratch, StringImpl::flagsOffset()), + TrustedImm32(StringImpl::flagIs8Bit())); + + m_jit.load16(MacroAssembler::Address(value), scratch); + + JITCompiler::Jump ready = m_jit.jump(); + + is8Bit.link(&m_jit); + m_jit.load8(MacroAssembler::Address(value), scratch); + + ready.link(&m_jit); + emitSwitchIntJump(data, scratch, value); +} + +void SpeculativeJIT::emitSwitchChar(Node* node, SwitchData* data) +{ + switch (node->child1().useKind()) { + case StringUse: { + SpeculateCellOperand op1(this, node->child1()); + GPRTemporary temp(this); + + GPRReg op1GPR = op1.gpr(); + GPRReg tempGPR = temp.gpr(); + + op1.use(); + + speculateString(node->child1(), op1GPR); + emitSwitchCharStringJump(data, op1GPR, tempGPR); + noResult(node, UseChildrenCalledExplicitly); + break; + } + + case UntypedUse: { + JSValueOperand op1(this, node->child1()); + GPRTemporary temp(this); + + JSValueRegs op1Regs = op1.jsValueRegs(); + GPRReg tempGPR = temp.gpr(); + + op1.use(); + + addBranch(m_jit.branchIfNotCell(op1Regs), data->fallThrough.block); + + addBranch(m_jit.branchIfNotString(op1Regs.payloadGPR()), data->fallThrough.block); + + emitSwitchCharStringJump(data, op1Regs.payloadGPR(), tempGPR); + noResult(node, UseChildrenCalledExplicitly); + break; + } + + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } +} + +namespace { + +struct CharacterCase { + bool operator<(const CharacterCase& other) const + { + return character < other.character; + } + + LChar character; + unsigned begin; + unsigned end; +}; + +} // anonymous namespace + +void SpeculativeJIT::emitBinarySwitchStringRecurse( + SwitchData* data, const Vector<SpeculativeJIT::StringSwitchCase>& cases, + unsigned numChecked, unsigned begin, unsigned end, GPRReg buffer, GPRReg length, + GPRReg temp, unsigned alreadyCheckedLength, bool checkedExactLength) +{ + static const bool verbose = false; + + if (verbose) { + dataLog("We're down to the following cases, alreadyCheckedLength = ", alreadyCheckedLength, ":\n"); + for (unsigned i = begin; i < end; ++i) { + dataLog(" ", cases[i].string, "\n"); + } + } + + if (begin == end) { + jump(data->fallThrough.block, ForceJump); + return; + } + + unsigned minLength = cases[begin].string->length(); + unsigned commonChars = minLength; + bool allLengthsEqual = true; + for (unsigned i = begin + 1; i < end; ++i) { + unsigned myCommonChars = numChecked; + for (unsigned j = numChecked; + j < std::min(cases[begin].string->length(), cases[i].string->length()); + ++j) { + if (cases[begin].string->at(j) != cases[i].string->at(j)) { + if (verbose) + dataLog("string(", cases[i].string, ")[", j, "] != string(", cases[begin].string, ")[", j, "]\n"); + break; + } + myCommonChars++; + } + commonChars = std::min(commonChars, myCommonChars); + if (minLength != cases[i].string->length()) + allLengthsEqual = false; + minLength = std::min(minLength, cases[i].string->length()); + } + + if (checkedExactLength) { + RELEASE_ASSERT(alreadyCheckedLength == minLength); + RELEASE_ASSERT(allLengthsEqual); + } + + RELEASE_ASSERT(minLength >= commonChars); + + if (verbose) + dataLog("length = ", minLength, ", commonChars = ", commonChars, ", allLengthsEqual = ", allLengthsEqual, "\n"); + + if (!allLengthsEqual && alreadyCheckedLength < minLength) + branch32(MacroAssembler::Below, length, Imm32(minLength), data->fallThrough.block); + if (allLengthsEqual && (alreadyCheckedLength < minLength || !checkedExactLength)) + branch32(MacroAssembler::NotEqual, length, Imm32(minLength), data->fallThrough.block); + + for (unsigned i = numChecked; i < commonChars; ++i) { + branch8( + MacroAssembler::NotEqual, MacroAssembler::Address(buffer, i), + TrustedImm32(cases[begin].string->at(i)), data->fallThrough.block); + } + + if (minLength == commonChars) { + // This is the case where one of the cases is a prefix of all of the other cases. + // We've already checked that the input string is a prefix of all of the cases, + // so we just check length to jump to that case. + + if (!ASSERT_DISABLED) { + ASSERT(cases[begin].string->length() == commonChars); + for (unsigned i = begin + 1; i < end; ++i) + ASSERT(cases[i].string->length() > commonChars); + } + + if (allLengthsEqual) { + RELEASE_ASSERT(end == begin + 1); + jump(cases[begin].target, ForceJump); + return; + } + + branch32(MacroAssembler::Equal, length, Imm32(commonChars), cases[begin].target); + + // We've checked if the length is >= minLength, and then we checked if the + // length is == commonChars. We get to this point if it is >= minLength but not + // == commonChars. Hence we know that it now must be > minLength, i.e., that + // it's >= minLength + 1. + emitBinarySwitchStringRecurse( + data, cases, commonChars, begin + 1, end, buffer, length, temp, minLength + 1, false); + return; + } + + // At this point we know that the string is longer than commonChars, and we've only + // verified commonChars. Use a binary switch on the next unchecked character, i.e. + // string[commonChars]. + + RELEASE_ASSERT(end >= begin + 2); + + m_jit.load8(MacroAssembler::Address(buffer, commonChars), temp); + + Vector<CharacterCase> characterCases; + CharacterCase currentCase; + currentCase.character = cases[begin].string->at(commonChars); + currentCase.begin = begin; + currentCase.end = begin + 1; + for (unsigned i = begin + 1; i < end; ++i) { + if (cases[i].string->at(commonChars) != currentCase.character) { + if (verbose) + dataLog("string(", cases[i].string, ")[", commonChars, "] != string(", cases[begin].string, ")[", commonChars, "]\n"); + currentCase.end = i; + characterCases.append(currentCase); + currentCase.character = cases[i].string->at(commonChars); + currentCase.begin = i; + currentCase.end = i + 1; + } else + currentCase.end = i + 1; + } + characterCases.append(currentCase); + + Vector<int64_t> characterCaseValues; + for (unsigned i = 0; i < characterCases.size(); ++i) + characterCaseValues.append(characterCases[i].character); + + BinarySwitch binarySwitch(temp, characterCaseValues, BinarySwitch::Int32); + while (binarySwitch.advance(m_jit)) { + const CharacterCase& myCase = characterCases[binarySwitch.caseIndex()]; + emitBinarySwitchStringRecurse( + data, cases, commonChars + 1, myCase.begin, myCase.end, buffer, length, + temp, minLength, allLengthsEqual); + } + + addBranch(binarySwitch.fallThrough(), data->fallThrough.block); +} + +void SpeculativeJIT::emitSwitchStringOnString(SwitchData* data, GPRReg string) +{ + data->didUseJumpTable = true; + + bool canDoBinarySwitch = true; + unsigned totalLength = 0; + + for (unsigned i = data->cases.size(); i--;) { + StringImpl* string = data->cases[i].value.stringImpl(); + if (!string->is8Bit()) { + canDoBinarySwitch = false; + break; + } + if (string->length() > Options::maximumBinaryStringSwitchCaseLength()) { + canDoBinarySwitch = false; + break; + } + totalLength += string->length(); + } + + if (!canDoBinarySwitch || totalLength > Options::maximumBinaryStringSwitchTotalLength()) { + flushRegisters(); + callOperation( + operationSwitchString, string, data->switchTableIndex, string); + m_jit.exceptionCheck(); + m_jit.jump(string); + return; + } + + GPRTemporary length(this); + GPRTemporary temp(this); + + GPRReg lengthGPR = length.gpr(); + GPRReg tempGPR = temp.gpr(); + + m_jit.load32(MacroAssembler::Address(string, JSString::offsetOfLength()), lengthGPR); + m_jit.loadPtr(MacroAssembler::Address(string, JSString::offsetOfValue()), tempGPR); + + MacroAssembler::JumpList slowCases; + slowCases.append(m_jit.branchTestPtr(MacroAssembler::Zero, tempGPR)); + slowCases.append(m_jit.branchTest32( + MacroAssembler::Zero, + MacroAssembler::Address(tempGPR, StringImpl::flagsOffset()), + TrustedImm32(StringImpl::flagIs8Bit()))); + + m_jit.loadPtr(MacroAssembler::Address(tempGPR, StringImpl::dataOffset()), string); + + Vector<StringSwitchCase> cases; + for (unsigned i = 0; i < data->cases.size(); ++i) { + cases.append( + StringSwitchCase(data->cases[i].value.stringImpl(), data->cases[i].target.block)); + } + + std::sort(cases.begin(), cases.end()); + + emitBinarySwitchStringRecurse( + data, cases, 0, 0, cases.size(), string, lengthGPR, tempGPR, 0, false); + + slowCases.link(&m_jit); + silentSpillAllRegisters(string); + callOperation(operationSwitchString, string, data->switchTableIndex, string); + silentFillAllRegisters(string); + m_jit.exceptionCheck(); + m_jit.jump(string); +} + +void SpeculativeJIT::emitSwitchString(Node* node, SwitchData* data) +{ + switch (node->child1().useKind()) { + case StringIdentUse: { + SpeculateCellOperand op1(this, node->child1()); + GPRTemporary temp(this); + + GPRReg op1GPR = op1.gpr(); + GPRReg tempGPR = temp.gpr(); + + speculateString(node->child1(), op1GPR); + speculateStringIdentAndLoadStorage(node->child1(), op1GPR, tempGPR); + + Vector<int64_t> identifierCaseValues; + for (unsigned i = 0; i < data->cases.size(); ++i) { + identifierCaseValues.append( + static_cast<int64_t>(bitwise_cast<intptr_t>(data->cases[i].value.stringImpl()))); + } + + BinarySwitch binarySwitch(tempGPR, identifierCaseValues, BinarySwitch::IntPtr); + while (binarySwitch.advance(m_jit)) + jump(data->cases[binarySwitch.caseIndex()].target.block, ForceJump); + addBranch(binarySwitch.fallThrough(), data->fallThrough.block); + + noResult(node); + break; + } + + case StringUse: { + SpeculateCellOperand op1(this, node->child1()); + + GPRReg op1GPR = op1.gpr(); + + op1.use(); + + speculateString(node->child1(), op1GPR); + emitSwitchStringOnString(data, op1GPR); + noResult(node, UseChildrenCalledExplicitly); + break; + } + + case UntypedUse: { + JSValueOperand op1(this, node->child1()); + + JSValueRegs op1Regs = op1.jsValueRegs(); + + op1.use(); + + addBranch(m_jit.branchIfNotCell(op1Regs), data->fallThrough.block); + + addBranch(m_jit.branchIfNotString(op1Regs.payloadGPR()), data->fallThrough.block); + + emitSwitchStringOnString(data, op1Regs.payloadGPR()); + noResult(node, UseChildrenCalledExplicitly); + break; + } + + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } +} + +void SpeculativeJIT::emitSwitch(Node* node) +{ + SwitchData* data = node->switchData(); + switch (data->kind) { + case SwitchImm: { + emitSwitchImm(node, data); + return; + } + case SwitchChar: { + emitSwitchChar(node, data); + return; + } + case SwitchString: { + emitSwitchString(node, data); + return; + } + case SwitchCell: { + DFG_CRASH(m_jit.graph(), node, "Bad switch kind"); + return; + } } + RELEASE_ASSERT_NOT_REACHED(); +} + +void SpeculativeJIT::addBranch(const MacroAssembler::JumpList& jump, BasicBlock* destination) +{ + for (unsigned i = jump.jumps().size(); i--;) + addBranch(jump.jumps()[i], destination); +} + +void SpeculativeJIT::linkBranches() +{ + for (size_t i = 0; i < m_branches.size(); ++i) { + BranchRecord& branch = m_branches[i]; + branch.jump.linkTo(m_jit.blockHeads()[branch.destination->index], &m_jit); + } +} + +void SpeculativeJIT::compileStoreBarrier(Node* node) +{ + ASSERT(node->op() == StoreBarrier); + + SpeculateCellOperand base(this, node->child1()); + GPRTemporary scratch1(this); + GPRTemporary scratch2(this); + + writeBarrier(base.gpr(), scratch1.gpr(), scratch2.gpr()); + + noResult(node); +} + +void SpeculativeJIT::storeToWriteBarrierBuffer(GPRReg cell, GPRReg scratch1, GPRReg scratch2) +{ + ASSERT(scratch1 != scratch2); + WriteBarrierBuffer& writeBarrierBuffer = m_jit.vm()->heap.m_writeBarrierBuffer; + m_jit.load32(writeBarrierBuffer.currentIndexAddress(), scratch2); + JITCompiler::Jump needToFlush = m_jit.branch32(MacroAssembler::AboveOrEqual, scratch2, MacroAssembler::TrustedImm32(writeBarrierBuffer.capacity())); + + m_jit.add32(TrustedImm32(1), scratch2); + m_jit.store32(scratch2, writeBarrierBuffer.currentIndexAddress()); + + m_jit.move(TrustedImmPtr(writeBarrierBuffer.buffer()), scratch1); + // We use an offset of -sizeof(void*) because we already added 1 to scratch2. + m_jit.storePtr(cell, MacroAssembler::BaseIndex(scratch1, scratch2, MacroAssembler::ScalePtr, static_cast<int32_t>(-sizeof(void*)))); + + JITCompiler::Jump done = m_jit.jump(); + needToFlush.link(&m_jit); + + silentSpillAllRegisters(InvalidGPRReg); + callOperation(operationFlushWriteBarrierBuffer, cell); + silentFillAllRegisters(InvalidGPRReg); + + done.link(&m_jit); +} + +void SpeculativeJIT::writeBarrier(GPRReg ownerGPR, GPRReg scratch1, GPRReg scratch2) +{ + JITCompiler::Jump ownerIsRememberedOrInEden = m_jit.jumpIfIsRememberedOrInEden(ownerGPR); + storeToWriteBarrierBuffer(ownerGPR, scratch1, scratch2); + ownerIsRememberedOrInEden.link(&m_jit); +} + +void SpeculativeJIT::compilePutAccessorById(Node* node) +{ + SpeculateCellOperand base(this, node->child1()); + SpeculateCellOperand accessor(this, node->child2()); + + GPRReg baseGPR = base.gpr(); + GPRReg accessorGPR = accessor.gpr(); + + flushRegisters(); + callOperation(node->op() == PutGetterById ? operationPutGetterById : operationPutSetterById, NoResult, baseGPR, identifierUID(node->identifierNumber()), node->accessorAttributes(), accessorGPR); + m_jit.exceptionCheck(); + + noResult(node); +} + +void SpeculativeJIT::compilePutGetterSetterById(Node* node) +{ + SpeculateCellOperand base(this, node->child1()); + JSValueOperand getter(this, node->child2()); + JSValueOperand setter(this, node->child3()); + +#if USE(JSVALUE64) + GPRReg baseGPR = base.gpr(); + GPRReg getterGPR = getter.gpr(); + GPRReg setterGPR = setter.gpr(); + + flushRegisters(); + callOperation(operationPutGetterSetter, NoResult, baseGPR, identifierUID(node->identifierNumber()), node->accessorAttributes(), getterGPR, setterGPR); +#else + // These JSValues may be JSUndefined OR JSFunction*. + // At that time, + // 1. If the JSValue is JSUndefined, its payload becomes nullptr. + // 2. If the JSValue is JSFunction*, its payload becomes JSFunction*. + // So extract payload and pass it to operationPutGetterSetter. This hack is used as the same way in baseline JIT. + GPRReg baseGPR = base.gpr(); + JSValueRegs getterRegs = getter.jsValueRegs(); + JSValueRegs setterRegs = setter.jsValueRegs(); + + flushRegisters(); + callOperation(operationPutGetterSetter, NoResult, baseGPR, identifierUID(node->identifierNumber()), node->accessorAttributes(), getterRegs.payloadGPR(), setterRegs.payloadGPR()); +#endif + m_jit.exceptionCheck(); + + noResult(node); +} + +void SpeculativeJIT::compilePutAccessorByVal(Node* node) +{ + SpeculateCellOperand base(this, node->child1()); + JSValueOperand subscript(this, node->child2()); + SpeculateCellOperand accessor(this, node->child3()); + + auto operation = node->op() == PutGetterByVal ? operationPutGetterByVal : operationPutSetterByVal; +#if USE(JSVALUE64) + GPRReg baseGPR = base.gpr(); + GPRReg subscriptGPR = subscript.gpr(); + GPRReg accessorGPR = accessor.gpr(); + + flushRegisters(); + callOperation(operation, NoResult, baseGPR, subscriptGPR, node->accessorAttributes(), accessorGPR); +#else + GPRReg baseGPR = base.gpr(); + JSValueRegs subscriptRegs = subscript.jsValueRegs(); + GPRReg accessorGPR = accessor.gpr(); + + flushRegisters(); + callOperation(operation, NoResult, baseGPR, subscriptRegs.tagGPR(), subscriptRegs.payloadGPR(), node->accessorAttributes(), accessorGPR); +#endif + m_jit.exceptionCheck(); + + noResult(node); +} + } } // namespace JSC::DFG #endif |