|  | /* | 
|  | * Copyright (C) 2011-2020 Apple Inc. All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | 
|  | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 
|  | * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR | 
|  | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 
|  | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 
|  | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 
|  | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 
|  | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  | #include "DFGSpeculativeJIT.h" | 
|  |  | 
|  | #if ENABLE(DFG_JIT) | 
|  |  | 
|  | #include "BinarySwitch.h" | 
|  | #include "DFGAbstractInterpreterInlines.h" | 
|  | #include "DFGArrayifySlowPathGenerator.h" | 
|  | #include "DFGCallArrayAllocatorSlowPathGenerator.h" | 
|  | #include "DFGCallCreateDirectArgumentsSlowPathGenerator.h" | 
|  | #include "DFGCapabilities.h" | 
|  | #include "DFGClobberize.h" | 
|  | #include "DFGMayExit.h" | 
|  | #include "DFGOSRExitFuzz.h" | 
|  | #include "DFGSaneStringGetByValSlowPathGenerator.h" | 
|  | #include "DFGSlowPathGenerator.h" | 
|  | #include "DFGSnippetParams.h" | 
|  | #include "DirectArguments.h" | 
|  | #include "DisallowMacroScratchRegisterUsage.h" | 
|  | #include "JITBitAndGenerator.h" | 
|  | #include "JITBitOrGenerator.h" | 
|  | #include "JITBitXorGenerator.h" | 
|  | #include "JITDivGenerator.h" | 
|  | #include "JITLeftShiftGenerator.h" | 
|  | #include "JITRightShiftGenerator.h" | 
|  | #include "JSArrayIterator.h" | 
|  | #include "JSAsyncFunction.h" | 
|  | #include "JSAsyncGeneratorFunction.h" | 
|  | #include "JSCInlines.h" | 
|  | #include "JSGeneratorFunction.h" | 
|  | #include "JSImmutableButterfly.h" | 
|  | #include "JSLexicalEnvironment.h" | 
|  | #include "JSMapIterator.h" | 
|  | #include "JSPropertyNameEnumerator.h" | 
|  | #include "JSSetIterator.h" | 
|  | #include "RegExpObject.h" | 
|  | #include "ScopedArguments.h" | 
|  | #include "TypeProfilerLog.h" | 
|  | #include "WeakMapImpl.h" | 
|  | #include <wtf/BitVector.h> | 
|  | #include <wtf/Box.h> | 
|  | #include <wtf/MathExtras.h> | 
|  |  | 
|  | namespace JSC { namespace DFG { | 
|  |  | 
|  | DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(SpeculativeJIT); | 
|  |  | 
|  | SpeculativeJIT::SpeculativeJIT(JITCompiler& jit) | 
|  | : m_jit(jit) | 
|  | , m_graph(m_jit.graph()) | 
|  | , m_currentNode(nullptr) | 
|  | , m_lastGeneratedNode(LastNodeType) | 
|  | , m_indexInBlock(0) | 
|  | , m_generationInfo(m_jit.graph().frameRegisterCount()) | 
|  | , m_compileOkay(true) | 
|  | , m_state(m_jit.graph()) | 
|  | , m_interpreter(m_jit.graph(), m_state) | 
|  | , m_stream(&jit.jitCode()->variableEventStream) | 
|  | , m_minifiedGraph(&jit.jitCode()->minifiedDFG) | 
|  | { | 
|  | } | 
|  |  | 
|  | SpeculativeJIT::~SpeculativeJIT() | 
|  | { | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::emitAllocateRawObject(GPRReg resultGPR, RegisteredStructure structure, GPRReg storageGPR, unsigned numElements, unsigned vectorLength) | 
|  | { | 
|  | ASSERT(!isCopyOnWrite(structure->indexingMode())); | 
|  | IndexingType indexingType = structure->indexingType(); | 
|  | bool hasIndexingHeader = hasIndexedProperties(indexingType); | 
|  |  | 
|  | unsigned inlineCapacity = structure->inlineCapacity(); | 
|  | unsigned outOfLineCapacity = structure->outOfLineCapacity(); | 
|  |  | 
|  | GPRTemporary scratch(this); | 
|  | GPRTemporary scratch2(this); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  | GPRReg scratch2GPR = scratch2.gpr(); | 
|  |  | 
|  | ASSERT(vectorLength >= numElements); | 
|  | vectorLength = Butterfly::optimalContiguousVectorLength(structure.get(), vectorLength); | 
|  |  | 
|  | JITCompiler::JumpList slowCases; | 
|  |  | 
|  | size_t size = 0; | 
|  | if (hasIndexingHeader) | 
|  | size += vectorLength * sizeof(JSValue) + sizeof(IndexingHeader); | 
|  | size += outOfLineCapacity * sizeof(JSValue); | 
|  |  | 
|  | m_jit.move(TrustedImmPtr(nullptr), storageGPR); | 
|  |  | 
|  | VM& vm = this->vm(); | 
|  | if (size) { | 
|  | if (Allocator allocator = vm.jsValueGigacageAuxiliarySpace.allocatorForNonVirtual(size, AllocatorForMode::AllocatorIfExists)) { | 
|  | m_jit.emitAllocate(storageGPR, JITAllocator::constant(allocator), scratchGPR, scratch2GPR, slowCases); | 
|  |  | 
|  | m_jit.addPtr( | 
|  | TrustedImm32(outOfLineCapacity * sizeof(JSValue) + sizeof(IndexingHeader)), | 
|  | storageGPR); | 
|  |  | 
|  | if (hasIndexingHeader) | 
|  | m_jit.store32(TrustedImm32(vectorLength), MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength())); | 
|  | } else | 
|  | slowCases.append(m_jit.jump()); | 
|  | } | 
|  |  | 
|  | Allocator allocator; | 
|  | if (structure->type() == JSType::ArrayType) | 
|  | allocator = allocatorForNonVirtualConcurrently<JSArray>(vm, JSArray::allocationSize(inlineCapacity), AllocatorForMode::AllocatorIfExists); | 
|  | else | 
|  | allocator = allocatorForNonVirtualConcurrently<JSFinalObject>(vm, JSFinalObject::allocationSize(inlineCapacity), AllocatorForMode::AllocatorIfExists); | 
|  | if (allocator) { | 
|  | emitAllocateJSObject(resultGPR, JITAllocator::constant(allocator), scratchGPR, TrustedImmPtr(structure), storageGPR, scratch2GPR, slowCases); | 
|  | m_jit.emitInitializeInlineStorage(resultGPR, structure->inlineCapacity()); | 
|  | } else | 
|  | slowCases.append(m_jit.jump()); | 
|  |  | 
|  | // 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(makeUnique<CallArrayAllocatorSlowPathGenerator>( | 
|  | slowCases, this, operationNewRawObject, resultGPR, storageGPR, | 
|  | structure, vectorLength)); | 
|  |  | 
|  | if (numElements < vectorLength) { | 
|  | #if USE(JSVALUE64) | 
|  | if (hasDouble(structure->indexingType())) | 
|  | m_jit.move(TrustedImm64(bitwise_cast<int64_t>(PNaN)), scratchGPR); | 
|  | else | 
|  | m_jit.move(TrustedImm64(JSValue::encode(JSValue())), scratchGPR); | 
|  | for (unsigned i = numElements; i < vectorLength; ++i) | 
|  | m_jit.store64(scratchGPR, MacroAssembler::Address(storageGPR, sizeof(double) * i)); | 
|  | #else | 
|  | EncodedValueDescriptor value; | 
|  | if (hasDouble(structure->indexingType())) | 
|  | value.asInt64 = JSValue::encode(JSValue(JSValue::EncodeAsDouble, PNaN)); | 
|  | else | 
|  | value.asInt64 = JSValue::encode(JSValue()); | 
|  | 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))); | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | if (hasIndexingHeader) | 
|  | m_jit.store32(TrustedImm32(numElements), MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); | 
|  |  | 
|  | m_jit.emitInitializeOutOfLineStorage(storageGPR, structure->outOfLineCapacity()); | 
|  |  | 
|  | m_jit.mutatorFence(vm); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::emitGetLength(InlineCallFrame* inlineCallFrame, GPRReg lengthGPR, bool includeThis) | 
|  | { | 
|  | if (inlineCallFrame && !inlineCallFrame->isVarargs()) | 
|  | m_jit.move(TrustedImm32(inlineCallFrame->argumentCountIncludingThis - !includeThis), lengthGPR); | 
|  | else { | 
|  | VirtualRegister argumentCountRegister = m_jit.argumentCount(inlineCallFrame); | 
|  | m_jit.load32(JITCompiler::payloadFor(argumentCountRegister), lengthGPR); | 
|  | if (!includeThis) | 
|  | m_jit.sub32(TrustedImm32(1), lengthGPR); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::emitGetLength(CodeOrigin origin, GPRReg lengthGPR, bool includeThis) | 
|  | { | 
|  | emitGetLength(origin.inlineCallFrame(), lengthGPR, includeThis); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::emitGetCallee(CodeOrigin origin, GPRReg calleeGPR) | 
|  | { | 
|  | auto* inlineCallFrame = origin.inlineCallFrame(); | 
|  | if (inlineCallFrame) { | 
|  | if (inlineCallFrame->isClosureCall) { | 
|  | m_jit.loadPtr( | 
|  | JITCompiler::addressFor(inlineCallFrame->calleeRecovery.virtualRegister()), | 
|  | calleeGPR); | 
|  | } else { | 
|  | m_jit.move( | 
|  | TrustedImmPtr::weakPointer(m_jit.graph(), inlineCallFrame->calleeRecovery.constant().asCell()), | 
|  | calleeGPR); | 
|  | } | 
|  | } else | 
|  | m_jit.loadPtr(JITCompiler::addressFor(CallFrameSlot::callee), calleeGPR); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::emitGetArgumentStart(CodeOrigin origin, GPRReg startGPR) | 
|  | { | 
|  | m_jit.addPtr( | 
|  | TrustedImm32( | 
|  | JITCompiler::argumentsStart(origin).offset() * static_cast<int>(sizeof(Register))), | 
|  | GPRInfo::callFrameRegister, startGPR); | 
|  | } | 
|  |  | 
|  | MacroAssembler::Jump SpeculativeJIT::emitOSRExitFuzzCheck() | 
|  | { | 
|  | if (!Options::useOSRExitFuzz() | 
|  | || !canUseOSRExitFuzzing(m_jit.graph().baselineCodeBlockFor(m_origin.semantic)) | 
|  | || !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; | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, MacroAssembler::Jump jumpToFail) | 
|  | { | 
|  | 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(m_currentNode, node), this, m_stream->size())); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, const MacroAssembler::JumpList& jumpsToFail) | 
|  | { | 
|  | if (!m_compileOkay) | 
|  | return; | 
|  | 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(m_currentNode, node), this, m_stream->size())); | 
|  | } | 
|  |  | 
|  | OSRExitJumpPlaceholder SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node) | 
|  | { | 
|  | if (!m_compileOkay) | 
|  | return OSRExitJumpPlaceholder(); | 
|  | unsigned index = m_jit.jitCode()->osrExit.size(); | 
|  | m_jit.appendExitInfo(); | 
|  | m_jit.jitCode()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(m_currentNode, node), this, m_stream->size())); | 
|  | return OSRExitJumpPlaceholder(index); | 
|  | } | 
|  |  | 
|  | OSRExitJumpPlaceholder SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse) | 
|  | { | 
|  | return speculationCheck(kind, jsValueSource, nodeUse.node()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail) | 
|  | { | 
|  | speculationCheck(kind, jsValueSource, nodeUse.node(), jumpToFail); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, const MacroAssembler::JumpList& jumpsToFail) | 
|  | { | 
|  | speculationCheck(kind, jsValueSource, nodeUse.node(), jumpsToFail); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery) | 
|  | { | 
|  | if (!m_compileOkay) | 
|  | return; | 
|  | unsigned recoveryIndex = m_jit.jitCode()->appendSpeculationRecovery(recovery); | 
|  | m_jit.appendExitInfo(jumpToFail); | 
|  | m_jit.jitCode()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(m_currentNode, node), this, m_stream->size(), recoveryIndex)); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery) | 
|  | { | 
|  | speculationCheck(kind, jsValueSource, nodeUse.node(), jumpToFail, recovery); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::emitInvalidationPoint(Node* node) | 
|  | { | 
|  | if (!m_compileOkay) | 
|  | return; | 
|  | OSRExitCompilationInfo& info = m_jit.appendExitInfo(JITCompiler::JumpList()); | 
|  | m_jit.jitCode()->appendOSRExit(OSRExit( | 
|  | UncountableInvalidation, JSValueSource(), MethodOfGettingAValueProfile(), | 
|  | this, m_stream->size())); | 
|  | info.m_replacementSource = m_jit.watchpointLabel(); | 
|  | ASSERT(info.m_replacementSource.isSet()); | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::unreachable(Node* node) | 
|  | { | 
|  | m_compileOkay = false; | 
|  | m_jit.abortWithReason(DFGUnreachableNode, node->op()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::terminateSpeculativeExecution(ExitKind kind, JSValueRegs jsValueRegs, Node* node) | 
|  | { | 
|  | 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) | 
|  | { | 
|  | terminateSpeculativeExecution(kind, jsValueRegs, nodeUse.node()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::typeCheck(JSValueSource source, Edge edge, SpeculatedType typesPassedThrough, MacroAssembler::Jump jumpToFail, ExitKind exitKind) | 
|  | { | 
|  | ASSERT(needsTypeCheck(edge, typesPassedThrough)); | 
|  | m_interpreter.filter(edge, typesPassedThrough); | 
|  | speculationCheck(exitKind, source, edge.node(), jumpToFail); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::typeCheck(JSValueSource source, Edge edge, SpeculatedType typesPassedThrough, MacroAssembler::JumpList jumpListToFail, ExitKind exitKind) | 
|  | { | 
|  | ASSERT(needsTypeCheck(edge, typesPassedThrough)); | 
|  | m_interpreter.filter(edge, typesPassedThrough); | 
|  | speculationCheck(exitKind, source, edge.node(), jumpListToFail); | 
|  | } | 
|  |  | 
|  | RegisterSet SpeculativeJIT::usedRegisters() | 
|  | { | 
|  | 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); | 
|  | } | 
|  |  | 
|  | // FIXME: This is overly conservative. We could subtract out those callee-saves that we | 
|  | // actually saved. | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=185686 | 
|  | result.merge(RegisterSet::stubUnavailableRegisters()); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::addSlowPathGenerator(std::unique_ptr<SlowPathGenerator> slowPathGenerator) | 
|  | { | 
|  | m_slowPathGenerators.append(WTFMove(slowPathGenerator)); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::addSlowPathGeneratorLambda(Function<void()>&& lambda) | 
|  | { | 
|  | m_slowPathLambdas.append(SlowPathLambda{ WTFMove(lambda), m_currentNode, static_cast<unsigned>(m_stream->size()) }); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::runSlowPathGenerators(PCToCodeOriginMapBuilder& pcToCodeOriginMapBuilder) | 
|  | { | 
|  | for (auto& slowPathGenerator : m_slowPathGenerators) { | 
|  | pcToCodeOriginMapBuilder.appendItem(m_jit.labelIgnoringWatchpoints(), slowPathGenerator->origin().semantic); | 
|  | slowPathGenerator->generate(this); | 
|  | } | 
|  | for (auto& slowPathLambda : m_slowPathLambdas) { | 
|  | Node* currentNode = slowPathLambda.currentNode; | 
|  | m_currentNode = currentNode; | 
|  | m_outOfLineStreamIndex = slowPathLambda.streamIndex; | 
|  | pcToCodeOriginMapBuilder.appendItem(m_jit.labelIgnoringWatchpoints(), currentNode->origin.semantic); | 
|  | slowPathLambda.generator(); | 
|  | m_outOfLineStreamIndex = WTF::nullopt; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::clearGenerationInfo() | 
|  | { | 
|  | for (unsigned i = 0; i < m_generationInfo.size(); ++i) | 
|  | m_generationInfo[i] = GenerationInfo(); | 
|  | m_gprs = RegisterBank<GPRInfo>(); | 
|  | m_fprs = RegisterBank<FPRInfo>(); | 
|  | } | 
|  |  | 
|  | SilentRegisterSavePlan SpeculativeJIT::silentSavePlanForGPR(VirtualRegister spillMe, GPRReg source) | 
|  | { | 
|  | GenerationInfo& info = generationInfoFromVirtualRegister(spillMe); | 
|  | Node* node = info.node(); | 
|  | DataFormat registerFormat = info.registerFormat(); | 
|  | ASSERT(registerFormat != DataFormatNone); | 
|  | ASSERT(registerFormat != DataFormatDouble); | 
|  |  | 
|  | SilentSpillAction spillAction; | 
|  | SilentFillAction fillAction; | 
|  |  | 
|  | if (!info.needsSpill()) | 
|  | spillAction = DoNothingForSpill; | 
|  | else { | 
|  | #if USE(JSVALUE64) | 
|  | ASSERT(info.gpr() == source); | 
|  | 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; | 
|  | } | 
|  | #elif USE(JSVALUE32_64) | 
|  | if (registerFormat & DataFormatJS) { | 
|  | ASSERT(info.tagGPR() == source || info.payloadGPR() == source); | 
|  | spillAction = source == info.tagGPR() ? Store32Tag : Store32Payload; | 
|  | } else { | 
|  | ASSERT(info.gpr() == source); | 
|  | spillAction = Store32Payload; | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | if (registerFormat == DataFormatInt32) { | 
|  | ASSERT(info.gpr() == source); | 
|  | ASSERT(isJSInt32(info.registerFormat())); | 
|  | if (node->hasConstant()) { | 
|  | 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(node->isBooleanConstant()); | 
|  | fillAction = SetBooleanConstant; | 
|  | } else | 
|  | fillAction = Load32Payload; | 
|  | #endif | 
|  | } else if (registerFormat == DataFormatCell) { | 
|  | ASSERT(info.gpr() == source); | 
|  | if (node->hasConstant()) { | 
|  | DFG_ASSERT(m_jit.graph(), m_currentNode, node->isCellConstant()); | 
|  | node->asCell(); // To get the assertion. | 
|  | fillAction = SetCellConstant; | 
|  | } else { | 
|  | #if USE(JSVALUE64) | 
|  | fillAction = LoadPtr; | 
|  | #else | 
|  | fillAction = Load32Payload; | 
|  | #endif | 
|  | } | 
|  | } 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 (node->isCellConstant()) | 
|  | fillAction = SetTrustedJSConstant; | 
|  | else | 
|  | fillAction = SetJSConstant; | 
|  | } else if (info.spillFormat() == DataFormatInt32) { | 
|  | ASSERT(registerFormat == DataFormatJSInt32); | 
|  | fillAction = Load32PayloadBoxInt; | 
|  | } else | 
|  | fillAction = Load64; | 
|  | #else | 
|  | ASSERT(info.tagGPR() == source || info.payloadGPR() == source); | 
|  | if (node->hasConstant()) | 
|  | fillAction = info.tagGPR() == source ? SetJSConstantTag : SetJSConstantPayload; | 
|  | else if (info.payloadGPR() == source) | 
|  | fillAction = Load32Payload; | 
|  | else { // Fill the Tag | 
|  | switch (info.spillFormat()) { | 
|  | case DataFormatInt32: | 
|  | ASSERT(registerFormat == DataFormatJSInt32); | 
|  | fillAction = SetInt32Tag; | 
|  | break; | 
|  | case DataFormatCell: | 
|  | ASSERT(registerFormat == DataFormatJSCell); | 
|  | fillAction = SetCellTag; | 
|  | break; | 
|  | case DataFormatBoolean: | 
|  | ASSERT(registerFormat == DataFormatJSBoolean); | 
|  | fillAction = SetBooleanTag; | 
|  | break; | 
|  | default: | 
|  | fillAction = Load32Tag; | 
|  | break; | 
|  | } | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | return SilentRegisterSavePlan(spillAction, fillAction, node, source); | 
|  | } | 
|  |  | 
|  | SilentRegisterSavePlan SpeculativeJIT::silentSavePlanForFPR(VirtualRegister spillMe, FPRReg source) | 
|  | { | 
|  | GenerationInfo& info = generationInfoFromVirtualRegister(spillMe); | 
|  | Node* node = info.node(); | 
|  | ASSERT(info.registerFormat() == DataFormatDouble); | 
|  |  | 
|  | SilentSpillAction spillAction; | 
|  | SilentFillAction fillAction; | 
|  |  | 
|  | if (!info.needsSpill()) | 
|  | spillAction = DoNothingForSpill; | 
|  | else { | 
|  | ASSERT(!node->hasConstant()); | 
|  | ASSERT(info.spillFormat() == DataFormatNone); | 
|  | ASSERT(info.fpr() == source); | 
|  | spillAction = StoreDouble; | 
|  | } | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | if (node->hasConstant()) { | 
|  | node->asNumber(); // To get the assertion. | 
|  | fillAction = SetDoubleConstant; | 
|  | } else { | 
|  | ASSERT(info.spillFormat() == DataFormatNone || info.spillFormat() == DataFormatDouble); | 
|  | fillAction = LoadDouble; | 
|  | } | 
|  | #elif USE(JSVALUE32_64) | 
|  | ASSERT(info.registerFormat() == DataFormatDouble); | 
|  | if (node->hasConstant()) { | 
|  | node->asNumber(); // To get the assertion. | 
|  | fillAction = SetDoubleConstant; | 
|  | } else | 
|  | fillAction = LoadDouble; | 
|  | #endif | 
|  |  | 
|  | return SilentRegisterSavePlan(spillAction, fillAction, node, source); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::silentSpill(const SilentRegisterSavePlan& plan) | 
|  | { | 
|  | switch (plan.spillAction()) { | 
|  | case DoNothingForSpill: | 
|  | break; | 
|  | case Store32Tag: | 
|  | m_jit.store32(plan.gpr(), JITCompiler::tagFor(plan.node()->virtualRegister())); | 
|  | break; | 
|  | case Store32Payload: | 
|  | m_jit.store32(plan.gpr(), JITCompiler::payloadFor(plan.node()->virtualRegister())); | 
|  | break; | 
|  | case StorePtr: | 
|  | m_jit.storePtr(plan.gpr(), JITCompiler::addressFor(plan.node()->virtualRegister())); | 
|  | break; | 
|  | #if USE(JSVALUE64) | 
|  | case Store64: | 
|  | m_jit.store64(plan.gpr(), JITCompiler::addressFor(plan.node()->virtualRegister())); | 
|  | break; | 
|  | #endif | 
|  | case StoreDouble: | 
|  | m_jit.storeDouble(plan.fpr(), JITCompiler::addressFor(plan.node()->virtualRegister())); | 
|  | break; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::silentFill(const SilentRegisterSavePlan& plan) | 
|  | { | 
|  | switch (plan.fillAction()) { | 
|  | case DoNothingForFill: | 
|  | break; | 
|  | case SetInt32Constant: | 
|  | m_jit.move(Imm32(plan.node()->asInt32()), plan.gpr()); | 
|  | break; | 
|  | #if USE(JSVALUE64) | 
|  | case SetInt52Constant: | 
|  | m_jit.move(Imm64(plan.node()->asAnyInt() << JSValue::int52ShiftAmount), plan.gpr()); | 
|  | break; | 
|  | case SetStrictInt52Constant: | 
|  | m_jit.move(Imm64(plan.node()->asAnyInt()), plan.gpr()); | 
|  | break; | 
|  | #endif // USE(JSVALUE64) | 
|  | case SetBooleanConstant: | 
|  | m_jit.move(TrustedImm32(plan.node()->asBoolean()), plan.gpr()); | 
|  | break; | 
|  | case SetCellConstant: | 
|  | ASSERT(plan.node()->constant()->value().isCell()); | 
|  | m_jit.move(TrustedImmPtr(plan.node()->constant()), plan.gpr()); | 
|  | break; | 
|  | #if USE(JSVALUE64) | 
|  | case SetTrustedJSConstant: | 
|  | m_jit.move(valueOfJSConstantAsImm64(plan.node()).asTrustedImm64(), plan.gpr()); | 
|  | break; | 
|  | case SetJSConstant: | 
|  | m_jit.move(valueOfJSConstantAsImm64(plan.node()), plan.gpr()); | 
|  | break; | 
|  | case SetDoubleConstant: | 
|  | m_jit.moveDouble(Imm64(reinterpretDoubleToInt64(plan.node()->asNumber())), plan.fpr()); | 
|  | break; | 
|  | case Load32PayloadBoxInt: | 
|  | m_jit.load32(JITCompiler::payloadFor(plan.node()->virtualRegister()), plan.gpr()); | 
|  | m_jit.or64(GPRInfo::numberTagRegister, plan.gpr()); | 
|  | break; | 
|  | 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 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(plan.node()->asJSValue().tag()), plan.gpr()); | 
|  | break; | 
|  | case SetJSConstantPayload: | 
|  | m_jit.move(Imm32(plan.node()->asJSValue().payload()), plan.gpr()); | 
|  | break; | 
|  | case SetInt32Tag: | 
|  | m_jit.move(TrustedImm32(JSValue::Int32Tag), plan.gpr()); | 
|  | break; | 
|  | case SetCellTag: | 
|  | m_jit.move(TrustedImm32(JSValue::CellTag), plan.gpr()); | 
|  | break; | 
|  | case SetBooleanTag: | 
|  | m_jit.move(TrustedImm32(JSValue::BooleanTag), plan.gpr()); | 
|  | break; | 
|  | case SetDoubleConstant: | 
|  | m_jit.loadDouble(TrustedImmPtr(m_jit.addressOfDoubleConstant(plan.node())), plan.fpr()); | 
|  | break; | 
|  | #endif | 
|  | case Load32Tag: | 
|  | m_jit.load32(JITCompiler::tagFor(plan.node()->virtualRegister()), plan.gpr()); | 
|  | break; | 
|  | case Load32Payload: | 
|  | m_jit.load32(JITCompiler::payloadFor(plan.node()->virtualRegister()), plan.gpr()); | 
|  | break; | 
|  | case LoadPtr: | 
|  | m_jit.loadPtr(JITCompiler::addressFor(plan.node()->virtualRegister()), plan.gpr()); | 
|  | break; | 
|  | #if USE(JSVALUE64) | 
|  | 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()); | 
|  | break; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  | } | 
|  |  | 
|  | JITCompiler::JumpList SpeculativeJIT::jumpSlowForUnwantedArrayMode(GPRReg tempGPR, ArrayMode arrayMode) | 
|  | { | 
|  | JITCompiler::JumpList result; | 
|  |  | 
|  | IndexingType indexingModeMask = IsArray | IndexingShapeMask; | 
|  | if (arrayMode.action() == Array::Write) | 
|  | indexingModeMask |= CopyOnWrite; | 
|  |  | 
|  | switch (arrayMode.type()) { | 
|  | case Array::Int32: | 
|  | case Array::Double: | 
|  | case Array::Contiguous: | 
|  | case Array::Undecided: | 
|  | case Array::ArrayStorage: { | 
|  | IndexingType shape = arrayMode.shapeMask(); | 
|  | switch (arrayMode.arrayClass()) { | 
|  | case Array::OriginalArray: | 
|  | case Array::OriginalCopyOnWriteArray: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return result; | 
|  |  | 
|  | case Array::Array: | 
|  | m_jit.and32(TrustedImm32(indexingModeMask), tempGPR); | 
|  | result.append(m_jit.branch32( | 
|  | MacroAssembler::NotEqual, tempGPR, TrustedImm32(IsArray | shape))); | 
|  | return result; | 
|  |  | 
|  | case Array::NonArray: | 
|  | case Array::OriginalNonArray: | 
|  | m_jit.and32(TrustedImm32(indexingModeMask), tempGPR); | 
|  | result.append(m_jit.branch32( | 
|  | MacroAssembler::NotEqual, tempGPR, TrustedImm32(shape))); | 
|  | return result; | 
|  |  | 
|  | case Array::PossiblyArray: | 
|  | m_jit.and32(TrustedImm32(indexingModeMask & ~IsArray), tempGPR); | 
|  | result.append(m_jit.branch32(MacroAssembler::NotEqual, tempGPR, TrustedImm32(shape))); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | case Array::SlowPutArrayStorage: { | 
|  | ASSERT(!arrayMode.isJSArrayWithOriginalStructure()); | 
|  |  | 
|  | switch (arrayMode.arrayClass()) { | 
|  | case Array::OriginalArray: | 
|  | case Array::OriginalCopyOnWriteArray: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return result; | 
|  |  | 
|  | case Array::Array: | 
|  | result.append( | 
|  | m_jit.branchTest32( | 
|  | MacroAssembler::Zero, tempGPR, MacroAssembler::TrustedImm32(IsArray))); | 
|  | break; | 
|  |  | 
|  | case Array::NonArray: | 
|  | case Array::OriginalNonArray: | 
|  | result.append( | 
|  | m_jit.branchTest32( | 
|  | MacroAssembler::NonZero, tempGPR, MacroAssembler::TrustedImm32(IsArray))); | 
|  | break; | 
|  |  | 
|  | case Array::PossiblyArray: | 
|  | break; | 
|  | } | 
|  |  | 
|  | m_jit.and32(TrustedImm32(IndexingShapeMask), tempGPR); | 
|  | m_jit.sub32(TrustedImm32(ArrayStorageShape), tempGPR); | 
|  | result.append( | 
|  | m_jit.branch32( | 
|  | MacroAssembler::Above, tempGPR, | 
|  | TrustedImm32(SlowPutArrayStorageShape - ArrayStorageShape))); | 
|  | return result; | 
|  | } | 
|  | default: | 
|  | CRASH(); | 
|  | break; | 
|  | } | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::checkArray(Node* node) | 
|  | { | 
|  | ArrayMode arrayMode = node->arrayMode(); | 
|  | ASSERT(arrayMode.isSpecific()); | 
|  | ASSERT(!arrayMode.doesConversion()); | 
|  |  | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | GPRReg baseReg = base.gpr(); | 
|  |  | 
|  | if (arrayMode.alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1()))) { | 
|  | // We can purge Empty check completely in this case of CheckArrayOrEmpty since CellUse only accepts SpecCell | SpecEmpty. | 
|  | ASSERT(typeFilterFor(node->child1().useKind()) & SpecEmpty); | 
|  | noResult(m_currentNode); | 
|  | return; | 
|  | } | 
|  |  | 
|  | Optional<GPRTemporary> temp; | 
|  | Optional<GPRReg> tempGPR; | 
|  | switch (arrayMode.type()) { | 
|  | case Array::Int32: | 
|  | case Array::Double: | 
|  | case Array::Contiguous: | 
|  | case Array::Undecided: | 
|  | case Array::ArrayStorage: | 
|  | case Array::SlowPutArrayStorage: { | 
|  | temp.emplace(this); | 
|  | tempGPR = temp->gpr(); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | CCallHelpers::Jump isEmpty; | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | if (node->op() == CheckArrayOrEmpty) { | 
|  | if (m_interpreter.forNode(node->child1()).m_type & SpecEmpty) | 
|  | isEmpty = m_jit.branchIfEmpty(baseReg); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | switch (arrayMode.type()) { | 
|  | case Array::String: | 
|  | RELEASE_ASSERT_NOT_REACHED(); // Should have been a Phantom(String:) | 
|  | return; | 
|  | case Array::Int32: | 
|  | case Array::Double: | 
|  | case Array::Contiguous: | 
|  | case Array::Undecided: | 
|  | case Array::ArrayStorage: | 
|  | case Array::SlowPutArrayStorage: { | 
|  | m_jit.load8(MacroAssembler::Address(baseReg, JSCell::indexingTypeAndMiscOffset()), tempGPR.value()); | 
|  | speculationCheck( | 
|  | BadIndexingType, JSValueSource::unboxedCell(baseReg), nullptr, | 
|  | jumpSlowForUnwantedArrayMode(tempGPR.value(), arrayMode)); | 
|  | break; | 
|  | } | 
|  | case Array::DirectArguments: | 
|  | speculateCellTypeWithoutTypeFiltering(node->child1(), baseReg, DirectArgumentsType); | 
|  | break; | 
|  | case Array::ScopedArguments: | 
|  | speculateCellTypeWithoutTypeFiltering(node->child1(), baseReg, ScopedArgumentsType); | 
|  | break; | 
|  | default: { | 
|  | DFG_ASSERT(m_graph, node, arrayMode.isSomeTypedArrayView()); | 
|  |  | 
|  | if (arrayMode.type() == Array::AnyTypedArray) | 
|  | speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), nullptr, m_jit.branchIfNotType(baseReg, JSTypeRange { JSType(FirstTypedArrayType), JSType(LastTypedArrayTypeExcludingDataView) })); | 
|  | else | 
|  | speculateCellTypeWithoutTypeFiltering(node->child1(), baseReg, typeForTypedArrayType(arrayMode.typedArrayType())); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (isEmpty.isSet()) | 
|  | isEmpty.link(&m_jit); | 
|  | noResult(m_currentNode); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::arrayify(Node* node, GPRReg baseReg, GPRReg propertyReg) | 
|  | { | 
|  | ASSERT(node->arrayMode().doesConversion()); | 
|  |  | 
|  | GPRTemporary temp(this); | 
|  | GPRTemporary structure; | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  | GPRReg structureGPR = InvalidGPRReg; | 
|  |  | 
|  | if (node->op() != ArrayifyToStructure) { | 
|  | GPRTemporary realStructure(this); | 
|  | structure.adopt(realStructure); | 
|  | structureGPR = structure.gpr(); | 
|  | } | 
|  |  | 
|  | // We can skip all that comes next if we already have array storage. | 
|  | MacroAssembler::JumpList slowPath; | 
|  |  | 
|  | if (node->op() == ArrayifyToStructure) { | 
|  | ASSERT(!isCopyOnWrite(node->structure()->indexingMode())); | 
|  | ASSERT((node->structure()->indexingType() & IndexingShapeMask) == node->arrayMode().shapeMask()); | 
|  | slowPath.append(m_jit.branchWeakStructure( | 
|  | JITCompiler::NotEqual, | 
|  | JITCompiler::Address(baseReg, JSCell::structureIDOffset()), | 
|  | node->structure())); | 
|  | } else { | 
|  | m_jit.load8( | 
|  | MacroAssembler::Address(baseReg, JSCell::indexingTypeAndMiscOffset()), tempGPR); | 
|  |  | 
|  | slowPath.append(jumpSlowForUnwantedArrayMode(tempGPR, node->arrayMode())); | 
|  | } | 
|  |  | 
|  | addSlowPathGenerator(makeUnique<ArrayifySlowPathGenerator>( | 
|  | slowPath, this, node, baseReg, propertyReg, tempGPR, structureGPR)); | 
|  |  | 
|  | noResult(m_currentNode); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::arrayify(Node* node) | 
|  | { | 
|  | ASSERT(node->arrayMode().isSpecific()); | 
|  |  | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  |  | 
|  | if (!node->child2()) { | 
|  | arrayify(node, base.gpr(), InvalidGPRReg); | 
|  | return; | 
|  | } | 
|  |  | 
|  | SpeculateInt32Operand property(this, node->child2()); | 
|  |  | 
|  | arrayify(node, base.gpr(), property.gpr()); | 
|  | } | 
|  |  | 
|  | GPRReg SpeculativeJIT::fillStorage(Edge edge) | 
|  | { | 
|  | VirtualRegister virtualRegister = edge->virtualRegister(); | 
|  | GenerationInfo& info = generationInfoFromVirtualRegister(virtualRegister); | 
|  |  | 
|  | switch (info.registerFormat()) { | 
|  | case DataFormatNone: { | 
|  | if (info.spillFormat() == DataFormatStorage) { | 
|  | GPRReg gpr = allocate(); | 
|  | m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); | 
|  | m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), gpr); | 
|  | info.fillStorage(*m_stream, gpr); | 
|  | return gpr; | 
|  | } | 
|  |  | 
|  | // Must be a cell; fill it as a cell and then return the pointer. | 
|  | return fillSpeculateCell(edge); | 
|  | } | 
|  |  | 
|  | case DataFormatStorage: { | 
|  | GPRReg gpr = info.gpr(); | 
|  | m_gprs.lock(gpr); | 
|  | return gpr; | 
|  | } | 
|  |  | 
|  | default: | 
|  | return fillSpeculateCell(edge); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::useChildren(Node* node) | 
|  | { | 
|  | if (node->flags() & NodeHasVarArgs) { | 
|  | for (unsigned childIdx = node->firstChild(); childIdx < node->firstChild() + node->numChildren(); childIdx++) { | 
|  | if (!!m_jit.graph().m_varArgChildren[childIdx]) | 
|  | use(m_jit.graph().m_varArgChildren[childIdx]); | 
|  | } | 
|  | } else { | 
|  | Edge child1 = node->child1(); | 
|  | if (!child1) { | 
|  | ASSERT(!node->child2() && !node->child3()); | 
|  | return; | 
|  | } | 
|  | use(child1); | 
|  |  | 
|  | Edge child2 = node->child2(); | 
|  | if (!child2) { | 
|  | ASSERT(!node->child3()); | 
|  | return; | 
|  | } | 
|  | use(child2); | 
|  |  | 
|  | Edge child3 = node->child3(); | 
|  | if (!child3) | 
|  | return; | 
|  | use(child3); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetById(Node* node, AccessType accessType) | 
|  | { | 
|  | ASSERT(accessType == AccessType::GetById || accessType == AccessType::GetByIdDirect || accessType == AccessType::TryGetById); | 
|  |  | 
|  | switch (node->child1().useKind()) { | 
|  | case CellUse: { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | JSValueRegsTemporary result(this, Reuse, base); | 
|  |  | 
|  | JSValueRegs baseRegs = JSValueRegs::payloadOnly(base.gpr()); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | base.use(); | 
|  |  | 
|  | cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->cacheableIdentifier(), JITCompiler::Jump(), NeedToSpill, accessType); | 
|  |  | 
|  | jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case UntypedUse: { | 
|  | JSValueOperand base(this, node->child1()); | 
|  | JSValueRegsTemporary result(this, Reuse, base); | 
|  |  | 
|  | JSValueRegs baseRegs = base.jsValueRegs(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | base.use(); | 
|  |  | 
|  | JITCompiler::Jump notCell = m_jit.branchIfNotCell(baseRegs); | 
|  |  | 
|  | cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->cacheableIdentifier(), notCell, NeedToSpill, accessType); | 
|  |  | 
|  | jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly); | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: | 
|  | DFG_CRASH(m_jit.graph(), node, "Bad use kind"); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetByIdFlush(Node* node, AccessType accessType) | 
|  | { | 
|  | switch (node->child1().useKind()) { | 
|  | case CellUse: { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | JSValueRegs baseRegs = JSValueRegs::payloadOnly(base.gpr()); | 
|  |  | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | base.use(); | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->cacheableIdentifier(), JITCompiler::Jump(), DontSpill, accessType); | 
|  |  | 
|  | jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case UntypedUse: { | 
|  | JSValueOperand base(this, node->child1()); | 
|  | JSValueRegs baseRegs = base.jsValueRegs(); | 
|  |  | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | base.use(); | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | JITCompiler::Jump notCell = m_jit.branchIfNotCell(baseRegs); | 
|  |  | 
|  | cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->cacheableIdentifier(), notCell, DontSpill, accessType); | 
|  |  | 
|  | jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly); | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: | 
|  | DFG_CRASH(m_jit.graph(), node, "Bad use kind"); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileDeleteById(Node* node) | 
|  | { | 
|  | if (node->child1().useKind() == CellUse) { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | JSValueRegsTemporary result(this); | 
|  | GPRTemporary scratch(this); | 
|  |  | 
|  | JITCompiler::JumpList slowCases; | 
|  |  | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  | GPRReg resultGPR = resultRegs.payloadGPR(); | 
|  |  | 
|  | CodeOrigin codeOrigin = node->origin.semantic; | 
|  | CallSiteIndex callSite = m_jit.recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded(codeOrigin, m_stream->size()); | 
|  | RegisterSet usedRegisters = this->usedRegisters(); | 
|  |  | 
|  | JITDelByIdGenerator gen( | 
|  | m_jit.codeBlock(), codeOrigin, callSite, usedRegisters, node->cacheableIdentifier(), | 
|  | JSValueRegs::payloadOnly(baseGPR), resultRegs, scratchGPR); | 
|  |  | 
|  | gen.generateFastPath(m_jit); | 
|  | slowCases.append(gen.slowPathJump()); | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | std::unique_ptr<SlowPathGenerator> slowPath = slowPathCall( | 
|  | slowCases, this, operationDeleteByIdOptimize, | 
|  | resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(codeOrigin)), gen.stubInfo(), JSValueRegs(baseGPR), node->cacheableIdentifier().rawBits(), TrustedImm32(node->ecmaMode().value())); | 
|  | #else | 
|  | std::unique_ptr<SlowPathGenerator> slowPath = slowPathCall( | 
|  | slowCases, this, operationDeleteByIdOptimize, | 
|  | resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(codeOrigin)), gen.stubInfo(), CCallHelpers::CellValue(baseGPR), node->cacheableIdentifier().rawBits(), TrustedImm32(node->ecmaMode().value())); | 
|  | #endif | 
|  |  | 
|  | m_jit.addDelById(gen, slowPath.get()); | 
|  | addSlowPathGenerator(WTFMove(slowPath)); | 
|  |  | 
|  | unblessedBooleanResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // FIXME: We should use IC even if child1 is UntypedUse. In that case, we should emit write-barrier after the fast path of IC. | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=209397 | 
|  | ASSERT(node->child1().useKind() == UntypedUse); | 
|  | JSValueOperand base(this, node->child1()); | 
|  |  | 
|  | JSValueRegs baseRegs = base.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | callOperation(operationDeleteByIdGeneric, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), nullptr, baseRegs, node->cacheableIdentifier().rawBits(), TrustedImm32(node->ecmaMode().value())); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | unblessedBooleanResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileDeleteByVal(Node* node) | 
|  | { | 
|  | if (node->child1().useKind() == CellUse) { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  |  | 
|  | JSValueOperand key(this, node->child2(), ManualOperandSpeculation); | 
|  | speculate(node, node->child2()); | 
|  |  | 
|  | JSValueRegsTemporary result(this, Reuse, key); | 
|  | GPRTemporary scratch(this); | 
|  |  | 
|  | JITCompiler::JumpList slowCases; | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | JSValueRegs keyRegs = key.jsValueRegs(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  | GPRReg resultGPR = resultRegs.payloadGPR(); | 
|  |  | 
|  | if (needsTypeCheck(node->child2(), SpecCell)) | 
|  | slowCases.append(m_jit.branchIfNotCell(keyRegs)); | 
|  |  | 
|  | CodeOrigin codeOrigin = node->origin.semantic; | 
|  | CallSiteIndex callSite = m_jit.recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded(codeOrigin, m_stream->size()); | 
|  | RegisterSet usedRegisters = this->usedRegisters(); | 
|  |  | 
|  | JITDelByValGenerator gen( | 
|  | m_jit.codeBlock(), codeOrigin, callSite, usedRegisters, | 
|  | JSValueRegs::payloadOnly(baseGPR), keyRegs, resultRegs, scratchGPR); | 
|  |  | 
|  | gen.generateFastPath(m_jit); | 
|  | slowCases.append(gen.slowPathJump()); | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | std::unique_ptr<SlowPathGenerator> slowPath = slowPathCall( | 
|  | slowCases, this, operationDeleteByValOptimize, | 
|  | resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(codeOrigin)), gen.stubInfo(), JSValueRegs(baseGPR), keyRegs, TrustedImm32(node->ecmaMode().value())); | 
|  | #else | 
|  | std::unique_ptr<SlowPathGenerator> slowPath = slowPathCall( | 
|  | slowCases, this, operationDeleteByValOptimize, | 
|  | resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(codeOrigin)), gen.stubInfo(), CCallHelpers::CellValue(baseGPR), keyRegs, TrustedImm32(node->ecmaMode().value())); | 
|  | #endif | 
|  |  | 
|  | m_jit.addDelByVal(gen, slowPath.get()); | 
|  | addSlowPathGenerator(WTFMove(slowPath)); | 
|  |  | 
|  | unblessedBooleanResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // FIXME: We should use IC even if child1 is UntypedUse. In that case, we should emit write-barrier after the fast path of IC. | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=209397 | 
|  | JSValueOperand base(this, node->child1()); | 
|  | JSValueOperand key(this, node->child2()); | 
|  |  | 
|  | JSValueRegs baseRegs = base.jsValueRegs(); | 
|  | JSValueRegs keyRegs = key.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | callOperation(operationDeleteByValGeneric, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), nullptr, baseRegs, keyRegs, TrustedImm32(node->ecmaMode().value())); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | unblessedBooleanResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileInById(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | JSValueRegsTemporary result(this, Reuse, base, PayloadWord); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | base.use(); | 
|  |  | 
|  | CodeOrigin codeOrigin = node->origin.semantic; | 
|  | CallSiteIndex callSite = m_jit.recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded(codeOrigin, m_stream->size()); | 
|  | RegisterSet usedRegisters = this->usedRegisters(); | 
|  | JITInByIdGenerator gen( | 
|  | m_jit.codeBlock(), codeOrigin, callSite, usedRegisters, node->cacheableIdentifier(), | 
|  | JSValueRegs::payloadOnly(baseGPR), resultRegs); | 
|  | gen.generateFastPath(m_jit); | 
|  |  | 
|  | auto slowPath = slowPathCall( | 
|  | gen.slowPathJump(), this, operationInByIdOptimize, | 
|  | NeedToSpill, ExceptionCheckRequirement::CheckNeeded, | 
|  | resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), gen.stubInfo(), CCallHelpers::CellValue(baseGPR), node->cacheableIdentifier().rawBits()); | 
|  |  | 
|  | m_jit.addInById(gen, slowPath.get()); | 
|  | addSlowPathGenerator(WTFMove(slowPath)); | 
|  |  | 
|  | blessedBooleanResult(resultRegs.payloadGPR(), node, UseChildrenCalledExplicitly); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileInByVal(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | JSValueOperand key(this, node->child2()); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | JSValueRegs regs = key.jsValueRegs(); | 
|  |  | 
|  | base.use(); | 
|  | key.use(); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationInByVal, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, regs); | 
|  | m_jit.exceptionCheck(); | 
|  | blessedBooleanResult(resultRegs.payloadGPR(), node, UseChildrenCalledExplicitly); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compilePushWithScope(Node* node) | 
|  | { | 
|  | SpeculateCellOperand currentScope(this, node->child1()); | 
|  | GPRReg currentScopeGPR = currentScope.gpr(); | 
|  |  | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | auto objectEdge = node->child2(); | 
|  | if (objectEdge.useKind() == ObjectUse) { | 
|  | SpeculateCellOperand object(this, objectEdge); | 
|  | GPRReg objectGPR = object.gpr(); | 
|  | speculateObject(objectEdge, objectGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(operationPushWithScopeObject, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), currentScopeGPR, objectGPR); | 
|  | // No exception check here as we did not have to call toObject(). | 
|  | } else { | 
|  | ASSERT(objectEdge.useKind() == UntypedUse); | 
|  | JSValueOperand object(this, objectEdge); | 
|  | JSValueRegs objectRegs = object.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(operationPushWithScope, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), currentScopeGPR, objectRegs); | 
|  | m_jit.exceptionCheck(); | 
|  | } | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | bool SpeculativeJIT::genericJSValueStrictEq(Node* node, bool invert) | 
|  | { | 
|  | unsigned branchIndexInBlock = detectPeepHoleBranch(); | 
|  | if (branchIndexInBlock != UINT_MAX) { | 
|  | Node* branchNode = m_block->at(branchIndexInBlock); | 
|  |  | 
|  | ASSERT(node->adjustedRefCount() == 1); | 
|  |  | 
|  | nonSpeculativePeepholeStrictEq(node, branchNode, invert); | 
|  |  | 
|  | m_indexInBlock = branchIndexInBlock; | 
|  | m_currentNode = branchNode; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | genericJSValueNonPeepholeStrictEq(node, invert); | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static const char* dataFormatString(DataFormat format) | 
|  | { | 
|  | // These values correspond to the DataFormat enum. | 
|  | const char* strings[] = { | 
|  | "[  ]", | 
|  | "[ i]", | 
|  | "[ d]", | 
|  | "[ c]", | 
|  | "Err!", | 
|  | "Err!", | 
|  | "Err!", | 
|  | "Err!", | 
|  | "[J ]", | 
|  | "[Ji]", | 
|  | "[Jd]", | 
|  | "[Jc]", | 
|  | "Err!", | 
|  | "Err!", | 
|  | "Err!", | 
|  | "Err!", | 
|  | }; | 
|  | return strings[format]; | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::dump(const char* label) | 
|  | { | 
|  | if (label) | 
|  | dataLogF("<%s>\n", label); | 
|  |  | 
|  | dataLogF("  gprs:\n"); | 
|  | m_gprs.dump(); | 
|  | dataLogF("  fprs:\n"); | 
|  | m_fprs.dump(); | 
|  | dataLogF("  VirtualRegisters:\n"); | 
|  | for (unsigned i = 0; i < m_generationInfo.size(); ++i) { | 
|  | GenerationInfo& info = m_generationInfo[i]; | 
|  | if (info.alive()) | 
|  | dataLogF("    % 3d:%s%s", i, dataFormatString(info.registerFormat()), dataFormatString(info.spillFormat())); | 
|  | else | 
|  | dataLogF("    % 3d:[__][__]", i); | 
|  | if (info.registerFormat() == DataFormatDouble) | 
|  | dataLogF(":fpr%d\n", info.fpr()); | 
|  | else if (info.registerFormat() != DataFormatNone | 
|  | #if USE(JSVALUE32_64) | 
|  | && !(info.registerFormat() & DataFormatJS) | 
|  | #endif | 
|  | ) { | 
|  | ASSERT(info.gpr() != InvalidGPRReg); | 
|  | dataLogF(":%s\n", GPRInfo::debugName(info.gpr())); | 
|  | } else | 
|  | dataLogF("\n"); | 
|  | } | 
|  | if (label) | 
|  | dataLogF("</%s>\n", label); | 
|  | } | 
|  |  | 
|  | GPRTemporary::GPRTemporary() | 
|  | : m_jit(nullptr) | 
|  | , m_gpr(InvalidGPRReg) | 
|  | { | 
|  | } | 
|  |  | 
|  | GPRTemporary::GPRTemporary(SpeculativeJIT* jit) | 
|  | : m_jit(jit) | 
|  | , m_gpr(InvalidGPRReg) | 
|  | { | 
|  | m_gpr = m_jit->allocate(); | 
|  | } | 
|  |  | 
|  | GPRTemporary::GPRTemporary(SpeculativeJIT* jit, GPRReg specific) | 
|  | : m_jit(jit) | 
|  | , m_gpr(InvalidGPRReg) | 
|  | { | 
|  | m_gpr = m_jit->allocate(specific); | 
|  | } | 
|  |  | 
|  | #if USE(JSVALUE32_64) | 
|  | GPRTemporary::GPRTemporary( | 
|  | SpeculativeJIT* jit, ReuseTag, JSValueOperand& op1, WhichValueWord which) | 
|  | : m_jit(jit) | 
|  | , m_gpr(InvalidGPRReg) | 
|  | { | 
|  | if (!op1.isDouble() && m_jit->canReuse(op1.node())) | 
|  | m_gpr = m_jit->reuse(op1.gpr(which)); | 
|  | else | 
|  | m_gpr = m_jit->allocate(); | 
|  | } | 
|  | #else // USE(JSVALUE32_64) | 
|  | GPRTemporary::GPRTemporary(SpeculativeJIT* jit, ReuseTag, JSValueOperand& op1, WhichValueWord) | 
|  | : GPRTemporary(jit, Reuse, op1) | 
|  | { | 
|  | } | 
|  | #endif | 
|  |  | 
|  | JSValueRegsTemporary::JSValueRegsTemporary() { } | 
|  |  | 
|  | JSValueRegsTemporary::JSValueRegsTemporary(SpeculativeJIT* jit) | 
|  | #if USE(JSVALUE64) | 
|  | : m_gpr(jit) | 
|  | #else | 
|  | : m_payloadGPR(jit) | 
|  | , m_tagGPR(jit) | 
|  | #endif | 
|  | { | 
|  | } | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | template<typename T> | 
|  | JSValueRegsTemporary::JSValueRegsTemporary(SpeculativeJIT* jit, ReuseTag, T& operand, WhichValueWord) | 
|  | : m_gpr(jit, Reuse, operand) | 
|  | { | 
|  | } | 
|  | #else | 
|  | template<typename T> | 
|  | JSValueRegsTemporary::JSValueRegsTemporary(SpeculativeJIT* jit, ReuseTag, T& operand, WhichValueWord resultWord) | 
|  | { | 
|  | if (resultWord == PayloadWord) { | 
|  | m_payloadGPR = GPRTemporary(jit, Reuse, operand); | 
|  | m_tagGPR = GPRTemporary(jit); | 
|  | } else { | 
|  | m_payloadGPR = GPRTemporary(jit); | 
|  | m_tagGPR = GPRTemporary(jit, Reuse, operand); | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | JSValueRegsTemporary::JSValueRegsTemporary(SpeculativeJIT* jit, ReuseTag, JSValueOperand& operand) | 
|  | { | 
|  | m_gpr = GPRTemporary(jit, Reuse, operand); | 
|  | } | 
|  | #else | 
|  | JSValueRegsTemporary::JSValueRegsTemporary(SpeculativeJIT* jit, ReuseTag, JSValueOperand& operand) | 
|  | { | 
|  | if (jit->canReuse(operand.node())) { | 
|  | m_payloadGPR = GPRTemporary(jit, Reuse, operand, PayloadWord); | 
|  | m_tagGPR = GPRTemporary(jit, Reuse, operand, TagWord); | 
|  | } else { | 
|  | m_payloadGPR = GPRTemporary(jit); | 
|  | m_tagGPR = GPRTemporary(jit); | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | JSValueRegsTemporary::~JSValueRegsTemporary() { } | 
|  |  | 
|  | JSValueRegs JSValueRegsTemporary::regs() | 
|  | { | 
|  | #if USE(JSVALUE64) | 
|  | return JSValueRegs(m_gpr.gpr()); | 
|  | #else | 
|  | return JSValueRegs(m_tagGPR.gpr(), m_payloadGPR.gpr()); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void GPRTemporary::adopt(GPRTemporary& other) | 
|  | { | 
|  | ASSERT(!m_jit); | 
|  | ASSERT(m_gpr == InvalidGPRReg); | 
|  | ASSERT(other.m_jit); | 
|  | ASSERT(other.m_gpr != InvalidGPRReg); | 
|  | m_jit = other.m_jit; | 
|  | m_gpr = other.m_gpr; | 
|  | other.m_jit = nullptr; | 
|  | other.m_gpr = InvalidGPRReg; | 
|  | } | 
|  |  | 
|  | FPRTemporary::FPRTemporary(FPRTemporary&& other) | 
|  | { | 
|  | ASSERT(other.m_jit); | 
|  | ASSERT(other.m_fpr != InvalidFPRReg); | 
|  | m_jit = other.m_jit; | 
|  | m_fpr = other.m_fpr; | 
|  |  | 
|  | other.m_jit = nullptr; | 
|  | } | 
|  |  | 
|  | FPRTemporary::FPRTemporary(SpeculativeJIT* jit) | 
|  | : m_jit(jit) | 
|  | , m_fpr(InvalidFPRReg) | 
|  | { | 
|  | m_fpr = m_jit->fprAllocate(); | 
|  | } | 
|  |  | 
|  | FPRTemporary::FPRTemporary(SpeculativeJIT* jit, SpeculateDoubleOperand& op1) | 
|  | : m_jit(jit) | 
|  | , m_fpr(InvalidFPRReg) | 
|  | { | 
|  | if (m_jit->canReuse(op1.node())) | 
|  | m_fpr = m_jit->reuse(op1.fpr()); | 
|  | else | 
|  | m_fpr = m_jit->fprAllocate(); | 
|  | } | 
|  |  | 
|  | FPRTemporary::FPRTemporary(SpeculativeJIT* jit, SpeculateDoubleOperand& op1, SpeculateDoubleOperand& op2) | 
|  | : m_jit(jit) | 
|  | , m_fpr(InvalidFPRReg) | 
|  | { | 
|  | if (m_jit->canReuse(op1.node())) | 
|  | 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(); | 
|  | } | 
|  |  | 
|  | #if USE(JSVALUE32_64) | 
|  | FPRTemporary::FPRTemporary(SpeculativeJIT* jit, JSValueOperand& op1) | 
|  | : m_jit(jit) | 
|  | , m_fpr(InvalidFPRReg) | 
|  | { | 
|  | if (op1.isDouble() && m_jit->canReuse(op1.node())) | 
|  | m_fpr = m_jit->reuse(op1.fpr()); | 
|  | else | 
|  | m_fpr = m_jit->fprAllocate(); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | void SpeculativeJIT::compilePeepHoleDoubleBranch(Node* node, Node* branchNode, JITCompiler::DoubleCondition condition) | 
|  | { | 
|  | BasicBlock* taken = branchNode->branchData()->taken.block; | 
|  | BasicBlock* notTaken = branchNode->branchData()->notTaken.block; | 
|  |  | 
|  | if (taken == nextBlock()) { | 
|  | condition = MacroAssembler::invert(condition); | 
|  | std::swap(taken, notTaken); | 
|  | } | 
|  |  | 
|  | SpeculateDoubleOperand op1(this, node->child1()); | 
|  | SpeculateDoubleOperand op2(this, node->child2()); | 
|  |  | 
|  | branchDouble(condition, op1.fpr(), op2.fpr(), taken); | 
|  | jump(notTaken); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compilePeepHoleObjectEquality(Node* node, Node* branchNode) | 
|  | { | 
|  | BasicBlock* taken = branchNode->branchData()->taken.block; | 
|  | BasicBlock* notTaken = branchNode->branchData()->notTaken.block; | 
|  |  | 
|  | MacroAssembler::RelationalCondition condition = MacroAssembler::Equal; | 
|  |  | 
|  | if (taken == nextBlock()) { | 
|  | condition = MacroAssembler::NotEqual; | 
|  | BasicBlock* tmp = taken; | 
|  | taken = notTaken; | 
|  | notTaken = tmp; | 
|  | } | 
|  |  | 
|  | SpeculateCellOperand op1(this, node->child1()); | 
|  | SpeculateCellOperand op2(this, node->child2()); | 
|  |  | 
|  | GPRReg op1GPR = op1.gpr(); | 
|  | GPRReg op2GPR = op2.gpr(); | 
|  |  | 
|  | if (masqueradesAsUndefinedWatchpointIsStillValid()) { | 
|  | if (m_state.forNode(node->child1()).m_type & ~SpecObject) { | 
|  | speculationCheck( | 
|  | 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.branchIfNotObject(op2GPR)); | 
|  | } | 
|  | } else { | 
|  | if (m_state.forNode(node->child1()).m_type & ~SpecObject) { | 
|  | speculationCheck( | 
|  | BadType, JSValueSource::unboxedCell(op1GPR), node->child1(), | 
|  | m_jit.branchIfNotObject(op1GPR)); | 
|  | } | 
|  | speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), node->child1(), | 
|  | m_jit.branchTest8( | 
|  | MacroAssembler::NonZero, | 
|  | MacroAssembler::Address(op1GPR, JSCell::typeInfoFlagsOffset()), | 
|  | MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); | 
|  |  | 
|  | if (m_state.forNode(node->child2()).m_type & ~SpecObject) { | 
|  | speculationCheck( | 
|  | BadType, JSValueSource::unboxedCell(op2GPR), node->child2(), | 
|  | m_jit.branchIfNotObject(op2GPR)); | 
|  | } | 
|  | speculationCheck(BadType, JSValueSource::unboxedCell(op2GPR), node->child2(), | 
|  | m_jit.branchTest8( | 
|  | MacroAssembler::NonZero, | 
|  | MacroAssembler::Address(op2GPR, JSCell::typeInfoFlagsOffset()), | 
|  | MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); | 
|  | } | 
|  |  | 
|  | branchPtr(condition, op1GPR, op2GPR, taken); | 
|  | jump(notTaken); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compilePeepHoleBooleanBranch(Node* node, Node* branchNode, JITCompiler::RelationalCondition condition) | 
|  | { | 
|  | 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); | 
|  | BasicBlock* tmp = taken; | 
|  | taken = notTaken; | 
|  | notTaken = tmp; | 
|  | } | 
|  |  | 
|  | if (node->child1()->isInt32Constant()) { | 
|  | int32_t imm = node->child1()->asInt32(); | 
|  | SpeculateBooleanOperand op2(this, node->child2()); | 
|  | branch32(condition, JITCompiler::Imm32(imm), op2.gpr(), taken); | 
|  | } else if (node->child2()->isInt32Constant()) { | 
|  | SpeculateBooleanOperand op1(this, node->child1()); | 
|  | 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()); | 
|  | branch32(condition, op1.gpr(), op2.gpr(), taken); | 
|  | } | 
|  |  | 
|  | jump(notTaken); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileStringSlice(Node* node) | 
|  | { | 
|  | SpeculateCellOperand string(this, node->child1()); | 
|  |  | 
|  | GPRReg stringGPR = string.gpr(); | 
|  |  | 
|  | speculateString(node->child1(), stringGPR); | 
|  |  | 
|  | SpeculateInt32Operand start(this, node->child2()); | 
|  | GPRReg startGPR = start.gpr(); | 
|  |  | 
|  | Optional<SpeculateInt32Operand> end; | 
|  | Optional<GPRReg> endGPR; | 
|  | if (node->child3()) { | 
|  | end.emplace(this, node->child3()); | 
|  | endGPR.emplace(end->gpr()); | 
|  | } | 
|  |  | 
|  | GPRTemporary temp(this); | 
|  | GPRTemporary temp2(this); | 
|  | GPRTemporary startIndex(this); | 
|  |  | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  | GPRReg temp2GPR = temp2.gpr(); | 
|  | GPRReg startIndexGPR = startIndex.gpr(); | 
|  |  | 
|  | m_jit.loadPtr(CCallHelpers::Address(stringGPR, JSString::offsetOfValue()), tempGPR); | 
|  | auto isRope = m_jit.branchIfRopeStringImpl(tempGPR); | 
|  | { | 
|  | m_jit.load32(MacroAssembler::Address(tempGPR, StringImpl::lengthMemoryOffset()), temp2GPR); | 
|  |  | 
|  | emitPopulateSliceIndex(node->child2(), startGPR, temp2GPR, startIndexGPR); | 
|  |  | 
|  | if (node->child3()) | 
|  | emitPopulateSliceIndex(node->child3(), endGPR.value(), temp2GPR, tempGPR); | 
|  | else | 
|  | m_jit.move(temp2GPR, tempGPR); | 
|  | } | 
|  |  | 
|  | CCallHelpers::JumpList doneCases; | 
|  | CCallHelpers::JumpList slowCases; | 
|  |  | 
|  | VM& vm = this->vm(); | 
|  | auto nonEmptyCase = m_jit.branch32(MacroAssembler::Below, startIndexGPR, tempGPR); | 
|  | m_jit.move(TrustedImmPtr::weakPointer(m_jit.graph(), jsEmptyString(vm)), tempGPR); | 
|  | doneCases.append(m_jit.jump()); | 
|  |  | 
|  | nonEmptyCase.link(&m_jit); | 
|  | m_jit.sub32(startIndexGPR, tempGPR); // the size of the sliced string. | 
|  | slowCases.append(m_jit.branch32(MacroAssembler::NotEqual, tempGPR, TrustedImm32(1))); | 
|  |  | 
|  | // Refill StringImpl* here. | 
|  | m_jit.loadPtr(MacroAssembler::Address(stringGPR, JSString::offsetOfValue()), temp2GPR); | 
|  | m_jit.loadPtr(MacroAssembler::Address(temp2GPR, StringImpl::dataOffset()), tempGPR); | 
|  |  | 
|  | // Load the character into scratchReg | 
|  | m_jit.zeroExtend32ToWord(startIndexGPR, startIndexGPR); | 
|  | auto is16Bit = m_jit.branchTest32(MacroAssembler::Zero, MacroAssembler::Address(temp2GPR, StringImpl::flagsOffset()), TrustedImm32(StringImpl::flagIs8Bit())); | 
|  |  | 
|  | m_jit.load8(MacroAssembler::BaseIndex(tempGPR, startIndexGPR, MacroAssembler::TimesOne, 0), tempGPR); | 
|  | auto cont8Bit = m_jit.jump(); | 
|  |  | 
|  | is16Bit.link(&m_jit); | 
|  | m_jit.load16(MacroAssembler::BaseIndex(tempGPR, startIndexGPR, MacroAssembler::TimesTwo, 0), tempGPR); | 
|  |  | 
|  | auto bigCharacter = m_jit.branch32(MacroAssembler::Above, tempGPR, TrustedImm32(maxSingleCharacterString)); | 
|  |  | 
|  | // 8 bit string values don't need the isASCII check. | 
|  | cont8Bit.link(&m_jit); | 
|  |  | 
|  | m_jit.lshift32(MacroAssembler::TrustedImm32(sizeof(void*) == 4 ? 2 : 3), tempGPR); | 
|  | m_jit.addPtr(TrustedImmPtr(vm.smallStrings.singleCharacterStrings()), tempGPR); | 
|  | m_jit.loadPtr(tempGPR, tempGPR); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(bigCharacter, this, operationSingleCharacterString, tempGPR, &vm, tempGPR)); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowCases, this, operationStringSubstr, tempGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), stringGPR, startIndexGPR, tempGPR)); | 
|  |  | 
|  | if (endGPR) | 
|  | addSlowPathGenerator(slowPathCall(isRope, this, operationStringSlice, tempGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), stringGPR, startGPR, *endGPR)); | 
|  | else | 
|  | addSlowPathGenerator(slowPathCall(isRope, this, operationStringSlice, tempGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), stringGPR, startGPR, TrustedImm32(std::numeric_limits<int32_t>::max()))); | 
|  |  | 
|  | doneCases.link(&m_jit); | 
|  | cellResult(tempGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileToLowerCase(Node* node) | 
|  | { | 
|  | ASSERT(node->op() == ToLowerCase); | 
|  | SpeculateCellOperand string(this, node->child1()); | 
|  | GPRTemporary temp(this); | 
|  | GPRTemporary index(this); | 
|  | GPRTemporary charReg(this); | 
|  | GPRTemporary length(this); | 
|  |  | 
|  | GPRReg stringGPR = string.gpr(); | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  | GPRReg indexGPR = index.gpr(); | 
|  | GPRReg charGPR = charReg.gpr(); | 
|  | GPRReg lengthGPR = length.gpr(); | 
|  |  | 
|  | speculateString(node->child1(), stringGPR); | 
|  |  | 
|  | CCallHelpers::JumpList slowPath; | 
|  |  | 
|  | m_jit.move(TrustedImmPtr(nullptr), indexGPR); | 
|  |  | 
|  | m_jit.loadPtr(MacroAssembler::Address(stringGPR, JSString::offsetOfValue()), tempGPR); | 
|  | slowPath.append(m_jit.branchIfRopeStringImpl(tempGPR)); | 
|  | slowPath.append(m_jit.branchTest32( | 
|  | MacroAssembler::Zero, MacroAssembler::Address(tempGPR, StringImpl::flagsOffset()), | 
|  | MacroAssembler::TrustedImm32(StringImpl::flagIs8Bit()))); | 
|  | m_jit.load32(MacroAssembler::Address(tempGPR, StringImpl::lengthMemoryOffset()), lengthGPR); | 
|  | m_jit.loadPtr(MacroAssembler::Address(tempGPR, StringImpl::dataOffset()), tempGPR); | 
|  |  | 
|  | auto loopStart = m_jit.label(); | 
|  | auto loopDone = m_jit.branch32(CCallHelpers::AboveOrEqual, indexGPR, lengthGPR); | 
|  | m_jit.load8(MacroAssembler::BaseIndex(tempGPR, indexGPR, MacroAssembler::TimesOne), charGPR); | 
|  | slowPath.append(m_jit.branchTest32(CCallHelpers::NonZero, charGPR, TrustedImm32(~0x7F))); | 
|  | m_jit.sub32(TrustedImm32('A'), charGPR); | 
|  | slowPath.append(m_jit.branch32(CCallHelpers::BelowOrEqual, charGPR, TrustedImm32('Z' - 'A'))); | 
|  |  | 
|  | m_jit.add32(TrustedImm32(1), indexGPR); | 
|  | m_jit.jump().linkTo(loopStart, &m_jit); | 
|  |  | 
|  | slowPath.link(&m_jit); | 
|  | silentSpillAllRegisters(lengthGPR); | 
|  | callOperation(operationToLowerCase, lengthGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), stringGPR, indexGPR); | 
|  | silentFillAllRegisters(); | 
|  | m_jit.exceptionCheck(); | 
|  | auto done = m_jit.jump(); | 
|  |  | 
|  | loopDone.link(&m_jit); | 
|  | m_jit.move(stringGPR, lengthGPR); | 
|  |  | 
|  | done.link(&m_jit); | 
|  | cellResult(lengthGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compilePeepHoleInt32Branch(Node* node, Node* branchNode, JITCompiler::RelationalCondition condition) | 
|  | { | 
|  | 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); | 
|  | BasicBlock* tmp = taken; | 
|  | taken = notTaken; | 
|  | notTaken = tmp; | 
|  | } | 
|  |  | 
|  | 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 (node->child2()->isInt32Constant()) { | 
|  | SpeculateInt32Operand op1(this, node->child1()); | 
|  | int32_t imm = node->child2()->asInt32(); | 
|  | branch32(condition, op1.gpr(), JITCompiler::Imm32(imm), taken); | 
|  | } else { | 
|  | SpeculateInt32Operand op1(this, node->child1()); | 
|  | SpeculateInt32Operand op2(this, node->child2()); | 
|  | branch32(condition, op1.gpr(), op2.gpr(), taken); | 
|  | } | 
|  |  | 
|  | jump(notTaken); | 
|  | } | 
|  |  | 
|  | // Returns true if the compare is fused with a subsequent branch. | 
|  | bool SpeculativeJIT::compilePeepHoleBranch(Node* node, MacroAssembler::RelationalCondition condition, MacroAssembler::DoubleCondition doubleCondition, S_JITOperation_GJJ operation) | 
|  | { | 
|  | // Fused compare & branch. | 
|  | unsigned branchIndexInBlock = detectPeepHoleBranch(); | 
|  | if (branchIndexInBlock != UINT_MAX) { | 
|  | 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)) | 
|  | compilePeepHoleInt32Branch(node, branchNode, condition); | 
|  | #if USE(BIGINT32) | 
|  | else if (node->isBinaryUseKind(BigInt32Use)) | 
|  | compilePeepHoleBigInt32Branch(node, branchNode, condition); | 
|  | #endif | 
|  | #if USE(JSVALUE64) | 
|  | else if (node->isBinaryUseKind(Int52RepUse)) | 
|  | compilePeepHoleInt52Branch(node, branchNode, condition); | 
|  | #endif // USE(JSVALUE64) | 
|  | else if (node->isBinaryUseKind(StringUse) || node->isBinaryUseKind(StringIdentUse)) { | 
|  | // Use non-peephole comparison, for now. | 
|  | return false; | 
|  | } else if (node->isBinaryUseKind(DoubleRepUse)) | 
|  | compilePeepHoleDoubleBranch(node, branchNode, doubleCondition); | 
|  | else if (node->op() == CompareEq) { | 
|  | 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->isBinaryUseKind(ObjectUse, ObjectOrOtherUse)) | 
|  | compilePeepHoleObjectToObjectOrOtherEquality(node->child1(), node->child2(), branchNode); | 
|  | 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 { | 
|  | genericJSValuePeepholeBranch(node, branchNode, condition, operation); | 
|  | return true; | 
|  | } | 
|  | } else { | 
|  | genericJSValuePeepholeBranch(node, branchNode, condition, operation); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | use(node->child1()); | 
|  | use(node->child2()); | 
|  | m_indexInBlock = branchIndexInBlock; | 
|  | m_currentNode = branchNode; | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::noticeOSRBirth(Node* node) | 
|  | { | 
|  | if (!node->hasVirtualRegister()) | 
|  | return; | 
|  |  | 
|  | VirtualRegister virtualRegister = node->virtualRegister(); | 
|  | GenerationInfo& info = generationInfoFromVirtualRegister(virtualRegister); | 
|  |  | 
|  | info.noticeOSRBirth(*m_stream, node, virtualRegister); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileMovHint(Node* node) | 
|  | { | 
|  | ASSERT(node->containsMovHint() && node->op() != ZombieHint); | 
|  |  | 
|  | Node* child = node->child1().node(); | 
|  | noticeOSRBirth(child); | 
|  |  | 
|  | m_stream->appendAndLog(VariableEvent::movHint(MinifiedID(child), node->unlinkedOperand())); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCheckNeutered(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | GPRReg baseReg = base.gpr(); | 
|  |  | 
|  | speculationCheck( | 
|  | BadIndexingType, JSValueSource::unboxedCell(baseReg), node->child1(), | 
|  | m_jit.branchTestPtr(MacroAssembler::Zero, MacroAssembler::Address(baseReg, JSArrayBufferView::offsetOfVector()))); | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::bail(AbortReason reason) | 
|  | { | 
|  | if (verboseCompilationEnabled()) | 
|  | dataLog("Bailing compilation.\n"); | 
|  | m_compileOkay = true; | 
|  | m_jit.abortWithReason(reason, m_lastGeneratedNode); | 
|  | clearGenerationInfo(); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCurrentBlock() | 
|  | { | 
|  | ASSERT(m_compileOkay); | 
|  |  | 
|  | if (!m_block) | 
|  | return; | 
|  |  | 
|  | 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. | 
|  | m_jit.abortWithReason(DFGUnreachableBasicBlock); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (m_block->isCatchEntrypoint) { | 
|  | m_jit.addPtr(CCallHelpers::TrustedImm32(-(m_jit.graph().frameRegisterCount() * sizeof(Register))), GPRInfo::callFrameRegister,  CCallHelpers::stackPointerRegister); | 
|  | m_jit.emitSaveCalleeSaves(); | 
|  | m_jit.emitMaterializeTagCheckRegisters(); | 
|  | m_jit.emitPutToCallFrameHeader(m_jit.codeBlock(), CallFrameSlot::codeBlock); | 
|  | } | 
|  |  | 
|  | m_stream->appendAndLog(VariableEvent::reset()); | 
|  |  | 
|  | m_jit.jitAssertHasValidCallFrame(); | 
|  | m_jit.jitAssertTagsInPlace(); | 
|  | m_jit.jitAssertArgumentCountSane(); | 
|  |  | 
|  | m_state.reset(); | 
|  | m_state.beginBasicBlock(m_block); | 
|  |  | 
|  | for (size_t i = m_block->variablesAtHead.size(); i--;) { | 
|  | Operand operand = m_block->variablesAtHead.operandForIndex(i); | 
|  | Node* node = m_block->variablesAtHead[i]; | 
|  | if (!node) | 
|  | 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()); | 
|  | DFG_ASSERT(m_jit.graph(), node, !operand.isArgument() || operand.virtualRegister().toArgument() >= 0); | 
|  | m_stream->appendAndLog(VariableEvent::setLocal(operand, variable->machineLocal(), format)); | 
|  | } | 
|  |  | 
|  | m_origin = NodeOrigin(); | 
|  |  | 
|  | if (Options::validateDFGClobberize()) { | 
|  | bool clobberedWorld = m_block->predecessors.isEmpty() || m_block->isOSRTarget || m_block->isCatchEntrypoint; | 
|  | auto validateClobberize = [&] () { | 
|  | clobberedWorld = true; | 
|  | }; | 
|  |  | 
|  | for (auto* predecessor : m_block->predecessors) { | 
|  | Node* terminal = predecessor->terminal(); | 
|  | // We sometimes fuse compare followed by branch. | 
|  | if (terminal->isBranch()) | 
|  | terminal = terminal->child1().node(); | 
|  | clobberize(m_graph, terminal, [] (auto...) { }, [] (auto...) { }, [] (auto...) { }, validateClobberize); | 
|  | } | 
|  |  | 
|  | if (!clobberedWorld) { | 
|  | auto ok = m_jit.branchTest8(MacroAssembler::Zero, MacroAssembler::AbsoluteAddress(&vm().didEnterVM)); | 
|  | m_jit.breakpoint(); | 
|  | ok.link(&m_jit); | 
|  | } else | 
|  | m_jit.store8(TrustedImm32(0), &vm().didEnterVM); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | m_interpreter.startExecuting(); | 
|  | m_interpreter.executeKnownEdgeTypes(m_currentNode); | 
|  | m_jit.setForNode(m_currentNode); | 
|  | m_origin = m_currentNode->origin; | 
|  | m_lastGeneratedNode = m_currentNode->op(); | 
|  |  | 
|  | ASSERT(m_currentNode->shouldGenerate()); | 
|  |  | 
|  | if (verboseCompilationEnabled()) | 
|  | dataLogLn("SpeculativeJIT generating Node @", (int)m_currentNode->index(), " (", m_currentNode->origin.semantic.bytecodeIndex().offset(), ") at JIT offset 0x", m_jit.debugOffset()); | 
|  |  | 
|  | if (Options::validateDFGExceptionHandling() && (mayExit(m_jit.graph(), m_currentNode) != DoesNotExit || m_currentNode->isTerminal())) | 
|  | m_jit.jitReleaseAssertNoException(m_jit.vm()); | 
|  |  | 
|  | m_jit.pcToCodeOriginMapBuilder().appendItem(m_jit.labelIgnoringWatchpoints(), m_origin.semantic); | 
|  |  | 
|  | if (m_indexInBlock && Options::validateDFGClobberize()) { | 
|  | bool clobberedWorld = false; | 
|  | auto validateClobberize = [&] () { | 
|  | clobberedWorld = true; | 
|  | }; | 
|  |  | 
|  | clobberize(m_graph, m_block->at(m_indexInBlock - 1), [] (auto...) { }, [] (auto...) { }, [] (auto...) { }, validateClobberize); | 
|  | if (!clobberedWorld) { | 
|  | auto ok = m_jit.branchTest8(MacroAssembler::Zero, MacroAssembler::AbsoluteAddress(&vm().didEnterVM)); | 
|  | m_jit.breakpoint(); | 
|  | ok.link(&m_jit); | 
|  | } else | 
|  | m_jit.store8(TrustedImm32(0), &vm().didEnterVM); | 
|  | } | 
|  |  | 
|  | compile(m_currentNode); | 
|  |  | 
|  | if (belongsInMinifiedGraph(m_currentNode->op())) | 
|  | m_minifiedGraph->append(MinifiedNode::fromNode(m_currentNode)); | 
|  |  | 
|  | #if ENABLE(DFG_REGISTER_ALLOCATION_VALIDATION) | 
|  | m_jit.clearRegisterAllocationOffsets(); | 
|  | #endif | 
|  |  | 
|  | if (!m_compileOkay) { | 
|  | bail(DFGBailedAtEndOfNode); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // 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_ENABLED) { | 
|  | for (auto& info : m_generationInfo) | 
|  | RELEASE_ASSERT(!info.alive()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // If we are making type predictions about our arguments then | 
|  | // we need to check that they are correct on function entry. | 
|  | void SpeculativeJIT::checkArgumentTypes() | 
|  | { | 
|  | ASSERT(!m_currentNode); | 
|  | m_origin = NodeOrigin(CodeOrigin(BytecodeIndex(0)), CodeOrigin(BytecodeIndex(0)), true); | 
|  |  | 
|  | auto& arguments = m_jit.graph().m_rootToArguments.find(m_jit.graph().block(0))->value; | 
|  | for (int i = 0; i < m_jit.codeBlock()->numParameters(); ++i) { | 
|  | Node* node = arguments[i]; | 
|  | if (!node) { | 
|  | // The argument is dead. We don't do any checks for such arguments. | 
|  | continue; | 
|  | } | 
|  |  | 
|  | ASSERT(node->op() == SetArgumentDefinitely); | 
|  | ASSERT(node->shouldGenerate()); | 
|  |  | 
|  | VariableAccessData* variableAccessData = node->variableAccessData(); | 
|  | FlushFormat format = variableAccessData->flushFormat(); | 
|  |  | 
|  | if (format == FlushedJSValue) | 
|  | continue; | 
|  |  | 
|  | VirtualRegister virtualRegister = variableAccessData->operand().virtualRegister(); | 
|  | ASSERT(virtualRegister.isArgument()); | 
|  |  | 
|  | JSValueSource valueSource = JSValueSource(JITCompiler::addressFor(virtualRegister)); | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | switch (format) { | 
|  | case FlushedInt32: { | 
|  | speculationCheck(BadType, valueSource, node, m_jit.branch64(MacroAssembler::Below, JITCompiler::addressFor(virtualRegister), GPRInfo::numberTagRegister)); | 
|  | break; | 
|  | } | 
|  | case FlushedBoolean: { | 
|  | GPRTemporary temp(this); | 
|  | m_jit.load64(JITCompiler::addressFor(virtualRegister), temp.gpr()); | 
|  | m_jit.xor64(TrustedImm32(JSValue::ValueFalse), temp.gpr()); | 
|  | speculationCheck(BadType, valueSource, node, m_jit.branchTest64(MacroAssembler::NonZero, temp.gpr(), TrustedImm32(static_cast<int32_t>(~1)))); | 
|  | break; | 
|  | } | 
|  | case FlushedCell: { | 
|  | speculationCheck(BadType, valueSource, node, m_jit.branchTest64(MacroAssembler::NonZero, JITCompiler::addressFor(virtualRegister), GPRInfo::notCellMaskRegister)); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  | #else | 
|  | switch (format) { | 
|  | case FlushedInt32: { | 
|  | speculationCheck(BadType, valueSource, node, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::Int32Tag))); | 
|  | break; | 
|  | } | 
|  | case FlushedBoolean: { | 
|  | speculationCheck(BadType, valueSource, node, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::BooleanTag))); | 
|  | 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_origin = NodeOrigin(); | 
|  | } | 
|  |  | 
|  | bool SpeculativeJIT::compile() | 
|  | { | 
|  | checkArgumentTypes(); | 
|  |  | 
|  | ASSERT(!m_currentNode); | 
|  | 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; | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::createOSREntries() | 
|  | { | 
|  | for (BlockIndex blockIndex = 0; blockIndex < m_jit.graph().numBlocks(); ++blockIndex) { | 
|  | BasicBlock* block = m_jit.graph().block(blockIndex); | 
|  | if (!block) | 
|  | continue; | 
|  | if (block->isOSRTarget || block->isCatchEntrypoint) { | 
|  | // 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().numBlocks(); ++blockIndex) { | 
|  | BasicBlock* block = m_jit.graph().block(blockIndex); | 
|  | if (!block) | 
|  | continue; | 
|  | if (!block->isOSRTarget && !block->isCatchEntrypoint) | 
|  | continue; | 
|  | if (block->isCatchEntrypoint) { | 
|  | auto& argumentsVector = m_jit.graph().m_rootToArguments.find(block)->value; | 
|  | Vector<FlushFormat> argumentFormats; | 
|  | argumentFormats.reserveInitialCapacity(argumentsVector.size()); | 
|  | for (Node* setArgument : argumentsVector) { | 
|  | if (setArgument) { | 
|  | FlushFormat flushFormat = setArgument->variableAccessData()->flushFormat(); | 
|  | ASSERT(flushFormat == FlushedInt32 || flushFormat == FlushedCell || flushFormat == FlushedBoolean || flushFormat == FlushedJSValue); | 
|  | argumentFormats.uncheckedAppend(flushFormat); | 
|  | } else | 
|  | argumentFormats.uncheckedAppend(DeadFlush); | 
|  | } | 
|  | m_jit.noticeCatchEntrypoint(*block, m_osrEntryHeads[osrEntryIndex++], linkBuffer, WTFMove(argumentFormats)); | 
|  | } else { | 
|  | ASSERT(block->isOSRTarget); | 
|  | m_jit.noticeOSREntry(*block, m_osrEntryHeads[osrEntryIndex++], linkBuffer); | 
|  | } | 
|  | } | 
|  |  | 
|  | m_jit.jitCode()->finalizeOSREntrypoints(); | 
|  | m_jit.jitCode()->common.finalizeCatchEntrypoints(); | 
|  |  | 
|  | ASSERT(osrEntryIndex == m_osrEntryHeads.size()); | 
|  |  | 
|  | 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::compileCheckTraps(Node* node) | 
|  | { | 
|  | ASSERT(Options::usePollingTraps()); | 
|  | GPRTemporary unused(this); | 
|  | GPRReg unusedGPR = unused.gpr(); | 
|  |  | 
|  | JITCompiler::Jump needTrapHandling = m_jit.branchTest8(JITCompiler::NonZero, | 
|  | JITCompiler::AbsoluteAddress(m_jit.vm().needTrapHandlingAddress())); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(needTrapHandling, this, operationHandleTraps, unusedGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)))); | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileDoublePutByVal(Node* node, SpeculateCellOperand& base, SpeculateStrictInt32Operand& property) | 
|  | { | 
|  | Edge child3 = m_jit.graph().varArgChild(node, 2); | 
|  | Edge child4 = m_jit.graph().varArgChild(node, 3); | 
|  |  | 
|  | ArrayMode arrayMode = node->arrayMode(); | 
|  |  | 
|  | GPRReg baseReg = base.gpr(); | 
|  | GPRReg propertyReg = property.gpr(); | 
|  |  | 
|  | SpeculateDoubleOperand value(this, child3); | 
|  |  | 
|  | FPRReg valueReg = value.fpr(); | 
|  |  | 
|  | DFG_TYPE_CHECK( | 
|  | JSValueRegs(), child3, SpecFullRealNumber, | 
|  | m_jit.branchIfNaN(valueReg)); | 
|  |  | 
|  | if (!m_compileOkay) | 
|  | return; | 
|  |  | 
|  | StorageOperand storage(this, child4); | 
|  | GPRReg storageReg = storage.gpr(); | 
|  |  | 
|  | if (node->op() == PutByValAlias) { | 
|  | // Store the value to the array. | 
|  | GPRReg propertyReg = property.gpr(); | 
|  | FPRReg valueReg = value.fpr(); | 
|  | m_jit.storeDouble(valueReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight)); | 
|  |  | 
|  | noResult(m_currentNode); | 
|  | return; | 
|  | } | 
|  |  | 
|  | GPRTemporary temporary; | 
|  | GPRReg temporaryReg = temporaryRegisterForPutByVal(temporary, node); | 
|  |  | 
|  | MacroAssembler::Jump slowCase; | 
|  |  | 
|  | if (arrayMode.isInBounds()) { | 
|  | speculationCheck( | 
|  | OutOfBounds, JSValueRegs(), nullptr, | 
|  | 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())); | 
|  |  | 
|  | slowCase = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfVectorLength())); | 
|  |  | 
|  | if (!arrayMode.isOutOfBounds()) | 
|  | speculationCheck(OutOfBounds, JSValueRegs(), nullptr, slowCase); | 
|  |  | 
|  | m_jit.add32(TrustedImm32(1), propertyReg, temporaryReg); | 
|  | m_jit.store32(temporaryReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())); | 
|  |  | 
|  | inBounds.link(&m_jit); | 
|  | } | 
|  |  | 
|  | m_jit.storeDouble(valueReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight)); | 
|  |  | 
|  | base.use(); | 
|  | property.use(); | 
|  | value.use(); | 
|  | storage.use(); | 
|  |  | 
|  | if (arrayMode.isOutOfBounds()) { | 
|  | addSlowPathGenerator( | 
|  | slowPathCall( | 
|  | slowCase, this, | 
|  | node->ecmaMode().isStrict() | 
|  | ? (node->op() == PutByValDirect ? operationPutDoubleByValDirectBeyondArrayBoundsStrict : operationPutDoubleByValBeyondArrayBoundsStrict) | 
|  | : (node->op() == PutByValDirect ? operationPutDoubleByValDirectBeyondArrayBoundsNonStrict : operationPutDoubleByValBeyondArrayBoundsNonStrict), | 
|  | NoResult, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseReg, propertyReg, valueReg)); | 
|  | } | 
|  |  | 
|  | noResult(m_currentNode, UseChildrenCalledExplicitly); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetCharCodeAt(Node* node) | 
|  | { | 
|  | SpeculateCellOperand string(this, node->child1()); | 
|  | SpeculateStrictInt32Operand index(this, node->child2()); | 
|  | StorageOperand storage(this, node->child3()); | 
|  |  | 
|  | GPRReg stringReg = string.gpr(); | 
|  | GPRReg indexReg = index.gpr(); | 
|  | GPRReg storageReg = storage.gpr(); | 
|  |  | 
|  | ASSERT(speculationChecked(m_state.forNode(node->child1()).m_type, SpecString)); | 
|  |  | 
|  | GPRTemporary scratch(this); | 
|  | GPRReg scratchReg = scratch.gpr(); | 
|  |  | 
|  | m_jit.loadPtr(MacroAssembler::Address(stringReg, JSString::offsetOfValue()), scratchReg); | 
|  |  | 
|  | // unsigned comparison so we can filter out negative indices and indices that are too large | 
|  | speculationCheck(Uncountable, JSValueRegs(), nullptr, m_jit.branch32(MacroAssembler::AboveOrEqual, indexReg, CCallHelpers::Address(scratchReg, StringImpl::lengthMemoryOffset()))); | 
|  |  | 
|  | // Load the character into scratchReg | 
|  | JITCompiler::Jump is16Bit = m_jit.branchTest32(MacroAssembler::Zero, MacroAssembler::Address(scratchReg, StringImpl::flagsOffset()), TrustedImm32(StringImpl::flagIs8Bit())); | 
|  |  | 
|  | m_jit.load8(MacroAssembler::BaseIndex(storageReg, indexReg, MacroAssembler::TimesOne, 0), scratchReg); | 
|  | JITCompiler::Jump cont8Bit = m_jit.jump(); | 
|  |  | 
|  | is16Bit.link(&m_jit); | 
|  |  | 
|  | m_jit.load16(MacroAssembler::BaseIndex(storageReg, indexReg, MacroAssembler::TimesTwo, 0), scratchReg); | 
|  |  | 
|  | cont8Bit.link(&m_jit); | 
|  |  | 
|  | strictInt32Result(scratchReg, m_currentNode); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetByValOnString(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, m_graph.child(node, 0)); | 
|  | SpeculateStrictInt32Operand property(this, m_graph.child(node, 1)); | 
|  | StorageOperand storage(this, m_graph.child(node, 2)); | 
|  | GPRReg baseReg = base.gpr(); | 
|  | 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, Array::Read).alreadyChecked(m_jit.graph(), node, m_state.forNode(m_graph.child(node, 0)))); | 
|  |  | 
|  | // unsigned comparison so we can filter out negative indices and indices that are too large | 
|  | m_jit.loadPtr(MacroAssembler::Address(baseReg, JSString::offsetOfValue()), scratchReg); | 
|  | JITCompiler::Jump outOfBounds = m_jit.branch32( | 
|  | MacroAssembler::AboveOrEqual, propertyReg, | 
|  | MacroAssembler::Address(scratchReg, StringImpl::lengthMemoryOffset())); | 
|  | if (node->arrayMode().isInBounds()) | 
|  | speculationCheck(OutOfBounds, JSValueRegs(), nullptr, outOfBounds); | 
|  |  | 
|  | // Load the character into scratchReg | 
|  | JITCompiler::Jump is16Bit = m_jit.branchTest32(MacroAssembler::Zero, MacroAssembler::Address(scratchReg, StringImpl::flagsOffset()), TrustedImm32(StringImpl::flagIs8Bit())); | 
|  |  | 
|  | m_jit.load8(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesOne, 0), scratchReg); | 
|  | JITCompiler::Jump cont8Bit = m_jit.jump(); | 
|  |  | 
|  | is16Bit.link(&m_jit); | 
|  |  | 
|  | m_jit.load16(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesTwo, 0), scratchReg); | 
|  |  | 
|  | JITCompiler::Jump bigCharacter = | 
|  | m_jit.branch32(MacroAssembler::Above, scratchReg, TrustedImm32(maxSingleCharacterString)); | 
|  |  | 
|  | // 8 bit string values don't need the isASCII check. | 
|  | cont8Bit.link(&m_jit); | 
|  |  | 
|  | VM& vm = this->vm(); | 
|  | m_jit.lshift32(MacroAssembler::TrustedImm32(sizeof(void*) == 4 ? 2 : 3), scratchReg); | 
|  | m_jit.addPtr(TrustedImmPtr(vm.smallStrings.singleCharacterStrings()), scratchReg); | 
|  | m_jit.loadPtr(scratchReg, scratchReg); | 
|  |  | 
|  | addSlowPathGenerator( | 
|  | slowPathCall( | 
|  | bigCharacter, this, operationSingleCharacterString, scratchReg, &vm, 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); | 
|  | Structure* stringPrototypeStructure = globalObject->stringPrototype()->structure(vm); | 
|  | Structure* objectPrototypeStructure = globalObject->objectPrototype()->structure(vm); | 
|  | WTF::loadLoadFence(); | 
|  |  | 
|  | if (globalObject->stringPrototypeChainIsSane()) { | 
|  | // FIXME: This could be captured using a Speculation mode that means "out-of-bounds | 
|  | // loads return a trivial value". Something like OutOfBoundsSaneChain. 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().registerAndWatchStructureTransition(stringPrototypeStructure); | 
|  | m_jit.graph().registerAndWatchStructureTransition(objectPrototypeStructure); | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | addSlowPathGenerator(makeUnique<SaneStringGetByValSlowPathGenerator>( | 
|  | outOfBounds, this, JSValueRegs(scratchReg), TrustedImmPtr::weakPointer(m_graph, globalObject), baseReg, propertyReg)); | 
|  | #else | 
|  | addSlowPathGenerator(makeUnique<SaneStringGetByValSlowPathGenerator>( | 
|  | outOfBounds, this, JSValueRegs(resultTagReg, scratchReg), TrustedImmPtr::weakPointer(m_graph, globalObject), baseReg, propertyReg)); | 
|  | #endif | 
|  | } else { | 
|  | #if USE(JSVALUE64) | 
|  | addSlowPathGenerator( | 
|  | slowPathCall( | 
|  | outOfBounds, this, operationGetByValStringInt, | 
|  | scratchReg, TrustedImmPtr::weakPointer(m_graph, globalObject), baseReg, propertyReg)); | 
|  | #else | 
|  | addSlowPathGenerator( | 
|  | slowPathCall( | 
|  | outOfBounds, this, operationGetByValStringInt, | 
|  | JSValueRegs(resultTagReg, scratchReg), TrustedImmPtr::weakPointer(m_graph, globalObject), 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) | 
|  | { | 
|  | Edge& child = node->child1(); | 
|  | if (child.useKind() == UntypedUse) { | 
|  | JSValueOperand opr(this, child); | 
|  | JSValueRegs oprRegs = opr.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationStringFromCharCodeUntyped, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), oprRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | SpeculateStrictInt32Operand property(this, child); | 
|  | GPRReg propertyReg = property.gpr(); | 
|  | GPRTemporary smallStrings(this); | 
|  | GPRTemporary scratch(this); | 
|  | GPRReg scratchReg = scratch.gpr(); | 
|  | GPRReg smallStringsReg = smallStrings.gpr(); | 
|  |  | 
|  | JITCompiler::JumpList slowCases; | 
|  | slowCases.append(m_jit.branch32(MacroAssembler::Above, propertyReg, TrustedImm32(maxSingleCharacterString))); | 
|  | m_jit.move(TrustedImmPtr(vm().smallStrings.singleCharacterStrings()), smallStringsReg); | 
|  | m_jit.loadPtr(MacroAssembler::BaseIndex(smallStringsReg, propertyReg, MacroAssembler::ScalePtr, 0), scratchReg); | 
|  |  | 
|  | slowCases.append(m_jit.branchTest32(MacroAssembler::Zero, scratchReg)); | 
|  | addSlowPathGenerator(slowPathCall(slowCases, this, operationStringFromCharCode, scratchReg, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), propertyReg)); | 
|  | cellResult(scratchReg, m_currentNode); | 
|  | } | 
|  |  | 
|  | GeneratedOperandType SpeculativeJIT::checkGeneratedTypeForToInt32(Node* node) | 
|  | { | 
|  | VirtualRegister virtualRegister = node->virtualRegister(); | 
|  | GenerationInfo& info = generationInfoFromVirtualRegister(virtualRegister); | 
|  |  | 
|  | switch (info.registerFormat()) { | 
|  | case DataFormatStorage: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  |  | 
|  | case DataFormatBoolean: | 
|  | case DataFormatCell: | 
|  | terminateSpeculativeExecution(Uncountable, JSValueRegs(), nullptr); | 
|  | return GeneratedOperandTypeUnknown; | 
|  |  | 
|  | case DataFormatNone: | 
|  | case DataFormatJSCell: | 
|  | case DataFormatJS: | 
|  | case DataFormatJSBoolean: | 
|  | case DataFormatJSDouble: | 
|  | case DataFormatJSBigInt32: | 
|  | return GeneratedOperandJSValue; | 
|  |  | 
|  | case DataFormatJSInt32: | 
|  | case DataFormatInt32: | 
|  | return GeneratedOperandInteger; | 
|  |  | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return GeneratedOperandTypeUnknown; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileValueToInt32(Node* node) | 
|  | { | 
|  | switch (node->child1().useKind()) { | 
|  | #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.zeroExtend32ToWord(op1GPR, resultGPR); | 
|  | strictInt32Result(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(); | 
|  | #if CPU(ARM64) | 
|  | if (MacroAssemblerARM64::supportsDoubleToInt32ConversionUsingJavaScriptSemantics()) | 
|  | m_jit.convertDoubleToInt32UsingJavaScriptSemantics(fpr, gpr); | 
|  | else | 
|  | #endif | 
|  | { | 
|  | JITCompiler::Jump notTruncatedToInteger = m_jit.branchTruncateDoubleToInt32(fpr, gpr, JITCompiler::BranchIfTruncateFailed); | 
|  | addSlowPathGenerator(slowPathCall(notTruncatedToInteger, this, | 
|  | hasSensibleDoubleToInt() ? operationToInt32SensibleSlow : operationToInt32, NeedToSpill, ExceptionCheckRequirement::CheckNotNeeded, gpr, fpr)); | 
|  | } | 
|  | strictInt32Result(gpr, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | case NumberUse: | 
|  | case NotCellNorBigIntUse: { | 
|  | switch (checkGeneratedTypeForToInt32(node->child1().node())) { | 
|  | case GeneratedOperandInteger: { | 
|  | SpeculateInt32Operand op1(this, node->child1(), ManualOperandSpeculation); | 
|  | GPRTemporary result(this, Reuse, op1); | 
|  | m_jit.move(op1.gpr(), result.gpr()); | 
|  | strictInt32Result(result.gpr(), node, op1.format()); | 
|  | return; | 
|  | } | 
|  | case GeneratedOperandJSValue: { | 
|  | GPRTemporary result(this); | 
|  | #if USE(JSVALUE64) | 
|  | JSValueOperand op1(this, node->child1(), ManualOperandSpeculation); | 
|  |  | 
|  | GPRReg gpr = op1.gpr(); | 
|  | GPRReg resultGpr = result.gpr(); | 
|  | FPRTemporary tempFpr(this); | 
|  | FPRReg fpr = tempFpr.fpr(); | 
|  |  | 
|  | JITCompiler::Jump isInteger = m_jit.branchIfInt32(gpr); | 
|  | JITCompiler::JumpList converted; | 
|  |  | 
|  | if (node->child1().useKind() == NumberUse) { | 
|  | DFG_TYPE_CHECK( | 
|  | JSValueRegs(gpr), node->child1(), SpecBytecodeNumber, | 
|  | m_jit.branchIfNotNumber(gpr)); | 
|  | } else { | 
|  | JITCompiler::Jump isNumber = m_jit.branchIfNumber(gpr); | 
|  |  | 
|  | DFG_TYPE_CHECK( | 
|  | JSValueRegs(gpr), node->child1(), ~SpecCellCheck, m_jit.branchIfCell(JSValueRegs(gpr))); | 
|  | #if USE(BIGINT32) | 
|  | DFG_TYPE_CHECK( | 
|  | JSValueRegs(gpr), node->child1(), ~SpecCellCheck & ~SpecBigInt, m_jit.branchIfBigInt32(JSValueRegs(gpr), resultGpr)); | 
|  | #endif | 
|  |  | 
|  | // It's not a cell: so true turns into 1 and all else turns into 0. | 
|  | m_jit.compare64(JITCompiler::Equal, gpr, TrustedImm32(JSValue::ValueTrue), resultGpr); | 
|  | converted.append(m_jit.jump()); | 
|  |  | 
|  | isNumber.link(&m_jit); | 
|  | } | 
|  |  | 
|  | // First, if we get here we have a double encoded as a JSValue | 
|  | unboxDouble(gpr, resultGpr, fpr); | 
|  | #if CPU(ARM64) | 
|  | if (MacroAssemblerARM64::supportsDoubleToInt32ConversionUsingJavaScriptSemantics()) | 
|  | m_jit.convertDoubleToInt32UsingJavaScriptSemantics(fpr, resultGpr); | 
|  | else | 
|  | #endif | 
|  | { | 
|  | silentSpillAllRegisters(resultGpr); | 
|  | callOperation(operationToInt32, resultGpr, fpr); | 
|  | silentFillAllRegisters(); | 
|  | } | 
|  |  | 
|  | converted.append(m_jit.jump()); | 
|  |  | 
|  | isInteger.link(&m_jit); | 
|  | m_jit.zeroExtend32ToWord(gpr, resultGpr); | 
|  |  | 
|  | converted.link(&m_jit); | 
|  | #else | 
|  | Node* childNode = node->child1().node(); | 
|  | VirtualRegister virtualRegister = childNode->virtualRegister(); | 
|  | GenerationInfo& info = generationInfoFromVirtualRegister(virtualRegister); | 
|  |  | 
|  | JSValueOperand op1(this, node->child1(), ManualOperandSpeculation); | 
|  |  | 
|  | GPRReg payloadGPR = op1.payloadGPR(); | 
|  | GPRReg resultGpr = result.gpr(); | 
|  |  | 
|  | JITCompiler::JumpList converted; | 
|  |  | 
|  | if (info.registerFormat() == DataFormatJSInt32) | 
|  | m_jit.move(payloadGPR, resultGpr); | 
|  | else { | 
|  | GPRReg tagGPR = op1.tagGPR(); | 
|  | FPRTemporary tempFpr(this); | 
|  | FPRReg fpr = tempFpr.fpr(); | 
|  | FPRTemporary scratch(this); | 
|  |  | 
|  | JITCompiler::Jump isInteger = m_jit.branchIfInt32(tagGPR); | 
|  |  | 
|  | if (node->child1().useKind() == NumberUse) { | 
|  | DFG_TYPE_CHECK( | 
|  | op1.jsValueRegs(), node->child1(), SpecBytecodeNumber, | 
|  | m_jit.branch32( | 
|  | MacroAssembler::AboveOrEqual, tagGPR, | 
|  | TrustedImm32(JSValue::LowestTag))); | 
|  | } else { | 
|  | JITCompiler::Jump isNumber = m_jit.branch32(MacroAssembler::Below, tagGPR, TrustedImm32(JSValue::LowestTag)); | 
|  |  | 
|  | DFG_TYPE_CHECK( | 
|  | 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.branchIfBoolean(tagGPR, InvalidGPRReg); | 
|  | m_jit.move(TrustedImm32(0), resultGpr); | 
|  | converted.append(m_jit.jump()); | 
|  |  | 
|  | isBoolean.link(&m_jit); | 
|  | m_jit.move(payloadGPR, resultGpr); | 
|  | converted.append(m_jit.jump()); | 
|  |  | 
|  | isNumber.link(&m_jit); | 
|  | } | 
|  |  | 
|  | unboxDouble(tagGPR, payloadGPR, fpr, scratch.fpr()); | 
|  |  | 
|  | silentSpillAllRegisters(resultGpr); | 
|  | callOperation(operationToInt32, resultGpr, fpr); | 
|  | silentFillAllRegisters(); | 
|  |  | 
|  | converted.append(m_jit.jump()); | 
|  |  | 
|  | isInteger.link(&m_jit); | 
|  | m_jit.move(payloadGPR, resultGpr); | 
|  |  | 
|  | converted.link(&m_jit); | 
|  | } | 
|  | #endif | 
|  | strictInt32Result(resultGpr, node); | 
|  | return; | 
|  | } | 
|  | case GeneratedOperandTypeUnknown: | 
|  | RELEASE_ASSERT(!m_compileOkay); | 
|  | return; | 
|  | } | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | default: | 
|  | ASSERT(!m_compileOkay); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileUInt32ToNumber(Node* node) | 
|  | { | 
|  | if (doesOverflow(node->arithMode())) { | 
|  | if (enableInt52()) { | 
|  | SpeculateInt32Operand op1(this, node->child1()); | 
|  | GPRTemporary result(this, Reuse, op1); | 
|  | m_jit.zeroExtend32ToWord(op1.gpr(), result.gpr()); | 
|  | strictInt52Result(result.gpr(), node); | 
|  | return; | 
|  | } | 
|  | SpeculateInt32Operand op1(this, node->child1()); | 
|  | FPRTemporary result(this); | 
|  |  | 
|  | GPRReg inputGPR = op1.gpr(); | 
|  | FPRReg outputFPR = result.fpr(); | 
|  |  | 
|  | m_jit.convertInt32ToDouble(inputGPR, outputFPR); | 
|  |  | 
|  | JITCompiler::Jump positive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, inputGPR, TrustedImm32(0)); | 
|  | m_jit.addDouble(JITCompiler::AbsoluteAddress(&AssemblyHelpers::twoToThe32), outputFPR); | 
|  | positive.link(&m_jit); | 
|  |  | 
|  | doubleResult(outputFPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | RELEASE_ASSERT(node->arithMode() == Arith::CheckOverflow); | 
|  |  | 
|  | SpeculateInt32Operand op1(this, node->child1()); | 
|  | GPRTemporary result(this); | 
|  |  | 
|  | m_jit.move(op1.gpr(), result.gpr()); | 
|  |  | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, m_jit.branch32(MacroAssembler::LessThan, result.gpr(), TrustedImm32(0))); | 
|  |  | 
|  | strictInt32Result(result.gpr(), node, op1.format()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileDoubleAsInt32(Node* node) | 
|  | { | 
|  | SpeculateDoubleOperand op1(this, node->child1()); | 
|  | FPRTemporary scratch(this); | 
|  | GPRTemporary result(this); | 
|  |  | 
|  | FPRReg valueFPR = op1.fpr(); | 
|  | FPRReg scratchFPR = scratch.fpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | JITCompiler::JumpList failureCases; | 
|  | RELEASE_ASSERT(shouldCheckOverflow(node->arithMode())); | 
|  | m_jit.branchConvertDoubleToInt32( | 
|  | valueFPR, resultGPR, failureCases, scratchFPR, | 
|  | shouldCheckNegativeZero(node->arithMode())); | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, failureCases); | 
|  |  | 
|  | strictInt32Result(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileDoubleRep(Node* node) | 
|  | { | 
|  | switch (node->child1().useKind()) { | 
|  | case RealNumberUse: { | 
|  | JSValueOperand op1(this, node->child1(), 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.branchIfNotNaN(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; | 
|  | } | 
|  |  | 
|  | case NotCellNorBigIntUse: | 
|  | case NumberUse: { | 
|  | 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); | 
|  |  | 
|  | GPRReg op1GPR = op1.gpr(); | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  | FPRReg resultFPR = result.fpr(); | 
|  | JITCompiler::JumpList done; | 
|  |  | 
|  | JITCompiler::Jump isInteger = m_jit.branchIfInt32(op1GPR); | 
|  |  | 
|  | if (node->child1().useKind() == NotCellNorBigIntUse) { | 
|  | JITCompiler::Jump isNumber = m_jit.branchIfNumber(op1GPR); | 
|  | JITCompiler::Jump isUndefined = m_jit.branchIfUndefined(op1GPR); | 
|  |  | 
|  | static constexpr double zero = 0; | 
|  | m_jit.loadDouble(TrustedImmPtr(&zero), resultFPR); | 
|  |  | 
|  | JITCompiler::Jump isNull = m_jit.branchIfNull(op1GPR); | 
|  | done.append(isNull); | 
|  |  | 
|  | DFG_TYPE_CHECK(JSValueRegs(op1GPR), node->child1(), ~SpecCellCheck & ~SpecBigInt, | 
|  | m_jit.branchTest64(JITCompiler::Zero, op1GPR, TrustedImm32(JSValue::BoolTag))); | 
|  |  | 
|  | JITCompiler::Jump isFalse = m_jit.branch64(JITCompiler::Equal, op1GPR, TrustedImm64(JSValue::ValueFalse)); | 
|  | static constexpr double one = 1; | 
|  | m_jit.loadDouble(TrustedImmPtr(&one), resultFPR); | 
|  | done.append(m_jit.jump()); | 
|  | done.append(isFalse); | 
|  |  | 
|  | isUndefined.link(&m_jit); | 
|  | static const double NaN = PNaN; | 
|  | m_jit.loadDouble(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.branchIfNotNumber(op1GPR)); | 
|  | } | 
|  |  | 
|  | unboxDouble(op1GPR, tempGPR, resultFPR); | 
|  | done.append(m_jit.jump()); | 
|  |  | 
|  | 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); | 
|  |  | 
|  | GPRReg op1TagGPR = op1.tagGPR(); | 
|  | GPRReg op1PayloadGPR = op1.payloadGPR(); | 
|  | FPRReg tempFPR = temp.fpr(); | 
|  | FPRReg resultFPR = result.fpr(); | 
|  | JITCompiler::JumpList done; | 
|  |  | 
|  | JITCompiler::Jump isInteger = m_jit.branchIfInt32(op1TagGPR); | 
|  |  | 
|  | if (node->child1().useKind() == NotCellNorBigIntUse) { | 
|  | JITCompiler::Jump isNumber = m_jit.branch32(JITCompiler::Below, op1TagGPR, JITCompiler::TrustedImm32(JSValue::LowestTag + 1)); | 
|  | JITCompiler::Jump isUndefined = m_jit.branchIfUndefined(op1TagGPR); | 
|  |  | 
|  | static constexpr double zero = 0; | 
|  | m_jit.loadDouble(TrustedImmPtr(&zero), resultFPR); | 
|  |  | 
|  | JITCompiler::Jump isNull = m_jit.branchIfNull(op1TagGPR); | 
|  | done.append(isNull); | 
|  |  | 
|  | DFG_TYPE_CHECK(JSValueRegs(op1TagGPR, op1PayloadGPR), node->child1(), ~SpecCell, m_jit.branchIfNotBoolean(op1TagGPR, InvalidGPRReg)); | 
|  |  | 
|  | JITCompiler::Jump isFalse = m_jit.branchTest32(JITCompiler::Zero, op1PayloadGPR, TrustedImm32(1)); | 
|  | static constexpr double one = 1; | 
|  | m_jit.loadDouble(TrustedImmPtr(&one), resultFPR); | 
|  | done.append(m_jit.jump()); | 
|  | done.append(isFalse); | 
|  |  | 
|  | isUndefined.link(&m_jit); | 
|  | static const double NaN = PNaN; | 
|  | m_jit.loadDouble(TrustedImmPtr(&NaN), resultFPR); | 
|  | done.append(m_jit.jump()); | 
|  |  | 
|  | isNumber.link(&m_jit); | 
|  | } else if (needsTypeCheck(node->child1(), SpecBytecodeNumber)) { | 
|  | // This check fails with Int32Tag, but it is OK since Int32 case is already excluded. | 
|  | typeCheck( | 
|  | JSValueRegs(op1TagGPR, op1PayloadGPR), node->child1(), SpecBytecodeNumber, | 
|  | m_jit.branch32(MacroAssembler::AboveOrEqual, op1TagGPR, TrustedImm32(JSValue::LowestTag))); | 
|  | } | 
|  |  | 
|  | unboxDouble(op1TagGPR, op1PayloadGPR, resultFPR, tempFPR); | 
|  | done.append(m_jit.jump()); | 
|  |  | 
|  | isInteger.link(&m_jit); | 
|  | m_jit.convertInt32ToDouble(op1PayloadGPR, resultFPR); | 
|  | done.link(&m_jit); | 
|  | #endif // USE(JSVALUE64) | 
|  |  | 
|  | 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) | 
|  | { | 
|  | d += 0.5; | 
|  | if (!(d > 0)) | 
|  | d = 0; | 
|  | else if (d > 255) | 
|  | d = 255; | 
|  | return d; | 
|  | } | 
|  |  | 
|  | static void compileClampIntegerToByte(JITCompiler& jit, GPRReg result) | 
|  | { | 
|  | MacroAssembler::Jump inBounds = jit.branch32(MacroAssembler::BelowOrEqual, result, JITCompiler::TrustedImm32(0xff)); | 
|  | MacroAssembler::Jump tooBig = jit.branch32(MacroAssembler::GreaterThan, result, JITCompiler::TrustedImm32(0xff)); | 
|  | jit.xorPtr(result, result); | 
|  | MacroAssembler::Jump clamped = jit.jump(); | 
|  | tooBig.link(&jit); | 
|  | jit.move(JITCompiler::TrustedImm32(255), result); | 
|  | clamped.link(&jit); | 
|  | inBounds.link(&jit); | 
|  | } | 
|  |  | 
|  | static void compileClampDoubleToByte(JITCompiler& jit, GPRReg result, FPRReg source, FPRReg scratch) | 
|  | { | 
|  | // Unordered compare so we pick up NaN | 
|  | static constexpr double zero = 0; | 
|  | static constexpr double byteMax = 255; | 
|  | static constexpr double half = 0.5; | 
|  | jit.loadDouble(JITCompiler::TrustedImmPtr(&zero), scratch); | 
|  | MacroAssembler::Jump tooSmall = jit.branchDouble(MacroAssembler::DoubleLessThanOrEqualOrUnordered, source, scratch); | 
|  | jit.loadDouble(JITCompiler::TrustedImmPtr(&byteMax), scratch); | 
|  | MacroAssembler::Jump tooBig = jit.branchDouble(MacroAssembler::DoubleGreaterThanAndOrdered, source, scratch); | 
|  |  | 
|  | jit.loadDouble(JITCompiler::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); | 
|  | jit.truncateDoubleToInt32(scratch, result); | 
|  | MacroAssembler::Jump truncatedInt = jit.jump(); | 
|  |  | 
|  | tooSmall.link(&jit); | 
|  | jit.xorPtr(result, result); | 
|  | MacroAssembler::Jump zeroed = jit.jump(); | 
|  |  | 
|  | tooBig.link(&jit); | 
|  | jit.move(JITCompiler::TrustedImm32(255), result); | 
|  |  | 
|  | truncatedInt.link(&jit); | 
|  | zeroed.link(&jit); | 
|  |  | 
|  | } | 
|  |  | 
|  | 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(), nullptr, jump); | 
|  | } | 
|  |  | 
|  | JITCompiler::Jump SpeculativeJIT::jumpForTypedArrayIsNeuteredIfOutOfBounds(Node* node, GPRReg base, JITCompiler::Jump outOfBounds) | 
|  | { | 
|  | JITCompiler::Jump done; | 
|  | if (outOfBounds.isSet()) { | 
|  | done = m_jit.jump(); | 
|  | if (node->arrayMode().isInBounds()) | 
|  | speculationCheck(OutOfBounds, JSValueSource(), nullptr, outOfBounds); | 
|  | else { | 
|  | outOfBounds.link(&m_jit); | 
|  |  | 
|  | JITCompiler::Jump notWasteful = m_jit.branch32( | 
|  | MacroAssembler::NotEqual, | 
|  | MacroAssembler::Address(base, JSArrayBufferView::offsetOfMode()), | 
|  | TrustedImm32(WastefulTypedArray)); | 
|  |  | 
|  | JITCompiler::Jump hasNullVector; | 
|  | #if CPU(ARM64E) | 
|  | { | 
|  | GPRReg scratch = m_jit.scratchRegister(); | 
|  | DisallowMacroScratchRegisterUsage disallowScratch(m_jit); | 
|  |  | 
|  | m_jit.loadPtr(MacroAssembler::Address(base, JSArrayBufferView::offsetOfVector()), scratch); | 
|  | m_jit.removeArrayPtrTag(scratch); | 
|  | hasNullVector = m_jit.branchTestPtr(MacroAssembler::Zero, scratch); | 
|  | } | 
|  | #else // CPU(ARM64E) | 
|  | hasNullVector = m_jit.branchTestPtr( | 
|  | MacroAssembler::Zero, | 
|  | MacroAssembler::Address(base, JSArrayBufferView::offsetOfVector())); | 
|  | #endif | 
|  | speculationCheck(Uncountable, JSValueSource(), node, hasNullVector); | 
|  | notWasteful.link(&m_jit); | 
|  | } | 
|  | } | 
|  | return done; | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::loadFromIntTypedArray(GPRReg storageReg, GPRReg propertyReg, GPRReg resultReg, TypedArrayType type) | 
|  | { | 
|  | switch (elementSize(type)) { | 
|  | case 1: | 
|  | 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 (isSigned(type)) | 
|  | m_jit.load16SignedExtendTo32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesTwo), resultReg); | 
|  | else | 
|  | m_jit.load16(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesTwo), resultReg); | 
|  | break; | 
|  | case 4: | 
|  | m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesFour), resultReg); | 
|  | break; | 
|  | default: | 
|  | CRASH(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::setIntTypedArrayLoadResult(Node* node, GPRReg resultReg, TypedArrayType type, bool canSpeculate) | 
|  | { | 
|  | if (elementSize(type) < 4 || isSigned(type)) { | 
|  | strictInt32Result(resultReg, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | ASSERT(elementSize(type) == 4 && !isSigned(type)); | 
|  | if (node->shouldSpeculateInt32() && canSpeculate) { | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, m_jit.branch32(MacroAssembler::LessThan, resultReg, TrustedImm32(0))); | 
|  | strictInt32Result(resultReg, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | if (node->shouldSpeculateInt52()) { | 
|  | ASSERT(enableInt52()); | 
|  | m_jit.zeroExtend32ToWord(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)); | 
|  | m_jit.addDouble(JITCompiler::AbsoluteAddress(&AssemblyHelpers::twoToThe32), fresult.fpr()); | 
|  | positive.link(&m_jit); | 
|  | doubleResult(fresult.fpr(), node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetByValOnIntTypedArray(Node* node, TypedArrayType type) | 
|  | { | 
|  | ASSERT(isInt(type)); | 
|  |  | 
|  | SpeculateCellOperand base(this, m_graph.varArgChild(node, 0)); | 
|  | SpeculateStrictInt32Operand property(this, m_graph.varArgChild(node, 1)); | 
|  | StorageOperand storage(this, m_graph.varArgChild(node, 2)); | 
|  |  | 
|  | GPRReg baseReg = base.gpr(); | 
|  | GPRReg propertyReg = property.gpr(); | 
|  | GPRReg storageReg = storage.gpr(); | 
|  |  | 
|  | GPRTemporary result(this); | 
|  | GPRReg resultReg = result.gpr(); | 
|  |  | 
|  | emitTypedArrayBoundsCheck(node, baseReg, propertyReg); | 
|  | loadFromIntTypedArray(storageReg, propertyReg, resultReg, type); | 
|  | bool canSpeculate = true; | 
|  | setIntTypedArrayLoadResult(node, resultReg, type, canSpeculate); | 
|  | } | 
|  |  | 
|  | bool SpeculativeJIT::getIntTypedArrayStoreOperand( | 
|  | GPRTemporary& value, | 
|  | GPRReg property, | 
|  | #if USE(JSVALUE32_64) | 
|  | GPRTemporary& propertyTag, | 
|  | GPRTemporary& valueTag, | 
|  | #endif | 
|  | Edge valueUse, JITCompiler::JumpList& slowPathCases, bool isClamped) | 
|  | { | 
|  | bool isAppropriateConstant = false; | 
|  | if (valueUse->isConstant()) { | 
|  | JSValue jsValue = valueUse->asJSValue(); | 
|  | SpeculatedType expectedType = typeFilterFor(valueUse.useKind()); | 
|  | SpeculatedType actualType = speculationFromValue(jsValue); | 
|  | isAppropriateConstant = (expectedType | actualType) == expectedType; | 
|  | } | 
|  |  | 
|  | if (isAppropriateConstant) { | 
|  | JSValue jsValue = valueUse->asJSValue(); | 
|  | if (!jsValue.isNumber()) { | 
|  | terminateSpeculativeExecution(Uncountable, JSValueRegs(), nullptr); | 
|  | return false; | 
|  | } | 
|  | double d = jsValue.asNumber(); | 
|  | if (isClamped) | 
|  | d = clampDoubleToByte(d); | 
|  | GPRTemporary scratch(this); | 
|  | GPRReg scratchReg = scratch.gpr(); | 
|  | m_jit.move(Imm32(toInt32(d)), scratchReg); | 
|  | value.adopt(scratch); | 
|  | } else { | 
|  | switch (valueUse.useKind()) { | 
|  | case Int32Use: { | 
|  | SpeculateInt32Operand valueOp(this, valueUse); | 
|  | GPRTemporary scratch(this); | 
|  | GPRReg scratchReg = scratch.gpr(); | 
|  | m_jit.move(valueOp.gpr(), scratchReg); | 
|  | if (isClamped) | 
|  | compileClampIntegerToByte(m_jit, scratchReg); | 
|  | value.adopt(scratch); | 
|  | break; | 
|  | } | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | case Int52RepUse: { | 
|  | SpeculateStrictInt52Operand valueOp(this, valueUse); | 
|  | GPRTemporary scratch(this); | 
|  | GPRReg scratchReg = scratch.gpr(); | 
|  | m_jit.move(valueOp.gpr(), scratchReg); | 
|  | if (isClamped) { | 
|  | 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); | 
|  | break; | 
|  | } | 
|  | #endif // USE(JSVALUE64) | 
|  |  | 
|  | case DoubleRepUse: { | 
|  | RELEASE_ASSERT(!isAtomicsIntrinsic(m_currentNode->op())); | 
|  | if (isClamped) { | 
|  | SpeculateDoubleOperand valueOp(this, valueUse); | 
|  | GPRTemporary result(this); | 
|  | FPRTemporary floatScratch(this); | 
|  | FPRReg fpr = valueOp.fpr(); | 
|  | GPRReg gpr = result.gpr(); | 
|  | compileClampDoubleToByte(m_jit, gpr, fpr, floatScratch.fpr()); | 
|  | value.adopt(result); | 
|  | } else { | 
|  | #if USE(JSVALUE32_64) | 
|  | GPRTemporary realPropertyTag(this); | 
|  | propertyTag.adopt(realPropertyTag); | 
|  | GPRReg propertyTagGPR = propertyTag.gpr(); | 
|  |  | 
|  | GPRTemporary realValueTag(this); | 
|  | valueTag.adopt(realValueTag); | 
|  | GPRReg valueTagGPR = valueTag.gpr(); | 
|  | #endif | 
|  | SpeculateDoubleOperand valueOp(this, valueUse); | 
|  | GPRTemporary result(this); | 
|  | FPRReg fpr = valueOp.fpr(); | 
|  | GPRReg gpr = result.gpr(); | 
|  | MacroAssembler::Jump notNaN = m_jit.branchIfNotNaN(fpr); | 
|  | m_jit.xorPtr(gpr, gpr); | 
|  | MacroAssembler::JumpList fixed(m_jit.jump()); | 
|  | notNaN.link(&m_jit); | 
|  |  | 
|  | fixed.append(m_jit.branchTruncateDoubleToInt32( | 
|  | fpr, gpr, MacroAssembler::BranchIfTruncateSuccessful)); | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | m_jit.or64(GPRInfo::numberTagRegister, property); | 
|  | boxDouble(fpr, gpr); | 
|  | #else | 
|  | UNUSED_PARAM(property); | 
|  | m_jit.move(TrustedImm32(JSValue::Int32Tag), propertyTagGPR); | 
|  | boxDouble(fpr, valueTagGPR, gpr); | 
|  | #endif | 
|  | slowPathCases.append(m_jit.jump()); | 
|  |  | 
|  | fixed.link(&m_jit); | 
|  | value.adopt(result); | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | 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(); | 
|  |  | 
|  | Edge valueUse = m_jit.graph().varArgChild(node, 2); | 
|  |  | 
|  | GPRTemporary value; | 
|  | #if USE(JSVALUE32_64) | 
|  | GPRTemporary propertyTag; | 
|  | GPRTemporary valueTag; | 
|  | #endif | 
|  |  | 
|  | JITCompiler::JumpList slowPathCases; | 
|  |  | 
|  | bool result = getIntTypedArrayStoreOperand( | 
|  | value, property, | 
|  | #if USE(JSVALUE32_64) | 
|  | propertyTag, valueTag, | 
|  | #endif | 
|  | valueUse, slowPathCases, isClamped(type)); | 
|  | if (!result) { | 
|  | noResult(node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | GPRReg valueGPR = value.gpr(); | 
|  | #if USE(JSVALUE32_64) | 
|  | GPRReg propertyTagGPR = propertyTag.gpr(); | 
|  | GPRReg valueTagGPR = valueTag.gpr(); | 
|  | #endif | 
|  |  | 
|  | ASSERT_UNUSED(valueGPR, valueGPR != property); | 
|  | ASSERT(valueGPR != base); | 
|  | ASSERT(valueGPR != storageReg); | 
|  | JITCompiler::Jump outOfBounds = jumpForTypedArrayOutOfBounds(node, base, property); | 
|  |  | 
|  | switch (elementSize(type)) { | 
|  | case 1: | 
|  | m_jit.store8(value.gpr(), MacroAssembler::BaseIndex(storageReg, property, MacroAssembler::TimesOne)); | 
|  | break; | 
|  | case 2: | 
|  | m_jit.store16(value.gpr(), MacroAssembler::BaseIndex(storageReg, property, MacroAssembler::TimesTwo)); | 
|  | break; | 
|  | case 4: | 
|  | m_jit.store32(value.gpr(), MacroAssembler::BaseIndex(storageReg, property, MacroAssembler::TimesFour)); | 
|  | break; | 
|  | default: | 
|  | CRASH(); | 
|  | } | 
|  |  | 
|  | JITCompiler::Jump done = jumpForTypedArrayIsNeuteredIfOutOfBounds(node, base, outOfBounds); | 
|  | if (done.isSet()) | 
|  | done.link(&m_jit); | 
|  |  | 
|  | if (!slowPathCases.empty()) { | 
|  | #if USE(JSVALUE64) | 
|  | if (node->op() == PutByValDirect) { | 
|  | addSlowPathGenerator(slowPathCall( | 
|  | slowPathCases, this, | 
|  | node->ecmaMode().isStrict() ? operationPutByValDirectStrict : operationPutByValDirectNonStrict, | 
|  | NoResult, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), base, property, valueGPR)); | 
|  | } else { | 
|  | addSlowPathGenerator(slowPathCall( | 
|  | slowPathCases, this, | 
|  | node->ecmaMode().isStrict() ? operationPutByValStrict : operationPutByValNonStrict, | 
|  | NoResult, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), base, property, valueGPR)); | 
|  | } | 
|  | #else // not USE(JSVALUE64) | 
|  | if (node->op() == PutByValDirect) { | 
|  | addSlowPathGenerator(slowPathCall( | 
|  | slowPathCases, this, | 
|  | node->ecmaMode().isStrict() ? operationPutByValDirectCellStrict : operationPutByValDirectCellNonStrict, | 
|  | NoResult, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), base, JSValueRegs(propertyTagGPR, property), JSValueRegs(valueTagGPR, valueGPR))); | 
|  | } else { | 
|  | addSlowPathGenerator(slowPathCall( | 
|  | slowPathCases, this, | 
|  | node->ecmaMode().isStrict() ? operationPutByValCellStrict : operationPutByValCellNonStrict, | 
|  | NoResult, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), base, JSValueRegs(propertyTagGPR, property), JSValueRegs(valueTagGPR, valueGPR))); | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetByValOnFloatTypedArray(Node* node, TypedArrayType type) | 
|  | { | 
|  | ASSERT(isFloat(type)); | 
|  |  | 
|  | SpeculateCellOperand base(this, m_graph.varArgChild(node, 0)); | 
|  | SpeculateStrictInt32Operand property(this, m_graph.varArgChild(node, 1)); | 
|  | StorageOperand storage(this, m_graph.varArgChild(node, 2)); | 
|  |  | 
|  | GPRReg baseReg = base.gpr(); | 
|  | GPRReg propertyReg = property.gpr(); | 
|  | GPRReg storageReg = storage.gpr(); | 
|  |  | 
|  | FPRTemporary result(this); | 
|  | FPRReg resultReg = result.fpr(); | 
|  | emitTypedArrayBoundsCheck(node, baseReg, propertyReg); | 
|  | switch (elementSize(type)) { | 
|  | case 4: | 
|  | m_jit.loadFloat(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesFour), resultReg); | 
|  | m_jit.convertFloatToDouble(resultReg, resultReg); | 
|  | break; | 
|  | case 8: { | 
|  | m_jit.loadDouble(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), resultReg); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  |  | 
|  | doubleResult(resultReg, node); | 
|  | } | 
|  |  | 
|  | 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(); | 
|  |  | 
|  | Edge baseUse = m_jit.graph().varArgChild(node, 0); | 
|  | Edge valueUse = m_jit.graph().varArgChild(node, 2); | 
|  |  | 
|  | SpeculateDoubleOperand valueOp(this, valueUse); | 
|  | FPRTemporary scratch(this); | 
|  | FPRReg valueFPR = valueOp.fpr(); | 
|  | FPRReg scratchFPR = scratch.fpr(); | 
|  |  | 
|  | ASSERT_UNUSED(baseUse, node->arrayMode().alreadyChecked(m_jit.graph(), node, m_state.forNode(baseUse))); | 
|  |  | 
|  | MacroAssembler::Jump outOfBounds = jumpForTypedArrayOutOfBounds(node, base, property); | 
|  |  | 
|  | switch (elementSize(type)) { | 
|  | case 4: { | 
|  | m_jit.moveDouble(valueFPR, scratchFPR); | 
|  | m_jit.convertDoubleToFloat(valueFPR, scratchFPR); | 
|  | m_jit.storeFloat(scratchFPR, MacroAssembler::BaseIndex(storageReg, property, MacroAssembler::TimesFour)); | 
|  | break; | 
|  | } | 
|  | case 8: | 
|  | m_jit.storeDouble(valueFPR, MacroAssembler::BaseIndex(storageReg, property, MacroAssembler::TimesEight)); | 
|  | break; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  |  | 
|  | JITCompiler::Jump done = jumpForTypedArrayIsNeuteredIfOutOfBounds(node, base, outOfBounds); | 
|  | if (done.isSet()) | 
|  | done.link(&m_jit); | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetByValForObjectWithString(Node* node) | 
|  | { | 
|  | SpeculateCellOperand arg1(this, m_graph.varArgChild(node, 0)); | 
|  | SpeculateCellOperand arg2(this, m_graph.varArgChild(node, 1)); | 
|  |  | 
|  | GPRReg arg1GPR = arg1.gpr(); | 
|  | GPRReg arg2GPR = arg2.gpr(); | 
|  |  | 
|  | speculateObject(m_graph.varArgChild(node, 0), arg1GPR); | 
|  | speculateString(m_graph.varArgChild(node, 1), arg2GPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationGetByValObjectString, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), arg1GPR, arg2GPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetByValForObjectWithSymbol(Node* node) | 
|  | { | 
|  | SpeculateCellOperand arg1(this, m_graph.varArgChild(node, 0)); | 
|  | SpeculateCellOperand arg2(this, m_graph.varArgChild(node, 1)); | 
|  |  | 
|  | GPRReg arg1GPR = arg1.gpr(); | 
|  | GPRReg arg2GPR = arg2.gpr(); | 
|  |  | 
|  | speculateObject(m_graph.varArgChild(node, 0), arg1GPR); | 
|  | speculateSymbol(m_graph.varArgChild(node, 1), arg2GPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationGetByValObjectSymbol, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), arg1GPR, arg2GPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compilePutByValForCellWithString(Node* node, Edge& child1, Edge& child2, Edge& child3) | 
|  | { | 
|  | SpeculateCellOperand arg1(this, child1); | 
|  | SpeculateCellOperand arg2(this, child2); | 
|  | JSValueOperand arg3(this, child3); | 
|  |  | 
|  | GPRReg arg1GPR = arg1.gpr(); | 
|  | GPRReg arg2GPR = arg2.gpr(); | 
|  | JSValueRegs arg3Regs = arg3.jsValueRegs(); | 
|  |  | 
|  | speculateString(child2, arg2GPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(node->ecmaMode().isStrict() ? operationPutByValCellStringStrict : operationPutByValCellStringNonStrict, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), arg1GPR, arg2GPR, arg3Regs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compilePutByValForCellWithSymbol(Node* node, Edge& child1, Edge& child2, Edge& child3) | 
|  | { | 
|  | SpeculateCellOperand arg1(this, child1); | 
|  | SpeculateCellOperand arg2(this, child2); | 
|  | JSValueOperand arg3(this, child3); | 
|  |  | 
|  | GPRReg arg1GPR = arg1.gpr(); | 
|  | GPRReg arg2GPR = arg2.gpr(); | 
|  | JSValueRegs arg3Regs = arg3.jsValueRegs(); | 
|  |  | 
|  | speculateSymbol(child2, arg2GPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(node->ecmaMode().isStrict() ? operationPutByValCellSymbolStrict : operationPutByValCellSymbolNonStrict, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), arg1GPR, arg2GPR, arg3Regs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetByValWithThis(Node* node) | 
|  | { | 
|  | JSValueOperand base(this, node->child1()); | 
|  | JSValueRegs baseRegs = base.jsValueRegs(); | 
|  | JSValueOperand thisValue(this, node->child2()); | 
|  | JSValueRegs thisValueRegs = thisValue.jsValueRegs(); | 
|  | JSValueOperand subscript(this, node->child3()); | 
|  | JSValueRegs subscriptRegs = subscript.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationGetByValWithThis, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseRegs, thisValueRegs, subscriptRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCheckTypeInfoFlags(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  |  | 
|  | // FIXME: This only works for checking if a single bit is set. If we want to check more | 
|  | // than one bit at once, we'll need to fix this: | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=185705 | 
|  | speculationCheck(BadTypeInfoFlags, JSValueRegs(), nullptr, m_jit.branchTest8(MacroAssembler::Zero, MacroAssembler::Address(baseGPR, JSCell::typeInfoFlagsOffset()), MacroAssembler::TrustedImm32(node->typeInfoOperand()))); | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileParseInt(Node* node) | 
|  | { | 
|  | RELEASE_ASSERT(node->child1().useKind() == UntypedUse || node->child1().useKind() == StringUse); | 
|  | if (node->child2()) { | 
|  | SpeculateInt32Operand radix(this, node->child2()); | 
|  | GPRReg radixGPR = radix.gpr(); | 
|  | if (node->child1().useKind() == UntypedUse) { | 
|  | JSValueOperand value(this, node->child1()); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationParseIntGeneric, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), valueRegs, radixGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | SpeculateCellOperand value(this, node->child1()); | 
|  | GPRReg valueGPR = value.gpr(); | 
|  | speculateString(node->child1(), valueGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationParseIntString, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), valueGPR, radixGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (node->child1().useKind() == UntypedUse) { | 
|  | JSValueOperand value(this, node->child1()); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationParseIntNoRadixGeneric, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), valueRegs); | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | SpeculateCellOperand value(this, node->child1()); | 
|  | GPRReg valueGPR = value.gpr(); | 
|  | speculateString(node->child1(), valueGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationParseIntStringNoRadix, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), valueGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileOverridesHasInstance(Node* node) | 
|  | { | 
|  | Node* hasInstanceValueNode = node->child2().node(); | 
|  | JSFunction* defaultHasInstanceFunction = jsCast<JSFunction*>(node->cellOperand()->value()); | 
|  |  | 
|  | MacroAssembler::JumpList notDefault; | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | JSValueOperand hasInstanceValue(this, node->child2()); | 
|  | GPRTemporary result(this); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | // It would be great if constant folding handled automatically the case where we knew the hasInstance function | 
|  | // was a constant. Unfortunately, the folding rule for OverridesHasInstance is in the strength reduction phase | 
|  | // since it relies on OSR information. https://bugs.webkit.org/show_bug.cgi?id=154832 | 
|  | if (!hasInstanceValueNode->isCellConstant() || defaultHasInstanceFunction != hasInstanceValueNode->asCell()) { | 
|  | JSValueRegs hasInstanceValueRegs = hasInstanceValue.jsValueRegs(); | 
|  | #if USE(JSVALUE64) | 
|  | notDefault.append(m_jit.branchPtr(MacroAssembler::NotEqual, hasInstanceValueRegs.gpr(), TrustedImmPtr(node->cellOperand()))); | 
|  | #else | 
|  | notDefault.append(m_jit.branchIfNotCell(hasInstanceValueRegs)); | 
|  | notDefault.append(m_jit.branchPtr(MacroAssembler::NotEqual, hasInstanceValueRegs.payloadGPR(), TrustedImmPtr(node->cellOperand()))); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | // Check that base 'ImplementsDefaultHasInstance'. | 
|  | m_jit.test8(MacroAssembler::Zero, MacroAssembler::Address(baseGPR, JSCell::typeInfoFlagsOffset()), MacroAssembler::TrustedImm32(ImplementsDefaultHasInstance), resultGPR); | 
|  | MacroAssembler::Jump done = m_jit.jump(); | 
|  |  | 
|  | if (!notDefault.empty()) { | 
|  | notDefault.link(&m_jit); | 
|  | m_jit.move(TrustedImm32(1), resultGPR); | 
|  | } | 
|  |  | 
|  | done.link(&m_jit); | 
|  | unblessedBooleanResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileInstanceOfForCells(Node* node, JSValueRegs valueRegs, JSValueRegs prototypeRegs, GPRReg resultGPR, GPRReg scratchGPR, GPRReg scratch2GPR, JITCompiler::Jump slowCase) | 
|  | { | 
|  | CallSiteIndex callSiteIndex = m_jit.addCallSite(node->origin.semantic); | 
|  |  | 
|  | JITInstanceOfGenerator gen( | 
|  | m_jit.codeBlock(), node->origin.semantic, callSiteIndex, usedRegisters(), resultGPR, | 
|  | valueRegs.payloadGPR(), prototypeRegs.payloadGPR(), scratchGPR, scratch2GPR, | 
|  | m_state.forNode(node->child2()).isType(SpecObject | ~SpecCell)); | 
|  | gen.generateFastPath(m_jit); | 
|  |  | 
|  | JITCompiler::JumpList slowCases; | 
|  | slowCases.append(slowCase); | 
|  |  | 
|  | std::unique_ptr<SlowPathGenerator> slowPath = slowPathCall( | 
|  | slowCases, this, operationInstanceOfOptimize, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), gen.stubInfo(), valueRegs, | 
|  | prototypeRegs); | 
|  |  | 
|  | m_jit.addInstanceOf(gen, slowPath.get()); | 
|  | addSlowPathGenerator(WTFMove(slowPath)); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileInstanceOf(Node* node) | 
|  | { | 
|  | #if USE(JSVALUE64) | 
|  | if (node->child1().useKind() == CellUse | 
|  | && node->child2().useKind() == CellUse) { | 
|  | SpeculateCellOperand value(this, node->child1()); | 
|  | SpeculateCellOperand prototype(this, node->child2()); | 
|  |  | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary scratch(this); | 
|  | GPRTemporary scratch2(this); | 
|  |  | 
|  | GPRReg valueGPR = value.gpr(); | 
|  | GPRReg prototypeGPR = prototype.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  | GPRReg scratch2GPR = scratch2.gpr(); | 
|  |  | 
|  | compileInstanceOfForCells(node, JSValueRegs(valueGPR), JSValueRegs(prototypeGPR), resultGPR, scratchGPR, scratch2GPR); | 
|  |  | 
|  | blessedBooleanResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | DFG_ASSERT(m_jit.graph(), node, node->child1().useKind() == UntypedUse); | 
|  | DFG_ASSERT(m_jit.graph(), node, node->child2().useKind() == UntypedUse); | 
|  |  | 
|  | JSValueOperand value(this, node->child1()); | 
|  | JSValueOperand prototype(this, node->child2()); | 
|  |  | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary scratch(this); | 
|  |  | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  | JSValueRegs prototypeRegs = prototype.jsValueRegs(); | 
|  |  | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  |  | 
|  | JITCompiler::Jump isCell = m_jit.branchIfCell(valueRegs); | 
|  | moveFalseTo(resultGPR); | 
|  |  | 
|  | JITCompiler::Jump done = m_jit.jump(); | 
|  |  | 
|  | isCell.link(&m_jit); | 
|  |  | 
|  | JITCompiler::Jump slowCase = m_jit.branchIfNotCell(prototypeRegs); | 
|  |  | 
|  | compileInstanceOfForCells(node, valueRegs, prototypeRegs, resultGPR, scratchGPR, InvalidGPRReg, slowCase); | 
|  |  | 
|  | done.link(&m_jit); | 
|  | blessedBooleanResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileValueBitNot(Node* node) | 
|  | { | 
|  | Edge& child1 = node->child1(); | 
|  |  | 
|  | #if USE(BIGINT32) | 
|  | if (child1.useKind() == BigInt32Use) { | 
|  | SpeculateBigInt32Operand operand(this, child1); | 
|  | GPRTemporary result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | // The following trick relies on details of the representation of BigInt32, and will have to be updated if we move bits around. | 
|  | static_assert(JSValue::BigInt32Tag == 0x12); | 
|  | static_assert(JSValue::BigInt32Mask == static_cast<int64_t>(0xfffe000000000012)); | 
|  | constexpr uint64_t maskForBigInt32Bits = 0x0000ffffffff0000; | 
|  | static_assert(!(JSValue::BigInt32Mask & maskForBigInt32Bits)); | 
|  | m_jit.move(TrustedImm64(maskForBigInt32Bits), resultGPR); | 
|  | m_jit.xor64(operand.gpr(), resultGPR); | 
|  |  | 
|  | jsValueResult(resultGPR, node); | 
|  |  | 
|  | return; | 
|  | } | 
|  | // FIXME: add support for mixed BigInt32 / HeapBigInt | 
|  | #endif | 
|  |  | 
|  | if (child1.useKind() == HeapBigIntUse) { | 
|  | SpeculateCellOperand operand(this, child1); | 
|  | GPRReg operandGPR = operand.gpr(); | 
|  |  | 
|  | speculateHeapBigInt(child1, operandGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | callOperation(operationBitNotHeapBigInt, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), operandGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | ASSERT(child1.useKind() == UntypedUse || child1.useKind() == AnyBigIntUse); | 
|  | JSValueOperand operand(this, child1, ManualOperandSpeculation); | 
|  | speculate(node, child1); // Required for the AnyBigIntUse case | 
|  | JSValueRegs operandRegs = operand.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationValueBitNot, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), operandRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileBitwiseNot(Node* node) | 
|  | { | 
|  | Edge& child1 = node->child1(); | 
|  |  | 
|  | SpeculateInt32Operand operand(this, child1); | 
|  | GPRTemporary result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | m_jit.move(operand.gpr(), resultGPR); | 
|  |  | 
|  | m_jit.not32(resultGPR); | 
|  |  | 
|  | strictInt32Result(resultGPR, node); | 
|  | } | 
|  |  | 
|  | template<typename SnippetGenerator, J_JITOperation_GJJ snippetSlowPathFunction> | 
|  | void SpeculativeJIT::emitUntypedOrAnyBigIntBitOp(Node* node) | 
|  | { | 
|  | Edge& leftChild = node->child1(); | 
|  | Edge& rightChild = node->child2(); | 
|  |  | 
|  | DFG_ASSERT(m_jit.graph(), node, node->isBinaryUseKind(UntypedUse) || node->isBinaryUseKind(AnyBigIntUse) || node->isBinaryUseKind(HeapBigIntUse) || node->isBinaryUseKind(BigInt32Use)); | 
|  |  | 
|  | if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node())) { | 
|  | JSValueOperand left(this, leftChild, ManualOperandSpeculation); | 
|  | JSValueOperand right(this, rightChild, ManualOperandSpeculation); | 
|  | speculate(node, leftChild); | 
|  | speculate(node, rightChild); | 
|  | JSValueRegs leftRegs = left.jsValueRegs(); | 
|  | JSValueRegs rightRegs = right.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(snippetSlowPathFunction, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftRegs, rightRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | Optional<JSValueOperand> left; | 
|  | Optional<JSValueOperand> right; | 
|  |  | 
|  | JSValueRegs leftRegs; | 
|  | JSValueRegs rightRegs; | 
|  |  | 
|  | #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; | 
|  | 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.emplace(this, leftChild, ManualOperandSpeculation); | 
|  | speculate(node, leftChild); // Required for AnyBigIntUse | 
|  | leftRegs = left->jsValueRegs(); | 
|  | } | 
|  | if (!rightOperand.isConst()) { | 
|  | right.emplace(this, rightChild, ManualOperandSpeculation); | 
|  | speculate(node, rightChild); // Required for AnyBigIntUse | 
|  | 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, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftRegs, rightRegs); | 
|  |  | 
|  | silentFillAllRegisters(); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | gen.endJumpList().link(&m_jit); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileValueBitwiseOp(Node* node) | 
|  | { | 
|  | NodeType op = node->op(); | 
|  | Edge& leftChild = node->child1(); | 
|  | Edge& rightChild = node->child2(); | 
|  |  | 
|  | #if USE(BIGINT32) | 
|  | if (leftChild.useKind() == BigInt32Use && rightChild.useKind() == BigInt32Use) { | 
|  | SpeculateBigInt32Operand left(this, leftChild); | 
|  | SpeculateBigInt32Operand right(this, rightChild); | 
|  | GPRTemporary result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | m_jit.move(left.gpr(), resultGPR); | 
|  |  | 
|  | switch (op) { | 
|  | case ValueBitAnd: | 
|  | // No need to unbox/box: bitAnd does not interfere with the encoding of BigInt32 | 
|  | m_jit.and64(right.gpr(), resultGPR); | 
|  | break; | 
|  | case ValueBitOr: | 
|  | // No need to unbox/box: bitOr does not interfere with the encoding of BigInt32 | 
|  | m_jit.or64(right.gpr(), resultGPR); | 
|  | break; | 
|  | case ValueBitXor: | 
|  | // BitXor removes the tag, so we must add it back after doing the operation | 
|  | m_jit.xor64(right.gpr(), resultGPR); | 
|  | m_jit.or64(TrustedImm32(JSValue::BigInt32Tag), resultGPR); | 
|  | break; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  |  | 
|  | jsValueResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  | // FIXME: add support for mixed BigInt32 / HeapBigInt | 
|  | #endif | 
|  |  | 
|  | if (node->isBinaryUseKind(HeapBigIntUse)) { | 
|  | SpeculateCellOperand left(this, node->child1()); | 
|  | SpeculateCellOperand right(this, node->child2()); | 
|  | GPRReg leftGPR = left.gpr(); | 
|  | GPRReg rightGPR = right.gpr(); | 
|  |  | 
|  | speculateHeapBigInt(leftChild, leftGPR); | 
|  | speculateHeapBigInt(rightChild, rightGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | switch (op) { | 
|  | case ValueBitAnd: | 
|  | callOperation(operationBitAndHeapBigInt, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR); | 
|  | break; | 
|  | case ValueBitXor: | 
|  | callOperation(operationBitXorHeapBigInt, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR); | 
|  | break; | 
|  | case ValueBitOr: | 
|  | callOperation(operationBitOrHeapBigInt, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR); | 
|  | break; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  |  | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | switch (op) { | 
|  | case ValueBitAnd: | 
|  | emitUntypedOrAnyBigIntBitOp<JITBitAndGenerator, operationValueBitAnd>(node); | 
|  | return; | 
|  | case ValueBitXor: | 
|  | emitUntypedOrAnyBigIntBitOp<JITBitXorGenerator, operationValueBitXor>(node); | 
|  | return; | 
|  | case ValueBitOr: | 
|  | emitUntypedOrAnyBigIntBitOp<JITBitOrGenerator, operationValueBitOr>(node); | 
|  | return; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileBitwiseOp(Node* node) | 
|  | { | 
|  | NodeType op = node->op(); | 
|  | Edge& leftChild = node->child1(); | 
|  | Edge& rightChild = node->child2(); | 
|  |  | 
|  | if (leftChild->isInt32Constant()) { | 
|  | SpeculateInt32Operand op2(this, rightChild); | 
|  | GPRTemporary result(this, Reuse, op2); | 
|  |  | 
|  | bitOp(op, leftChild->asInt32(), op2.gpr(), result.gpr()); | 
|  |  | 
|  | strictInt32Result(result.gpr(), node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (rightChild->isInt32Constant()) { | 
|  | SpeculateInt32Operand op1(this, leftChild); | 
|  | GPRTemporary result(this, Reuse, op1); | 
|  |  | 
|  | bitOp(op, rightChild->asInt32(), op1.gpr(), result.gpr()); | 
|  |  | 
|  | strictInt32Result(result.gpr(), node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | 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()); | 
|  |  | 
|  | strictInt32Result(result.gpr(), node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::emitUntypedOrBigIntRightShiftBitOp(Node* node) | 
|  | { | 
|  | J_JITOperation_GJJ snippetSlowPathFunction = node->op() == ValueBitRShift | 
|  | ? operationValueBitRShift : operationValueBitURShift; | 
|  | JITRightShiftGenerator::ShiftType shiftType = node->op() == ValueBitRShift | 
|  | ? JITRightShiftGenerator::SignedShift : JITRightShiftGenerator::UnsignedShift; | 
|  |  | 
|  | Edge& leftChild = node->child1(); | 
|  | Edge& rightChild = node->child2(); | 
|  |  | 
|  | if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node()) || node->isBinaryUseKind(BigInt32Use) || node->isBinaryUseKind(AnyBigIntUse)) { | 
|  | JSValueOperand left(this, leftChild, ManualOperandSpeculation); | 
|  | JSValueOperand right(this, rightChild, ManualOperandSpeculation); | 
|  | speculate(node, leftChild); | 
|  | speculate(node, rightChild); | 
|  | JSValueRegs leftRegs = left.jsValueRegs(); | 
|  | JSValueRegs rightRegs = right.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(snippetSlowPathFunction, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftRegs, rightRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | 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.emplace(this, leftChild); | 
|  | leftRegs = left->jsValueRegs(); | 
|  | } | 
|  | if (!rightOperand.isConst()) { | 
|  | right.emplace(this, rightChild); | 
|  | rightRegs = right->jsValueRegs(); | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | callOperation(snippetSlowPathFunction, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftRegs, rightRegs); | 
|  |  | 
|  | silentFillAllRegisters(); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | gen.endJumpList().link(&m_jit); | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileValueLShiftOp(Node* node) | 
|  | { | 
|  | Edge& leftChild = node->child1(); | 
|  | Edge& rightChild = node->child2(); | 
|  |  | 
|  | // FIXME: support BigInt32 | 
|  | if (node->binaryUseKind() == HeapBigIntUse) { | 
|  | SpeculateCellOperand left(this, leftChild); | 
|  | SpeculateCellOperand right(this, rightChild); | 
|  | GPRReg leftGPR = left.gpr(); | 
|  | GPRReg rightGPR = right.gpr(); | 
|  |  | 
|  | speculateHeapBigInt(leftChild, leftGPR); | 
|  | speculateHeapBigInt(rightChild, rightGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | callOperation(operationBitLShiftHeapBigInt, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | emitUntypedOrAnyBigIntBitOp<JITLeftShiftGenerator, operationValueBitLShift>(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileValueBitRShift(Node* node) | 
|  | { | 
|  | Edge& leftChild = node->child1(); | 
|  | Edge& rightChild = node->child2(); | 
|  |  | 
|  | // FIXME: support BigInt32 | 
|  | if (node->isBinaryUseKind(HeapBigIntUse)) { | 
|  | SpeculateCellOperand left(this, leftChild); | 
|  | SpeculateCellOperand right(this, rightChild); | 
|  | GPRReg leftGPR = left.gpr(); | 
|  | GPRReg rightGPR = right.gpr(); | 
|  |  | 
|  | speculateHeapBigInt(leftChild, leftGPR); | 
|  | speculateHeapBigInt(rightChild, rightGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationBitRShiftHeapBigInt, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | emitUntypedOrBigIntRightShiftBitOp(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileShiftOp(Node* node) | 
|  | { | 
|  | NodeType op = node->op(); | 
|  | Edge& leftChild = node->child1(); | 
|  | Edge& rightChild = node->child2(); | 
|  |  | 
|  | if (leftChild.useKind() == UntypedUse || rightChild.useKind() == UntypedUse) { | 
|  | RELEASE_ASSERT(op == BitURShift); | 
|  | emitUntypedOrBigIntRightShiftBitOp(node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (rightChild->isInt32Constant()) { | 
|  | SpeculateInt32Operand op1(this, leftChild); | 
|  | GPRTemporary result(this, Reuse, op1); | 
|  |  | 
|  | shiftOp(op, op1.gpr(), rightChild->asInt32() & 0x1f, result.gpr()); | 
|  |  | 
|  | strictInt32Result(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()); | 
|  |  | 
|  | strictInt32Result(result.gpr(), node); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileValueAdd(Node* node) | 
|  | { | 
|  | Edge& leftChild = node->child1(); | 
|  | Edge& rightChild = node->child2(); | 
|  |  | 
|  | #if USE(BIGINT32) | 
|  | // FIXME: Introduce another BigInt32 code generation: binary use kinds are BigIntUse32, but result is SpecAnyInt and accepting overflow. | 
|  | // Let's distinguish these modes based on result type information by introducing NodeResultBigInt32. | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=210957 | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=211040 | 
|  | if (node->isBinaryUseKind(BigInt32Use)) { | 
|  | SpeculateBigInt32Operand left(this, leftChild); | 
|  | SpeculateBigInt32Operand right(this, rightChild); | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary temp(this); | 
|  |  | 
|  | GPRReg leftGPR = left.gpr(); | 
|  | GPRReg rightGPR = right.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  |  | 
|  | m_jit.unboxBigInt32(leftGPR, resultGPR); | 
|  | m_jit.unboxBigInt32(rightGPR, tempGPR); | 
|  |  | 
|  | MacroAssembler::Jump check = m_jit.branchAdd32(MacroAssembler::Overflow, resultGPR, tempGPR, resultGPR); | 
|  |  | 
|  | speculationCheck(BigInt32Overflow, JSValueRegs(), nullptr, check); | 
|  |  | 
|  | m_jit.boxBigInt32(resultGPR); | 
|  | jsValueResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (node->isBinaryUseKind(AnyBigIntUse)) { | 
|  | JSValueOperand left(this, leftChild, ManualOperandSpeculation); | 
|  | JSValueOperand right(this, rightChild, ManualOperandSpeculation); | 
|  | speculate(node, leftChild); | 
|  | speculate(node, rightChild); | 
|  | JSValueRegs leftRegs = left.jsValueRegs(); | 
|  | JSValueRegs rightRegs = right.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | // FIXME: call a more specialized function | 
|  | callOperation(operationValueAddNotNumber, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftRegs, rightRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  | // FIXME: add support for mixed BigInt32/HeapBigInt | 
|  | #endif // USE(BIGINT32) | 
|  |  | 
|  | if (node->isBinaryUseKind(HeapBigIntUse)) { | 
|  | SpeculateCellOperand left(this, leftChild); | 
|  | SpeculateCellOperand right(this, rightChild); | 
|  | GPRReg leftGPR = left.gpr(); | 
|  | GPRReg rightGPR = right.gpr(); | 
|  |  | 
|  | speculateHeapBigInt(leftChild, leftGPR); | 
|  | speculateHeapBigInt(rightChild, rightGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationAddHeapBigInt, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node())) { | 
|  | JSValueOperand left(this, leftChild); | 
|  | JSValueOperand right(this, rightChild); | 
|  | JSValueRegs leftRegs = left.jsValueRegs(); | 
|  | JSValueRegs rightRegs = right.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationValueAddNotNumber, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftRegs, rightRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | bool needsScratchGPRReg = true; | 
|  | bool needsScratchFPRReg = false; | 
|  | #else | 
|  | bool needsScratchGPRReg = true; | 
|  | bool needsScratchFPRReg = true; | 
|  | #endif | 
|  |  | 
|  | CodeBlock* baselineCodeBlock = m_jit.graph().baselineCodeBlockFor(node->origin.semantic); | 
|  | BytecodeIndex bytecodeIndex = node->origin.semantic.bytecodeIndex(); | 
|  | BinaryArithProfile* arithProfile = baselineCodeBlock->binaryArithProfileForBytecodeIndex(bytecodeIndex); | 
|  | JITAddIC* addIC = m_jit.codeBlock()->addJITAddIC(arithProfile); | 
|  | auto repatchingFunction = operationValueAddOptimize; | 
|  | auto nonRepatchingFunction = operationValueAdd; | 
|  |  | 
|  | compileMathIC(node, addIC, needsScratchGPRReg, needsScratchFPRReg, repatchingFunction, nonRepatchingFunction); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileValueSub(Node* node) | 
|  | { | 
|  | Edge& leftChild = node->child1(); | 
|  | Edge& rightChild = node->child2(); | 
|  |  | 
|  | #if USE(BIGINT32) | 
|  | // FIXME: Introduce another BigInt32 code generation: binary use kinds are BigIntUse32, but result is SpecAnyInt and accepting overflow. | 
|  | // Let's distinguish these modes based on result type information by introducing NodeResultBigInt32. | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=210957 | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=211040 | 
|  | if (node->binaryUseKind() == BigInt32Use) { | 
|  | SpeculateBigInt32Operand left(this, node->child1()); | 
|  | SpeculateBigInt32Operand right(this, node->child2()); | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary temp(this); | 
|  |  | 
|  | GPRReg leftGPR = left.gpr(); | 
|  | GPRReg rightGPR = right.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  |  | 
|  | m_jit.unboxBigInt32(leftGPR, resultGPR); | 
|  | m_jit.unboxBigInt32(rightGPR, tempGPR); | 
|  |  | 
|  | MacroAssembler::Jump check = m_jit.branchSub32(MacroAssembler::Overflow, resultGPR, tempGPR, resultGPR); | 
|  |  | 
|  | speculationCheck(BigInt32Overflow, JSValueRegs(), nullptr, check); | 
|  |  | 
|  | m_jit.boxBigInt32(resultGPR); | 
|  | jsValueResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  | // FIXME: add support for mixed BigInt32/HeapBigInt | 
|  |  | 
|  | // FIXME: why do compileValueAdd/compileValueMul use isKnownNotNumber but not ValueSub? | 
|  | if (node->binaryUseKind() == AnyBigIntUse) { | 
|  | JSValueOperand left(this, leftChild, ManualOperandSpeculation); | 
|  | JSValueOperand right(this, rightChild, ManualOperandSpeculation); | 
|  | speculateAnyBigInt(leftChild); | 
|  | speculateAnyBigInt(rightChild); | 
|  | JSValueRegs leftRegs = left.jsValueRegs(); | 
|  | JSValueRegs rightRegs = right.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationValueSub, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftRegs, rightRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  | #endif // USE(BIGINT32) | 
|  |  | 
|  | if (node->binaryUseKind() == HeapBigIntUse) { | 
|  | SpeculateCellOperand left(this, node->child1()); | 
|  | SpeculateCellOperand right(this, node->child2()); | 
|  | GPRReg leftGPR = left.gpr(); | 
|  | GPRReg rightGPR = right.gpr(); | 
|  |  | 
|  | speculateHeapBigInt(leftChild, leftGPR); | 
|  | speculateHeapBigInt(rightChild, rightGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | callOperation(operationSubHeapBigInt, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR); | 
|  |  | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | bool needsScratchGPRReg = true; | 
|  | bool needsScratchFPRReg = false; | 
|  | #else | 
|  | bool needsScratchGPRReg = true; | 
|  | bool needsScratchFPRReg = true; | 
|  | #endif | 
|  |  | 
|  | CodeBlock* baselineCodeBlock = m_jit.graph().baselineCodeBlockFor(node->origin.semantic); | 
|  | BytecodeIndex bytecodeIndex = node->origin.semantic.bytecodeIndex(); | 
|  | BinaryArithProfile* arithProfile = baselineCodeBlock->binaryArithProfileForBytecodeIndex(bytecodeIndex); | 
|  | JITSubIC* subIC = m_jit.codeBlock()->addJITSubIC(arithProfile); | 
|  | auto repatchingFunction = operationValueSubOptimize; | 
|  | auto nonRepatchingFunction = operationValueSub; | 
|  |  | 
|  | compileMathIC(node, subIC, needsScratchGPRReg, needsScratchFPRReg, repatchingFunction, nonRepatchingFunction); | 
|  | } | 
|  |  | 
|  | template <typename Generator, typename RepatchingFunction, typename NonRepatchingFunction> | 
|  | void SpeculativeJIT::compileMathIC(Node* node, JITBinaryMathIC<Generator>* mathIC, bool needsScratchGPRReg, bool needsScratchFPRReg, RepatchingFunction repatchingFunction, NonRepatchingFunction nonRepatchingFunction) | 
|  | { | 
|  | Edge& leftChild = node->child1(); | 
|  | Edge& rightChild = node->child2(); | 
|  |  | 
|  | Optional<JSValueOperand> left; | 
|  | Optional<JSValueOperand> right; | 
|  |  | 
|  | JSValueRegs leftRegs; | 
|  | JSValueRegs rightRegs; | 
|  |  | 
|  | FPRTemporary leftNumber(this); | 
|  | FPRTemporary rightNumber(this); | 
|  | FPRReg leftFPR = leftNumber.fpr(); | 
|  | FPRReg rightFPR = rightNumber.fpr(); | 
|  |  | 
|  | GPRReg scratchGPR = InvalidGPRReg; | 
|  | FPRReg scratchFPR = InvalidFPRReg; | 
|  |  | 
|  | Optional<FPRTemporary> fprScratch; | 
|  | if (needsScratchFPRReg) { | 
|  | fprScratch.emplace(this); | 
|  | scratchFPR = fprScratch->fpr(); | 
|  | } | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | Optional<GPRTemporary> gprScratch; | 
|  | if (needsScratchGPRReg) { | 
|  | gprScratch.emplace(this); | 
|  | scratchGPR = gprScratch->gpr(); | 
|  | } | 
|  | GPRTemporary result(this); | 
|  | JSValueRegs resultRegs = JSValueRegs(result.gpr()); | 
|  | #else | 
|  | GPRTemporary resultTag(this); | 
|  | GPRTemporary resultPayload(this); | 
|  | JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr()); | 
|  | if (needsScratchGPRReg) | 
|  | scratchGPR = resultRegs.tagGPR(); | 
|  | #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()); | 
|  | ASSERT(!(Generator::isLeftOperandValidConstant(leftOperand) && Generator::isRightOperandValidConstant(rightOperand))); | 
|  |  | 
|  | if (!Generator::isLeftOperandValidConstant(leftOperand)) { | 
|  | left.emplace(this, leftChild); | 
|  | leftRegs = left->jsValueRegs(); | 
|  | } | 
|  | if (!Generator::isRightOperandValidConstant(rightOperand)) { | 
|  | right.emplace(this, rightChild); | 
|  | rightRegs = right->jsValueRegs(); | 
|  | } | 
|  |  | 
|  | #if ENABLE(MATH_IC_STATS) | 
|  | auto inlineStart = m_jit.label(); | 
|  | #endif | 
|  |  | 
|  | Box<MathICGenerationState> addICGenerationState = Box<MathICGenerationState>::create(); | 
|  | mathIC->m_generator = Generator(leftOperand, rightOperand, resultRegs, leftRegs, rightRegs, leftFPR, rightFPR, scratchGPR, scratchFPR); | 
|  |  | 
|  | bool shouldEmitProfiling = false; | 
|  | bool generatedInline = mathIC->generateInline(m_jit, *addICGenerationState, shouldEmitProfiling); | 
|  | if (generatedInline) { | 
|  | ASSERT(!addICGenerationState->slowPathJumps.empty()); | 
|  |  | 
|  | Vector<SilentRegisterSavePlan> savePlans; | 
|  | silentSpillAllRegistersImpl(false, savePlans, resultRegs); | 
|  |  | 
|  | auto done = m_jit.label(); | 
|  |  | 
|  | addSlowPathGeneratorLambda([=, savePlans = WTFMove(savePlans)] () { | 
|  | addICGenerationState->slowPathJumps.link(&m_jit); | 
|  | addICGenerationState->slowPathStart = m_jit.label(); | 
|  | #if ENABLE(MATH_IC_STATS) | 
|  | auto slowPathStart = m_jit.label(); | 
|  | #endif | 
|  |  | 
|  | silentSpill(savePlans); | 
|  |  | 
|  | auto innerLeftRegs = leftRegs; | 
|  | auto innerRightRegs = rightRegs; | 
|  | if (Generator::isLeftOperandValidConstant(leftOperand)) { | 
|  | innerLeftRegs = resultRegs; | 
|  | m_jit.moveValue(leftChild->asJSValue(), innerLeftRegs); | 
|  | } else if (Generator::isRightOperandValidConstant(rightOperand)) { | 
|  | innerRightRegs = resultRegs; | 
|  | m_jit.moveValue(rightChild->asJSValue(), innerRightRegs); | 
|  | } | 
|  |  | 
|  | if (addICGenerationState->shouldSlowPathRepatch) | 
|  | addICGenerationState->slowPathCall = callOperation(bitwise_cast<J_JITOperation_GJJMic>(repatchingFunction), resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), innerLeftRegs, innerRightRegs, TrustedImmPtr(mathIC)); | 
|  | else | 
|  | addICGenerationState->slowPathCall = callOperation(nonRepatchingFunction, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), innerLeftRegs, innerRightRegs); | 
|  |  | 
|  | silentFill(savePlans); | 
|  | m_jit.exceptionCheck(); | 
|  | m_jit.jump().linkTo(done, &m_jit); | 
|  |  | 
|  | m_jit.addLinkTask([=] (LinkBuffer& linkBuffer) { | 
|  | mathIC->finalizeInlineCode(*addICGenerationState, linkBuffer); | 
|  | }); | 
|  |  | 
|  | #if ENABLE(MATH_IC_STATS) | 
|  | auto slowPathEnd = m_jit.label(); | 
|  | m_jit.addLinkTask([=] (LinkBuffer& linkBuffer) { | 
|  | size_t size = static_cast<char*>(linkBuffer.locationOf(slowPathEnd).executableAddress()) - static_cast<char*>(linkBuffer.locationOf(slowPathStart).executableAddress()); | 
|  | mathIC->m_generatedCodeSize += size; | 
|  | }); | 
|  | #endif | 
|  |  | 
|  | }); | 
|  | } else { | 
|  | if (Generator::isLeftOperandValidConstant(leftOperand)) { | 
|  | left.emplace(this, leftChild); | 
|  | leftRegs = left->jsValueRegs(); | 
|  | } else if (Generator::isRightOperandValidConstant(rightOperand)) { | 
|  | right.emplace(this, rightChild); | 
|  | rightRegs = right->jsValueRegs(); | 
|  | } | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(nonRepatchingFunction, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftRegs, rightRegs); | 
|  | m_jit.exceptionCheck(); | 
|  | } | 
|  |  | 
|  | #if ENABLE(MATH_IC_STATS) | 
|  | auto inlineEnd = m_jit.label(); | 
|  | m_jit.addLinkTask([=] (LinkBuffer& linkBuffer) { | 
|  | size_t size = static_cast<char*>(linkBuffer.locationOf(inlineEnd).executableAddress()) - static_cast<char*>(linkBuffer.locationOf(inlineStart).executableAddress()); | 
|  | mathIC->m_generatedCodeSize += size; | 
|  | }); | 
|  | #endif | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | 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, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), valueRegs, constructorGPR, hasInstanceRegs)); | 
|  |  | 
|  | unblessedBooleanResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileIsCellWithType(Node* node) | 
|  | { | 
|  | switch (node->child1().useKind()) { | 
|  | case UntypedUse: { | 
|  | JSValueOperand value(this, node->child1()); | 
|  | GPRTemporary result(this, Reuse, value, PayloadWord); | 
|  |  | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | JITCompiler::Jump isNotCell = m_jit.branchIfNotCell(valueRegs); | 
|  |  | 
|  | m_jit.compare8(JITCompiler::Equal, | 
|  | JITCompiler::Address(valueRegs.payloadGPR(), JSCell::typeInfoTypeOffset()), | 
|  | TrustedImm32(node->queriedType()), | 
|  | resultGPR); | 
|  | blessBoolean(resultGPR); | 
|  | JITCompiler::Jump done = m_jit.jump(); | 
|  |  | 
|  | isNotCell.link(&m_jit); | 
|  | moveFalseTo(resultGPR); | 
|  |  | 
|  | done.link(&m_jit); | 
|  | blessedBooleanResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | case CellUse: { | 
|  | SpeculateCellOperand cell(this, node->child1()); | 
|  | GPRTemporary result(this, Reuse, cell); | 
|  |  | 
|  | GPRReg cellGPR = cell.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | m_jit.compare8(JITCompiler::Equal, | 
|  | JITCompiler::Address(cellGPR, JSCell::typeInfoTypeOffset()), | 
|  | TrustedImm32(node->queriedType()), | 
|  | resultGPR); | 
|  | blessBoolean(resultGPR); | 
|  | blessedBooleanResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileIsTypedArrayView(Node* node) | 
|  | { | 
|  | JSValueOperand value(this, node->child1()); | 
|  | GPRTemporary result(this, Reuse, value, PayloadWord); | 
|  |  | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | JITCompiler::Jump isNotCell = m_jit.branchIfNotCell(valueRegs); | 
|  |  | 
|  | m_jit.load8(JITCompiler::Address(valueRegs.payloadGPR(), JSCell::typeInfoTypeOffset()), resultGPR); | 
|  | m_jit.sub32(TrustedImm32(FirstTypedArrayType), resultGPR); | 
|  | m_jit.compare32(JITCompiler::Below, | 
|  | resultGPR, | 
|  | TrustedImm32(NumberOfTypedArrayTypesExcludingDataView), | 
|  | resultGPR); | 
|  | blessBoolean(resultGPR); | 
|  | JITCompiler::Jump done = m_jit.jump(); | 
|  |  | 
|  | isNotCell.link(&m_jit); | 
|  | moveFalseTo(resultGPR); | 
|  |  | 
|  | done.link(&m_jit); | 
|  | blessedBooleanResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileToObjectOrCallObjectConstructor(Node* node) | 
|  | { | 
|  | RELEASE_ASSERT(node->child1().useKind() == UntypedUse); | 
|  |  | 
|  | JSValueOperand value(this, node->child1()); | 
|  | GPRTemporary result(this, Reuse, value, PayloadWord); | 
|  |  | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | MacroAssembler::JumpList slowCases; | 
|  | slowCases.append(m_jit.branchIfNotCell(valueRegs)); | 
|  | slowCases.append(m_jit.branchIfNotObject(valueRegs.payloadGPR())); | 
|  | m_jit.move(valueRegs.payloadGPR(), resultGPR); | 
|  |  | 
|  | if (node->op() == ToObject) { | 
|  | UniquedStringImpl* errorMessage = nullptr; | 
|  | if (node->identifierNumber() != UINT32_MAX) | 
|  | errorMessage = identifierUID(node->identifierNumber()); | 
|  | addSlowPathGenerator(slowPathCall(slowCases, this, operationToObject, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), valueRegs, errorMessage)); | 
|  | } else | 
|  | addSlowPathGenerator(slowPathCall(slowCases, this, operationCallObjectConstructor, resultGPR, TrustedImmPtr(node->cellOperand()), valueRegs)); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileArithAdd(Node* node) | 
|  | { | 
|  | switch (node->binaryUseKind()) { | 
|  | case Int32Use: { | 
|  | ASSERT(!shouldCheckNegativeZero(node->arithMode())); | 
|  |  | 
|  | if (node->child2()->isInt32Constant()) { | 
|  | SpeculateInt32Operand op1(this, node->child1()); | 
|  | GPRTemporary result(this, Reuse, op1); | 
|  |  | 
|  | GPRReg gpr1 = op1.gpr(); | 
|  | int32_t imm2 = node->child2()->asInt32(); | 
|  | GPRReg gprResult = result.gpr(); | 
|  |  | 
|  | if (!shouldCheckOverflow(node->arithMode())) { | 
|  | m_jit.add32(Imm32(imm2), gpr1, gprResult); | 
|  | strictInt32Result(gprResult, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | MacroAssembler::Jump check = m_jit.branchAdd32(MacroAssembler::Overflow, gpr1, Imm32(imm2), gprResult); | 
|  | if (gpr1 == gprResult) { | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, check, | 
|  | SpeculationRecovery(SpeculativeAddImmediate, gpr1, imm2)); | 
|  | } else | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, check); | 
|  |  | 
|  | strictInt32Result(gprResult, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | 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 (!shouldCheckOverflow(node->arithMode())) | 
|  | m_jit.add32(gpr1, gpr2, gprResult); | 
|  | else { | 
|  | MacroAssembler::Jump check = m_jit.branchAdd32(MacroAssembler::Overflow, gpr1, gpr2, gprResult); | 
|  |  | 
|  | if (gpr1 == gprResult && gpr2 == gprResult) | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, check, SpeculationRecovery(SpeculativeAddSelf, gprResult, gpr2)); | 
|  | else if (gpr1 == gprResult) | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, check, SpeculationRecovery(SpeculativeAdd, gprResult, gpr2)); | 
|  | else if (gpr2 == gprResult) | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, check, SpeculationRecovery(SpeculativeAdd, gprResult, gpr1)); | 
|  | else | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, check); | 
|  | } | 
|  |  | 
|  | strictInt32Result(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(SpecNonInt32AsInt52) | 
|  | && !m_state.forNode(node->child2()).couldBeType(SpecNonInt32AsInt52)) { | 
|  | 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(), nullptr, | 
|  | m_jit.branchAdd64(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, op2); | 
|  |  | 
|  | FPRReg reg1 = op1.fpr(); | 
|  | FPRReg reg2 = op2.fpr(); | 
|  | m_jit.addDouble(reg1, reg2, result.fpr()); | 
|  |  | 
|  | doubleResult(result.fpr(), node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileArithAbs(Node* node) | 
|  | { | 
|  | switch (node->child1().useKind()) { | 
|  | case Int32Use: { | 
|  | SpeculateStrictInt32Operand op1(this, node->child1()); | 
|  | GPRTemporary result(this, Reuse, op1); | 
|  | GPRTemporary scratch(this); | 
|  |  | 
|  | m_jit.move(op1.gpr(), result.gpr()); | 
|  | m_jit.rshift32(result.gpr(), MacroAssembler::TrustedImm32(31), scratch.gpr()); | 
|  | m_jit.add32(scratch.gpr(), result.gpr()); | 
|  | m_jit.xor32(scratch.gpr(), result.gpr()); | 
|  | if (shouldCheckOverflow(node->arithMode())) | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, m_jit.branchTest32(MacroAssembler::Signed, result.gpr())); | 
|  | strictInt32Result(result.gpr(), node); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case DoubleRepUse: { | 
|  | SpeculateDoubleOperand op1(this, node->child1()); | 
|  | FPRTemporary result(this); | 
|  |  | 
|  | m_jit.absDouble(op1.fpr(), result.fpr()); | 
|  | doubleResult(result.fpr(), node); | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: { | 
|  | DFG_ASSERT(m_jit.graph(), node, node->child1().useKind() == UntypedUse, node->child1().useKind()); | 
|  | JSValueOperand op1(this, node->child1()); | 
|  | JSValueRegs op1Regs = op1.jsValueRegs(); | 
|  | flushRegisters(); | 
|  | FPRResult result(this); | 
|  | callOperation(operationArithAbs, result.fpr(), TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), op1Regs); | 
|  | m_jit.exceptionCheck(); | 
|  | doubleResult(result.fpr(), node); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileArithClz32(Node* node) | 
|  | { | 
|  | if (node->child1().useKind() == Int32Use || node->child1().useKind() == KnownInt32Use) { | 
|  | SpeculateInt32Operand value(this, node->child1()); | 
|  | GPRTemporary result(this, Reuse, value); | 
|  | GPRReg valueReg = value.gpr(); | 
|  | GPRReg resultReg = result.gpr(); | 
|  | m_jit.countLeadingZeros32(valueReg, resultReg); | 
|  | strictInt32Result(resultReg, node); | 
|  | return; | 
|  | } | 
|  | JSValueOperand op1(this, node->child1()); | 
|  | JSValueRegs op1Regs = op1.jsValueRegs(); | 
|  | GPRTemporary result(this); | 
|  | GPRReg resultReg = result.gpr(); | 
|  | flushRegisters(); | 
|  | callOperation(operationArithClz32, resultReg, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), op1Regs); | 
|  | m_jit.exceptionCheck(); | 
|  | strictInt32Result(resultReg, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileArithDoubleUnaryOp(Node* node, double (*doubleFunction)(double), double (*operation)(JSGlobalObject*, EncodedJSValue)) | 
|  | { | 
|  | if (node->child1().useKind() == DoubleRepUse) { | 
|  | SpeculateDoubleOperand op1(this, node->child1()); | 
|  | FPRReg op1FPR = op1.fpr(); | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | FPRResult result(this); | 
|  | callOperation(doubleFunction, result.fpr(), op1FPR); | 
|  |  | 
|  | doubleResult(result.fpr(), node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | JSValueOperand op1(this, node->child1()); | 
|  | JSValueRegs op1Regs = op1.jsValueRegs(); | 
|  | flushRegisters(); | 
|  | FPRResult result(this); | 
|  | callOperation(operation, result.fpr(), TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), op1Regs); | 
|  | m_jit.exceptionCheck(); | 
|  | doubleResult(result.fpr(), node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileArithSub(Node* node) | 
|  | { | 
|  | switch (node->binaryUseKind()) { | 
|  | case Int32Use: { | 
|  | ASSERT(!shouldCheckNegativeZero(node->arithMode())); | 
|  |  | 
|  | if (node->child2()->isInt32Constant()) { | 
|  | SpeculateInt32Operand op1(this, node->child1()); | 
|  | int32_t imm2 = node->child2()->asInt32(); | 
|  | GPRTemporary result(this); | 
|  |  | 
|  | if (!shouldCheckOverflow(node->arithMode())) { | 
|  | m_jit.move(op1.gpr(), result.gpr()); | 
|  | m_jit.sub32(Imm32(imm2), result.gpr()); | 
|  | } else { | 
|  | GPRTemporary scratch(this); | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, m_jit.branchSub32(MacroAssembler::Overflow, op1.gpr(), Imm32(imm2), result.gpr(), scratch.gpr())); | 
|  | } | 
|  |  | 
|  | strictInt32Result(result.gpr(), node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | 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 (!shouldCheckOverflow(node->arithMode())) | 
|  | m_jit.sub32(op2.gpr(), result.gpr()); | 
|  | else | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, m_jit.branchSub32(MacroAssembler::Overflow, op2.gpr(), result.gpr())); | 
|  |  | 
|  | strictInt32Result(result.gpr(), node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | SpeculateInt32Operand op1(this, node->child1()); | 
|  | SpeculateInt32Operand op2(this, node->child2()); | 
|  | GPRTemporary result(this); | 
|  |  | 
|  | if (!shouldCheckOverflow(node->arithMode())) { | 
|  | m_jit.move(op1.gpr(), result.gpr()); | 
|  | m_jit.sub32(op2.gpr(), result.gpr()); | 
|  | } else | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, m_jit.branchSub32(MacroAssembler::Overflow, op1.gpr(), op2.gpr(), result.gpr())); | 
|  |  | 
|  | strictInt32Result(result.gpr(), 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(SpecNonInt32AsInt52) | 
|  | && !m_state.forNode(node->child2()).couldBeType(SpecNonInt32AsInt52)) { | 
|  | 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(), nullptr, | 
|  | 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); | 
|  |  | 
|  | FPRReg reg1 = op1.fpr(); | 
|  | FPRReg reg2 = op2.fpr(); | 
|  | m_jit.subDouble(reg1, reg2, result.fpr()); | 
|  |  | 
|  | doubleResult(result.fpr(), node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileIncOrDec(Node* node) | 
|  | { | 
|  | // In all other cases the node should have been transformed into an add or a sub by FixupPhase | 
|  | ASSERT(node->child1().useKind() == UntypedUse); | 
|  |  | 
|  | JSValueOperand op1(this, node->child1()); | 
|  | JSValueRegs op1Regs = op1.jsValueRegs(); | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | auto operation = node->op() == Inc ? operationInc : operationDec; | 
|  | callOperation(operation, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), op1Regs); | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileValueNegate(Node* node) | 
|  | { | 
|  | // FIXME: add a fast path, at least for BigInt32, but probably also for HeapBigInt here. | 
|  | CodeBlock* baselineCodeBlock = m_jit.graph().baselineCodeBlockFor(node->origin.semantic); | 
|  | BytecodeIndex bytecodeIndex = node->origin.semantic.bytecodeIndex(); | 
|  | UnaryArithProfile* arithProfile = baselineCodeBlock->unaryArithProfileForBytecodeIndex(bytecodeIndex); | 
|  | JITNegIC* negIC = m_jit.codeBlock()->addJITNegIC(arithProfile); | 
|  | auto repatchingFunction = operationArithNegateOptimize; | 
|  | auto nonRepatchingFunction = operationArithNegate; | 
|  | bool needsScratchGPRReg = true; | 
|  | compileMathIC(node, negIC, needsScratchGPRReg, repatchingFunction, nonRepatchingFunction); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileArithNegate(Node* node) | 
|  | { | 
|  | switch (node->child1().useKind()) { | 
|  | case Int32Use: { | 
|  | SpeculateInt32Operand op1(this, node->child1()); | 
|  | GPRTemporary result(this); | 
|  |  | 
|  | m_jit.move(op1.gpr(), result.gpr()); | 
|  |  | 
|  | // 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 if (!shouldCheckNegativeZero(node->arithMode())) | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, m_jit.branchNeg32(MacroAssembler::Overflow, result.gpr())); | 
|  | else { | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, m_jit.branchTest32(MacroAssembler::Zero, result.gpr(), TrustedImm32(0x7fffffff))); | 
|  | m_jit.neg32(result.gpr()); | 
|  | } | 
|  |  | 
|  | strictInt32Result(result.gpr(), node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | case Int52RepUse: { | 
|  | ASSERT(shouldCheckOverflow(node->arithMode())); | 
|  |  | 
|  | if (!m_state.forNode(node->child1()).couldBeType(SpecNonInt32AsInt52)) { | 
|  | 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(), nullptr, | 
|  | 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(), nullptr, | 
|  | m_jit.branchNeg64(MacroAssembler::Overflow, resultGPR)); | 
|  | if (shouldCheckNegativeZero(node->arithMode())) { | 
|  | speculationCheck( | 
|  | NegativeZero, JSValueRegs(), nullptr, | 
|  | m_jit.branchTest64(MacroAssembler::Zero, resultGPR)); | 
|  | } | 
|  | int52Result(resultGPR, node); | 
|  | return; | 
|  | } | 
|  | #endif // USE(JSVALUE64) | 
|  |  | 
|  | case DoubleRepUse: { | 
|  | SpeculateDoubleOperand op1(this, node->child1()); | 
|  | FPRTemporary result(this); | 
|  |  | 
|  | m_jit.negateDouble(op1.fpr(), result.fpr()); | 
|  |  | 
|  | doubleResult(result.fpr(), node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | default: { | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | template <typename Generator, typename RepatchingFunction, typename NonRepatchingFunction> | 
|  | void SpeculativeJIT::compileMathIC(Node* node, JITUnaryMathIC<Generator>* mathIC, bool needsScratchGPRReg, RepatchingFunction repatchingFunction, NonRepatchingFunction nonRepatchingFunction) | 
|  | { | 
|  | GPRReg scratchGPR = InvalidGPRReg; | 
|  | Optional<GPRTemporary> gprScratch; | 
|  | if (needsScratchGPRReg) { | 
|  | gprScratch.emplace(this); | 
|  | scratchGPR = gprScratch->gpr(); | 
|  | } | 
|  | JSValueOperand childOperand(this, node->child1()); | 
|  | JSValueRegs childRegs = childOperand.jsValueRegs(); | 
|  | #if USE(JSVALUE64) | 
|  | GPRTemporary result(this, Reuse, childOperand); | 
|  | JSValueRegs resultRegs(result.gpr()); | 
|  | #else | 
|  | GPRTemporary resultTag(this); | 
|  | GPRTemporary resultPayload(this); | 
|  | JSValueRegs resultRegs(resultPayload.gpr(), resultTag.gpr()); | 
|  | #endif | 
|  |  | 
|  | #if ENABLE(MATH_IC_STATS) | 
|  | auto inlineStart = m_jit.label(); | 
|  | #endif | 
|  |  | 
|  | Box<MathICGenerationState> icGenerationState = Box<MathICGenerationState>::create(); | 
|  | mathIC->m_generator = Generator(resultRegs, childRegs, scratchGPR); | 
|  |  | 
|  | bool shouldEmitProfiling = false; | 
|  | bool generatedInline = mathIC->generateInline(m_jit, *icGenerationState, shouldEmitProfiling); | 
|  | if (generatedInline) { | 
|  | ASSERT(!icGenerationState->slowPathJumps.empty()); | 
|  |  | 
|  | Vector<SilentRegisterSavePlan> savePlans; | 
|  | silentSpillAllRegistersImpl(false, savePlans, resultRegs); | 
|  |  | 
|  | auto done = m_jit.label(); | 
|  |  | 
|  | addSlowPathGeneratorLambda([=, savePlans = WTFMove(savePlans)] () { | 
|  | icGenerationState->slowPathJumps.link(&m_jit); | 
|  | icGenerationState->slowPathStart = m_jit.label(); | 
|  | #if ENABLE(MATH_IC_STATS) | 
|  | auto slowPathStart = m_jit.label(); | 
|  | #endif | 
|  |  | 
|  | silentSpill(savePlans); | 
|  |  | 
|  | if (icGenerationState->shouldSlowPathRepatch) | 
|  | icGenerationState->slowPathCall = callOperation(bitwise_cast<J_JITOperation_GJMic>(repatchingFunction), resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), childRegs, TrustedImmPtr(mathIC)); | 
|  | else | 
|  | icGenerationState->slowPathCall = callOperation(nonRepatchingFunction, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), childRegs); | 
|  |  | 
|  | silentFill(savePlans); | 
|  | m_jit.exceptionCheck(); | 
|  | m_jit.jump().linkTo(done, &m_jit); | 
|  |  | 
|  | m_jit.addLinkTask([=] (LinkBuffer& linkBuffer) { | 
|  | mathIC->finalizeInlineCode(*icGenerationState, linkBuffer); | 
|  | }); | 
|  |  | 
|  | #if ENABLE(MATH_IC_STATS) | 
|  | auto slowPathEnd = m_jit.label(); | 
|  | m_jit.addLinkTask([=] (LinkBuffer& linkBuffer) { | 
|  | size_t size = static_cast<char*>(linkBuffer.locationOf(slowPathEnd).executableAddress()) - static_cast<char*>(linkBuffer.locationOf(slowPathStart).executableAddress()); | 
|  | mathIC->m_generatedCodeSize += size; | 
|  | }); | 
|  | #endif | 
|  |  | 
|  | }); | 
|  | } else { | 
|  | flushRegisters(); | 
|  | callOperation(nonRepatchingFunction, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), childRegs); | 
|  | m_jit.exceptionCheck(); | 
|  | } | 
|  |  | 
|  | #if ENABLE(MATH_IC_STATS) | 
|  | auto inlineEnd = m_jit.label(); | 
|  | m_jit.addLinkTask([=] (LinkBuffer& linkBuffer) { | 
|  | size_t size = static_cast<char*>(linkBuffer.locationOf(inlineEnd).executableAddress()) - static_cast<char*>(linkBuffer.locationOf(inlineStart).executableAddress()); | 
|  | mathIC->m_generatedCodeSize += size; | 
|  | }); | 
|  | #endif | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileValueMul(Node* node) | 
|  | { | 
|  | Edge& leftChild = node->child1(); | 
|  | Edge& rightChild = node->child2(); | 
|  |  | 
|  | #if USE(BIGINT32) | 
|  | // FIXME: Introduce another BigInt32 code generation: binary use kinds are BigIntUse32, but result is SpecAnyInt and accepting overflow. | 
|  | // Let's distinguish these modes based on result type information by introducing NodeResultBigInt32. | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=210957 | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=211040 | 
|  | if (node->binaryUseKind() == BigInt32Use) { | 
|  | // FIXME: the code between compileValueAdd, compileValueSub and compileValueMul for BigInt32 is nearly identical, so try to get rid of the duplication. | 
|  | SpeculateBigInt32Operand left(this, node->child1()); | 
|  | SpeculateBigInt32Operand right(this, node->child2()); | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary temp(this); | 
|  |  | 
|  | GPRReg leftGPR = left.gpr(); | 
|  | GPRReg rightGPR = right.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  |  | 
|  | m_jit.unboxBigInt32(leftGPR, resultGPR); | 
|  | m_jit.unboxBigInt32(rightGPR, tempGPR); | 
|  |  | 
|  | MacroAssembler::Jump check = m_jit.branchMul32(MacroAssembler::Overflow, resultGPR, tempGPR, resultGPR); | 
|  |  | 
|  | speculationCheck(BigInt32Overflow, JSValueRegs(), nullptr, check); | 
|  |  | 
|  | m_jit.boxBigInt32(resultGPR); | 
|  | jsValueResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  | // FIXME: add support for mixed BigInt32/HeapBigInt | 
|  | #endif | 
|  |  | 
|  | if (leftChild.useKind() == HeapBigIntUse && rightChild.useKind() == HeapBigIntUse) { | 
|  | SpeculateCellOperand left(this, leftChild); | 
|  | SpeculateCellOperand right(this, rightChild); | 
|  | GPRReg leftGPR = left.gpr(); | 
|  | GPRReg rightGPR = right.gpr(); | 
|  |  | 
|  | speculateHeapBigInt(leftChild, leftGPR); | 
|  | speculateHeapBigInt(rightChild, rightGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | callOperation(operationMulHeapBigInt, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR); | 
|  |  | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node()) || node->isBinaryUseKind(AnyBigIntUse)) { | 
|  | JSValueOperand left(this, leftChild, ManualOperandSpeculation); | 
|  | JSValueOperand right(this, rightChild, ManualOperandSpeculation); | 
|  | speculate(node, leftChild); | 
|  | speculate(node, rightChild); | 
|  | JSValueRegs leftRegs = left.jsValueRegs(); | 
|  | JSValueRegs rightRegs = right.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationValueMul, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftRegs, rightRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | bool needsScratchGPRReg = true; | 
|  | #if USE(JSVALUE64) | 
|  | bool needsScratchFPRReg = false; | 
|  | #else | 
|  | bool needsScratchFPRReg = true; | 
|  | #endif | 
|  |  | 
|  | CodeBlock* baselineCodeBlock = m_jit.graph().baselineCodeBlockFor(node->origin.semantic); | 
|  | BytecodeIndex bytecodeIndex = node->origin.semantic.bytecodeIndex(); | 
|  | BinaryArithProfile* arithProfile = baselineCodeBlock->binaryArithProfileForBytecodeIndex(bytecodeIndex); | 
|  | JITMulIC* mulIC = m_jit.codeBlock()->addJITMulIC(arithProfile); | 
|  | auto repatchingFunction = operationValueMulOptimize; | 
|  | auto nonRepatchingFunction = operationValueMul; | 
|  |  | 
|  | compileMathIC(node, mulIC, needsScratchGPRReg, needsScratchFPRReg, repatchingFunction, nonRepatchingFunction); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileArithMul(Node* node) | 
|  | { | 
|  | switch (node->binaryUseKind()) { | 
|  | case Int32Use: { | 
|  | 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(), nullptr, | 
|  | 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(), nullptr, m_jit.branchTest32(MacroAssembler::Signed, op1GPR)); | 
|  | else if (imm < 0) { | 
|  | if (shouldCheckOverflow(node->arithMode())) | 
|  | speculationCheck(NegativeZero, JSValueRegs(), nullptr, m_jit.branchTest32(MacroAssembler::Zero, resultGPR)); | 
|  | else | 
|  | speculationCheck(NegativeZero, JSValueRegs(), nullptr, m_jit.branchTest32(MacroAssembler::Zero, op1GPR)); | 
|  | } | 
|  | } | 
|  |  | 
|  | strictInt32Result(resultGPR, node); | 
|  | return; | 
|  | } | 
|  | SpeculateInt32Operand op1(this, node->child1()); | 
|  | SpeculateInt32Operand op2(this, node->child2()); | 
|  | GPRTemporary result(this); | 
|  |  | 
|  | GPRReg reg1 = op1.gpr(); | 
|  | GPRReg reg2 = op2.gpr(); | 
|  |  | 
|  | // 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 (!shouldCheckOverflow(node->arithMode())) | 
|  | m_jit.mul32(reg1, reg2, result.gpr()); | 
|  | else { | 
|  | speculationCheck( | 
|  | Overflow, JSValueRegs(), nullptr, | 
|  | m_jit.branchMul32(MacroAssembler::Overflow, reg1, reg2, result.gpr())); | 
|  | } | 
|  |  | 
|  | // Check for negative zero, if the users of this node care about such things. | 
|  | if (shouldCheckNegativeZero(node->arithMode())) { | 
|  | MacroAssembler::Jump resultNonZero = m_jit.branchTest32(MacroAssembler::NonZero, result.gpr()); | 
|  | speculationCheck(NegativeZero, JSValueRegs(), nullptr, m_jit.branchTest32(MacroAssembler::Signed, reg1)); | 
|  | speculationCheck(NegativeZero, JSValueRegs(), nullptr, m_jit.branchTest32(MacroAssembler::Signed, reg2)); | 
|  | resultNonZero.link(&m_jit); | 
|  | } | 
|  |  | 
|  | strictInt32Result(result.gpr(), node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | case Int52RepUse: { | 
|  | ASSERT(shouldCheckOverflow(node->arithMode())); | 
|  |  | 
|  | // 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(), nullptr, | 
|  | m_jit.branchMul64(MacroAssembler::Overflow, op2GPR, resultGPR)); | 
|  |  | 
|  | if (shouldCheckNegativeZero(node->arithMode())) { | 
|  | MacroAssembler::Jump resultNonZero = m_jit.branchTest64( | 
|  | MacroAssembler::NonZero, resultGPR); | 
|  | speculationCheck( | 
|  | NegativeZero, JSValueRegs(), nullptr, | 
|  | m_jit.branch64(MacroAssembler::LessThan, op1GPR, TrustedImm32(0))); | 
|  | speculationCheck( | 
|  | NegativeZero, JSValueRegs(), nullptr, | 
|  | m_jit.branch64(MacroAssembler::LessThan, op2GPR, TrustedImm32(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); | 
|  |  | 
|  | FPRReg reg1 = op1.fpr(); | 
|  | FPRReg reg2 = op2.fpr(); | 
|  |  | 
|  | m_jit.mulDouble(reg1, reg2, result.fpr()); | 
|  |  | 
|  | doubleResult(result.fpr(), node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileValueDiv(Node* node) | 
|  | { | 
|  | Edge& leftChild = node->child1(); | 
|  | Edge& rightChild = node->child2(); | 
|  |  | 
|  | // FIXME: add a fast path for BigInt32. Currently we go through the slow path, because of how ugly the code for Div gets. | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=211041 | 
|  |  | 
|  | if (node->isBinaryUseKind(HeapBigIntUse)) { | 
|  | SpeculateCellOperand left(this, leftChild); | 
|  | SpeculateCellOperand right(this, rightChild); | 
|  | GPRReg leftGPR = left.gpr(); | 
|  | GPRReg rightGPR = right.gpr(); | 
|  |  | 
|  | speculateHeapBigInt(leftChild, leftGPR); | 
|  | speculateHeapBigInt(rightChild, rightGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | callOperation(operationDivHeapBigInt, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR); | 
|  |  | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node()) || node->isBinaryUseKind(AnyBigIntUse) || node->isBinaryUseKind(BigInt32Use)) { | 
|  | JSValueOperand left(this, leftChild, ManualOperandSpeculation); | 
|  | JSValueOperand right(this, rightChild, ManualOperandSpeculation); | 
|  | speculate(node, leftChild); | 
|  | speculate(node, rightChild); | 
|  | JSValueRegs leftRegs = left.jsValueRegs(); | 
|  | JSValueRegs rightRegs = right.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationValueDiv, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftRegs, rightRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | ASSERT(node->isBinaryUseKind(UntypedUse)); | 
|  |  | 
|  | Optional<JSValueOperand> left; | 
|  | Optional<JSValueOperand> right; | 
|  |  | 
|  | JSValueRegs leftRegs; | 
|  | JSValueRegs rightRegs; | 
|  |  | 
|  | FPRTemporary leftNumber(this); | 
|  | FPRTemporary rightNumber(this); | 
|  | FPRReg leftFPR = leftNumber.fpr(); | 
|  | FPRReg rightFPR = rightNumber.fpr(); | 
|  | FPRTemporary fprScratch(this); | 
|  | FPRReg scratchFPR = fprScratch.fpr(); | 
|  |  | 
|  | #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.emplace(this, leftChild); | 
|  | leftRegs = left->jsValueRegs(); | 
|  | } | 
|  | if (!rightOperand.isConst()) { | 
|  | right.emplace(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, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftRegs, rightRegs); | 
|  |  | 
|  | silentFillAllRegisters(); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | gen.endJumpList().link(&m_jit); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileArithDiv(Node* node) | 
|  | { | 
|  | switch (node->binaryUseKind()) { | 
|  | case Int32Use: { | 
|  | #if 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); | 
|  |  | 
|  | m_jit.add32(JITCompiler::TrustedImm32(1), op2GPR, temp); | 
|  |  | 
|  | JITCompiler::Jump safeDenominator = m_jit.branch32(JITCompiler::Above, temp, JITCompiler::TrustedImm32(1)); | 
|  |  | 
|  | JITCompiler::JumpList done; | 
|  | if (shouldCheckOverflow(node->arithMode())) { | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, m_jit.branchTest32(JITCompiler::Zero, op2GPR)); | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, 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()); | 
|  |  | 
|  | notZero.link(&m_jit); | 
|  | JITCompiler::Jump notNeg2ToThe31 = | 
|  | m_jit.branch32(JITCompiler::NotEqual, op1GPR, TrustedImm32(-2147483647-1)); | 
|  | m_jit.zeroExtend32ToWord(op1GPR, eax.gpr()); | 
|  | done.append(m_jit.jump()); | 
|  |  | 
|  | notNeg2ToThe31.link(&m_jit); | 
|  | } | 
|  |  | 
|  | safeDenominator.link(&m_jit); | 
|  |  | 
|  | // If the user cares about negative zero, then speculate that we're not about | 
|  | // to produce negative zero. | 
|  | if (shouldCheckNegativeZero(node->arithMode())) { | 
|  | MacroAssembler::Jump numeratorNonZero = m_jit.branchTest32(MacroAssembler::NonZero, op1GPR); | 
|  | speculationCheck(NegativeZero, JSValueRegs(), nullptr, m_jit.branch32(MacroAssembler::LessThan, op2GPR, TrustedImm32(0))); | 
|  | numeratorNonZero.link(&m_jit); | 
|  | } | 
|  |  | 
|  | if (op2TempGPR != InvalidGPRReg) { | 
|  | m_jit.move(op2GPR, op2TempGPR); | 
|  | op2GPR = op2TempGPR; | 
|  | } | 
|  |  | 
|  | m_jit.move(op1GPR, eax.gpr()); | 
|  | m_jit.x86ConvertToDoubleWord32(); | 
|  | m_jit.x86Div32(op2GPR); | 
|  |  | 
|  | if (op2TempGPR != InvalidGPRReg) | 
|  | unlock(op2TempGPR); | 
|  |  | 
|  | // Check that there was no remainder. If there had been, then we'd be obligated to | 
|  | // produce a double result instead. | 
|  | if (shouldCheckOverflow(node->arithMode())) | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, m_jit.branchTest32(JITCompiler::NonZero, edx.gpr())); | 
|  |  | 
|  | done.link(&m_jit); | 
|  | strictInt32Result(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 (shouldCheckOverflow(node->arithMode())) | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, m_jit.branchTest32(MacroAssembler::Zero, 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)); | 
|  | } | 
|  |  | 
|  | strictInt32Result(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; | 
|  | } | 
|  |  | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileArithFRound(Node* node) | 
|  | { | 
|  | if (node->child1().useKind() == DoubleRepUse) { | 
|  | SpeculateDoubleOperand op1(this, node->child1()); | 
|  | FPRTemporary result(this, op1); | 
|  | m_jit.convertDoubleToFloat(op1.fpr(), result.fpr()); | 
|  | m_jit.convertFloatToDouble(result.fpr(), result.fpr()); | 
|  | doubleResult(result.fpr(), node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | JSValueOperand op1(this, node->child1()); | 
|  | JSValueRegs op1Regs = op1.jsValueRegs(); | 
|  | flushRegisters(); | 
|  | FPRResult result(this); | 
|  | callOperation(operationArithFRound, result.fpr(), TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), op1Regs); | 
|  | m_jit.exceptionCheck(); | 
|  | doubleResult(result.fpr(), node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileValueMod(Node* node) | 
|  | { | 
|  | Edge& leftChild = node->child1(); | 
|  | Edge& rightChild = node->child2(); | 
|  |  | 
|  | // FIXME: add a fast path for BigInt32. Currently we go through the slow path, because of how ugly the code for Mod gets. | 
|  |  | 
|  | if (node->binaryUseKind() == HeapBigIntUse) { | 
|  | SpeculateCellOperand left(this, leftChild); | 
|  | SpeculateCellOperand right(this, rightChild); | 
|  | GPRReg leftGPR = left.gpr(); | 
|  | GPRReg rightGPR = right.gpr(); | 
|  |  | 
|  | speculateHeapBigInt(leftChild, leftGPR); | 
|  | speculateHeapBigInt(rightChild, rightGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | callOperation(operationModHeapBigInt, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR); | 
|  |  | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | DFG_ASSERT(m_jit.graph(), node, node->binaryUseKind() == UntypedUse || node->binaryUseKind() == AnyBigIntUse || node->binaryUseKind() == BigInt32Use, node->binaryUseKind()); | 
|  | JSValueOperand op1(this, leftChild, ManualOperandSpeculation); | 
|  | JSValueOperand op2(this, rightChild, ManualOperandSpeculation); | 
|  | speculate(node, leftChild); | 
|  | speculate(node, rightChild); | 
|  | JSValueRegs op1Regs = op1.jsValueRegs(); | 
|  | JSValueRegs op2Regs = op2.jsValueRegs(); | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationValueMod, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), op1Regs, op2Regs); | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileArithMod(Node* node) | 
|  | { | 
|  | switch (node->binaryUseKind()) { | 
|  | case Int32Use: { | 
|  | // 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(), nullptr, m_jit.branchTest32(JITCompiler::Zero, resultGPR)); | 
|  | numeratorPositive.link(&m_jit); | 
|  | } | 
|  |  | 
|  | strictInt32Result(resultGPR, node); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | #if 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(), nullptr, m_jit.branchTest32(JITCompiler::Zero, edx.gpr())); | 
|  | numeratorPositive.link(&m_jit); | 
|  | } | 
|  |  | 
|  | if (op1SaveGPR != op1Gpr) | 
|  | unlock(op1SaveGPR); | 
|  |  | 
|  | strictInt32Result(edx.gpr(), node); | 
|  | return; | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | SpeculateInt32Operand op2(this, node->child2()); | 
|  | #if 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(), nullptr, m_jit.branchTest32(JITCompiler::Zero, op2GPR)); | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, 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(), nullptr, m_jit.branchTest32(JITCompiler::Zero, edx.gpr())); | 
|  | numeratorPositive.link(&m_jit); | 
|  | } | 
|  |  | 
|  | if (op1SaveGPR != op1GPR) | 
|  | unlock(op1SaveGPR); | 
|  |  | 
|  | done.link(&m_jit); | 
|  | strictInt32Result(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); | 
|  | // We know that the low 32-bit of divisorGPR is 0, but we don't know if the high bits are. | 
|  | // So, use TrustedImm32(0) on ARM instead because done expects the result to be in DataFormatInt32. | 
|  | // Using an immediate 0 doesn't cost anything extra on ARM. | 
|  | m_jit.move(TrustedImm32(0), 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); | 
|  |  | 
|  | strictInt32Result(quotientThenRemainderGPR, node); | 
|  | #else // not architecture that can do integer division | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | #endif | 
|  | return; | 
|  | } | 
|  |  | 
|  | case DoubleRepUse: { | 
|  | SpeculateDoubleOperand op1(this, node->child1()); | 
|  | SpeculateDoubleOperand op2(this, node->child2()); | 
|  |  | 
|  | FPRReg op1FPR = op1.fpr(); | 
|  | FPRReg op2FPR = op2.fpr(); | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | FPRResult result(this); | 
|  |  | 
|  | using OperationType = D_JITOperation_DD; | 
|  | callOperation<OperationType>(jsMod, result.fpr(), op1FPR, op2FPR); | 
|  |  | 
|  | doubleResult(result.fpr(), node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileArithRounding(Node* node) | 
|  | { | 
|  | if (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); | 
|  |  | 
|  | strictInt32Result(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 constexpr double halfConstant = 0.5; | 
|  | m_jit.loadDouble(TrustedImmPtr(&halfConstant), resultFPR); | 
|  | m_jit.addDouble(valueFPR, resultFPR); | 
|  | m_jit.floorDouble(resultFPR, resultFPR); | 
|  | } else { | 
|  | m_jit.ceilDouble(valueFPR, resultFPR); | 
|  |  | 
|  | FPRTemporary scratch(this); | 
|  | FPRReg scratchFPR = scratch.fpr(); | 
|  | static constexpr double halfConstant = -0.5; | 
|  | m_jit.loadDouble(TrustedImmPtr(&halfConstant), scratchFPR); | 
|  | m_jit.addDouble(resultFPR, scratchFPR); | 
|  |  | 
|  | JITCompiler::Jump shouldUseCeiled = m_jit.branchDouble(JITCompiler::DoubleLessThanOrEqualAndOrdered, scratchFPR, valueFPR); | 
|  | static constexpr double oneConstant = -1.0; | 
|  | m_jit.loadDouble(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; | 
|  | } | 
|  |  | 
|  | case ArithTrunc: { | 
|  | FPRTemporary rounded(this); | 
|  | FPRReg resultFPR = rounded.fpr(); | 
|  | m_jit.roundTowardZeroDouble(valueFPR, resultFPR); | 
|  | setResult(resultFPR); | 
|  | return; | 
|  | } | 
|  |  | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  | } else { | 
|  | flushRegisters(); | 
|  | FPRResult roundedResultAsDouble(this); | 
|  | FPRReg resultFPR = roundedResultAsDouble.fpr(); | 
|  | using OperationType = D_JITOperation_D; | 
|  | if (node->op() == ArithRound) | 
|  | callOperation<OperationType>(jsRound, resultFPR, valueFPR); | 
|  | else if (node->op() == ArithFloor) | 
|  | callOperation<OperationType>(floor, resultFPR, valueFPR); | 
|  | else if (node->op() == ArithCeil) | 
|  | callOperation<OperationType>(ceil, resultFPR, valueFPR); | 
|  | else { | 
|  | ASSERT(node->op() == ArithTrunc); | 
|  | callOperation<OperationType>(trunc, resultFPR, valueFPR); | 
|  | } | 
|  | setResult(resultFPR); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | DFG_ASSERT(m_jit.graph(), node, node->child1().useKind() == UntypedUse, node->child1().useKind()); | 
|  |  | 
|  | JSValueOperand argument(this, node->child1()); | 
|  | JSValueRegs argumentRegs = argument.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | J_JITOperation_GJ operation = nullptr; | 
|  | if (node->op() == ArithRound) | 
|  | operation = operationArithRound; | 
|  | else if (node->op() == ArithFloor) | 
|  | operation = operationArithFloor; | 
|  | else if (node->op() == ArithCeil) | 
|  | operation = operationArithCeil; | 
|  | else { | 
|  | ASSERT(node->op() == ArithTrunc); | 
|  | operation = operationArithTrunc; | 
|  | } | 
|  | callOperation(operation, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), argumentRegs); | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileArithUnary(Node* node) | 
|  | { | 
|  | compileArithDoubleUnaryOp(node, arithUnaryFunction(node->arithUnaryType()), arithUnaryOperation(node->arithUnaryType())); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileArithSqrt(Node* node) | 
|  | { | 
|  | if (node->child1().useKind() == DoubleRepUse) { | 
|  | SpeculateDoubleOperand op1(this, node->child1()); | 
|  | FPRReg op1FPR = op1.fpr(); | 
|  |  | 
|  | if (!MacroAssembler::supportsFloatingPointSqrt() || !Options::useArchitectureSpecificOptimizations()) { | 
|  | flushRegisters(); | 
|  | FPRResult result(this); | 
|  | callOperation<D_JITOperation_D>(sqrt, result.fpr(), op1FPR); | 
|  | doubleResult(result.fpr(), node); | 
|  | } else { | 
|  | FPRTemporary result(this, op1); | 
|  | m_jit.sqrtDouble(op1.fpr(), result.fpr()); | 
|  | doubleResult(result.fpr(), node); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | JSValueOperand op1(this, node->child1()); | 
|  | JSValueRegs op1Regs = op1.jsValueRegs(); | 
|  | flushRegisters(); | 
|  | FPRResult result(this); | 
|  | callOperation(operationArithSqrt, result.fpr(), TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), op1Regs); | 
|  | m_jit.exceptionCheck(); | 
|  | doubleResult(result.fpr(), node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileArithMinMax(Node* node) | 
|  | { | 
|  | switch (node->binaryUseKind()) { | 
|  | case Int32Use: { | 
|  | SpeculateStrictInt32Operand op1(this, node->child1()); | 
|  | SpeculateStrictInt32Operand op2(this, node->child2()); | 
|  | GPRTemporary result(this, Reuse, op1); | 
|  |  | 
|  | GPRReg op1GPR = op1.gpr(); | 
|  | GPRReg op2GPR = op2.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | MacroAssembler::Jump op1Less = m_jit.branch32(node->op() == ArithMin ? MacroAssembler::LessThan : MacroAssembler::GreaterThan, op1GPR, op2GPR); | 
|  | m_jit.move(op2GPR, resultGPR); | 
|  | if (op1GPR != resultGPR) { | 
|  | MacroAssembler::Jump done = m_jit.jump(); | 
|  | op1Less.link(&m_jit); | 
|  | m_jit.move(op1GPR, resultGPR); | 
|  | done.link(&m_jit); | 
|  | } else | 
|  | op1Less.link(&m_jit); | 
|  |  | 
|  | strictInt32Result(resultGPR, node); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case DoubleRepUse: { | 
|  | SpeculateDoubleOperand op1(this, node->child1()); | 
|  | SpeculateDoubleOperand op2(this, node->child2()); | 
|  | FPRTemporary result(this, op1); | 
|  |  | 
|  | FPRReg op1FPR = op1.fpr(); | 
|  | FPRReg op2FPR = op2.fpr(); | 
|  | FPRReg resultFPR = result.fpr(); | 
|  |  | 
|  | MacroAssembler::JumpList done; | 
|  |  | 
|  | MacroAssembler::Jump op1Less = m_jit.branchDouble(node->op() == ArithMin ? MacroAssembler::DoubleLessThanAndOrdered : MacroAssembler::DoubleGreaterThanAndOrdered, op1FPR, op2FPR); | 
|  | MacroAssembler::Jump opNotEqualOrUnordered = m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, op1FPR, op2FPR); | 
|  |  | 
|  | // The spec for Math.min and Math.max states that +0 is considered to be larger than -0. | 
|  | if (node->op() == ArithMin) | 
|  | m_jit.orDouble(op1FPR, op2FPR, resultFPR); | 
|  | else | 
|  | m_jit.andDouble(op1FPR, op2FPR, resultFPR); | 
|  |  | 
|  | done.append(m_jit.jump()); | 
|  |  | 
|  | opNotEqualOrUnordered.link(&m_jit); | 
|  | // op2 is either the lesser one or one of then is NaN | 
|  | MacroAssembler::Jump op2Less = m_jit.branchDouble(node->op() == ArithMin ? MacroAssembler::DoubleGreaterThanAndOrdered : MacroAssembler::DoubleLessThanAndOrdered, op1FPR, op2FPR); | 
|  |  | 
|  | // Unordered case. We don't know which of op1, op2 is NaN. Manufacture NaN by adding | 
|  | // op1 + op2 and putting it into result. | 
|  | m_jit.addDouble(op1FPR, op2FPR, resultFPR); | 
|  | done.append(m_jit.jump()); | 
|  |  | 
|  | op2Less.link(&m_jit); | 
|  | m_jit.moveDouble(op2FPR, resultFPR); | 
|  |  | 
|  | if (op1FPR != resultFPR) { | 
|  | done.append(m_jit.jump()); | 
|  |  | 
|  | op1Less.link(&m_jit); | 
|  | m_jit.moveDouble(op1FPR, resultFPR); | 
|  | } else | 
|  | op1Less.link(&m_jit); | 
|  |  | 
|  | done.link(&m_jit); | 
|  |  | 
|  | doubleResult(resultFPR, node); | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: | 
|  | DFG_CRASH(m_jit.graph(), node, "Bad use kind"); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // 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(maxExponentForIntegerMathPow))); | 
|  |  | 
|  | static constexpr 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::compileValuePow(Node* node) | 
|  | { | 
|  | Edge& leftChild = node->child1(); | 
|  | Edge& rightChild = node->child2(); | 
|  |  | 
|  | // FIXME: do we want a fast path for BigInt32 for Pow? I expect it would overflow pretty often. | 
|  | if (node->binaryUseKind() == HeapBigIntUse) { | 
|  | SpeculateCellOperand left(this, leftChild); | 
|  | SpeculateCellOperand right(this, rightChild); | 
|  | GPRReg leftGPR = left.gpr(); | 
|  | GPRReg rightGPR = right.gpr(); | 
|  |  | 
|  | speculateHeapBigInt(leftChild, leftGPR); | 
|  | speculateHeapBigInt(rightChild, rightGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | callOperation(operationPowHeapBigInt, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR); | 
|  |  | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | DFG_ASSERT(m_jit.graph(), node, node->binaryUseKind() == UntypedUse || node->binaryUseKind() == AnyBigIntUse || node->binaryUseKind() == BigInt32Use, node->binaryUseKind()); | 
|  |  | 
|  | JSValueOperand left(this, leftChild, ManualOperandSpeculation); | 
|  | JSValueOperand right(this, rightChild, ManualOperandSpeculation); | 
|  | speculate(node, leftChild); | 
|  | speculate(node, rightChild); | 
|  | JSValueRegs leftRegs = left.jsValueRegs(); | 
|  | JSValueRegs rightRegs = right.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationValuePow, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftRegs, rightRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | if (node->child2()->isDoubleConstant()) { | 
|  | double exponent = node->child2()->asNumber(); | 
|  | static constexpr double infinityConstant = std::numeric_limits<double>::infinity(); | 
|  | static constexpr double minusInfinityConstant = -std::numeric_limits<double>::infinity(); | 
|  | if (exponent == 0.5) { | 
|  | SpeculateDoubleOperand xOperand(this, node->child1()); | 
|  | FPRTemporary result(this); | 
|  | FPRReg xOperandFpr = xOperand.fpr(); | 
|  | FPRReg resultFpr = result.fpr(); | 
|  |  | 
|  | m_jit.moveZeroToDouble(resultFpr); | 
|  | MacroAssembler::Jump xIsZeroOrNegativeZero = m_jit.branchDouble(MacroAssembler::DoubleEqualAndOrdered, xOperandFpr, resultFpr); | 
|  |  | 
|  | m_jit.loadDouble(TrustedImmPtr(&minusInfinityConstant), resultFpr); | 
|  | MacroAssembler::Jump xIsMinusInfinity = m_jit.branchDouble(MacroAssembler::DoubleEqualAndOrdered, xOperandFpr, resultFpr); | 
|  | m_jit.sqrtDouble(xOperandFpr, resultFpr); | 
|  | MacroAssembler::Jump doneWithSqrt = m_jit.jump(); | 
|  |  | 
|  | xIsMinusInfinity.link(&m_jit); | 
|  | if (isX86()) | 
|  | m_jit.loadDouble(TrustedImmPtr(&infinityConstant), resultFpr); | 
|  | else | 
|  | m_jit.absDouble(resultFpr, resultFpr); | 
|  |  | 
|  | xIsZeroOrNegativeZero.link(&m_jit); | 
|  | doneWithSqrt.link(&m_jit); | 
|  | doubleResult(resultFpr, node); | 
|  | return; | 
|  | } | 
|  | if (exponent == -0.5) { | 
|  | SpeculateDoubleOperand xOperand(this, node->child1()); | 
|  | FPRTemporary scratch(this); | 
|  | FPRTemporary result(this); | 
|  | FPRReg xOperandFpr = xOperand.fpr(); | 
|  | FPRReg scratchFPR = scratch.fpr(); | 
|  | FPRReg resultFpr = result.fpr(); | 
|  |  | 
|  | m_jit.moveZeroToDouble(resultFpr); | 
|  | MacroAssembler::Jump xIsZeroOrNegativeZero = m_jit.branchDouble(MacroAssembler::DoubleEqualAndOrdered, xOperandFpr, resultFpr); | 
|  |  | 
|  | m_jit.loadDouble(TrustedImmPtr(&minusInfinityConstant), resultFpr); | 
|  | MacroAssembler::Jump xIsMinusInfinity = m_jit.branchDouble(MacroAssembler::DoubleEqualAndOrdered, xOperandFpr, resultFpr); | 
|  |  | 
|  | static constexpr double oneConstant = 1.; | 
|  | m_jit.loadDouble(TrustedImmPtr(&oneConstant), resultFpr); | 
|  | m_jit.sqrtDouble(xOperandFpr, scratchFPR); | 
|  | m_jit.divDouble(resultFpr, scratchFPR, resultFpr); | 
|  | MacroAssembler::Jump doneWithSqrt = m_jit.jump(); | 
|  |  | 
|  | xIsZeroOrNegativeZero.link(&m_jit); | 
|  | m_jit.loadDouble(TrustedImmPtr(&infinityConstant), resultFpr); | 
|  | MacroAssembler::Jump doneWithBaseZero = m_jit.jump(); | 
|  |  | 
|  | xIsMinusInfinity.link(&m_jit); | 
|  | m_jit.moveZeroToDouble(resultFpr); | 
|  |  | 
|  | doneWithBaseZero.link(&m_jit); | 
|  | doneWithSqrt.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); | 
|  | } | 
|  |  | 
|  | // Returns true if the compare is fused with a subsequent branch. | 
|  | bool SpeculativeJIT::compare(Node* node, MacroAssembler::RelationalCondition condition, MacroAssembler::DoubleCondition doubleCondition, S_JITOperation_GJJ operation) | 
|  | { | 
|  | if (compilePeepHoleBranch(node, condition, doubleCondition, operation)) | 
|  | return true; | 
|  |  | 
|  | if (node->isBinaryUseKind(Int32Use)) { | 
|  | compileInt32Compare(node, condition); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | #if USE(BIGINT32) | 
|  | if (node->isBinaryUseKind(BigInt32Use)) { | 
|  | compileBigInt32Compare(node, condition); | 
|  | return false; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | if (node->isBinaryUseKind(Int52RepUse)) { | 
|  | compileInt52Compare(node, condition); | 
|  | return false; | 
|  | } | 
|  | #endif // USE(JSVALUE64) | 
|  |  | 
|  | if (node->isBinaryUseKind(DoubleRepUse)) { | 
|  | compileDoubleCompare(node, doubleCondition); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (node->isBinaryUseKind(StringUse)) { | 
|  | if (node->op() == CompareEq) | 
|  | compileStringEquality(node); | 
|  | else | 
|  | compileStringCompare(node, condition); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (node->isBinaryUseKind(StringIdentUse)) { | 
|  | if (node->op() == CompareEq) | 
|  | compileStringIdentEquality(node); | 
|  | else | 
|  | compileStringIdentCompare(node, condition); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // FIXME: add HeapBigInt case here. | 
|  | // Not having it means that the compare will not be fused with the branch for this case. | 
|  |  | 
|  | if (node->op() == CompareEq) { | 
|  | if (node->isBinaryUseKind(BooleanUse)) { | 
|  | compileBooleanCompare(node, condition); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (node->isBinaryUseKind(SymbolUse)) { | 
|  | compileSymbolEquality(node); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (node->isBinaryUseKind(ObjectUse)) { | 
|  | compileObjectEquality(node); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (node->isBinaryUseKind(ObjectUse, ObjectOrOtherUse)) { | 
|  | compileObjectToObjectOrOtherEquality(node->child1(), node->child2()); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (node->isBinaryUseKind(ObjectOrOtherUse, ObjectUse)) { | 
|  | compileObjectToObjectOrOtherEquality(node->child2(), node->child1()); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (!needsTypeCheck(node->child1(), SpecOther)) { | 
|  | nonSpeculativeNonPeepholeCompareNullOrUndefined(node->child2()); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (!needsTypeCheck(node->child2(), SpecOther)) { | 
|  | nonSpeculativeNonPeepholeCompareNullOrUndefined(node->child1()); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | genericJSValueNonPeepholeCompare(node, condition, operation); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCompareUnsigned(Node* node, MacroAssembler::RelationalCondition condition) | 
|  | { | 
|  | compileInt32Compare(node, condition); | 
|  | } | 
|  |  | 
|  | bool SpeculativeJIT::compileStrictEq(Node* node) | 
|  | { | 
|  | if (node->isBinaryUseKind(BooleanUse)) { | 
|  | unsigned branchIndexInBlock = detectPeepHoleBranch(); | 
|  | if (branchIndexInBlock != UINT_MAX) { | 
|  | Node* branchNode = m_block->at(branchIndexInBlock); | 
|  | compilePeepHoleBooleanBranch(node, branchNode, MacroAssembler::Equal); | 
|  | use(node->child1()); | 
|  | use(node->child2()); | 
|  | m_indexInBlock = branchIndexInBlock; | 
|  | m_currentNode = branchNode; | 
|  | return true; | 
|  | } | 
|  | compileBooleanCompare(node, MacroAssembler::Equal); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (node->isBinaryUseKind(Int32Use)) { | 
|  | unsigned branchIndexInBlock = detectPeepHoleBranch(); | 
|  | if (branchIndexInBlock != UINT_MAX) { | 
|  | 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; | 
|  | } | 
|  | compileInt32Compare(node, MacroAssembler::Equal); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | #if USE(BIGINT32) | 
|  | if (node->isBinaryUseKind(BigInt32Use)) { | 
|  | unsigned branchIndexInBlock = detectPeepHoleBranch(); | 
|  | if (branchIndexInBlock != UINT_MAX) { | 
|  | Node* branchNode = m_block->at(branchIndexInBlock); | 
|  | compilePeepHoleBigInt32Branch(node, branchNode, MacroAssembler::Equal); | 
|  | use(node->child1()); | 
|  | use(node->child2()); | 
|  | m_indexInBlock = branchIndexInBlock; | 
|  | m_currentNode = branchNode; | 
|  | return true; | 
|  | } | 
|  | compileBigInt32Compare(node, MacroAssembler::Equal); | 
|  | return false; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #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_block->at(branchIndexInBlock); | 
|  | compilePeepHoleDoubleBranch(node, branchNode, MacroAssembler::DoubleEqualAndOrdered); | 
|  | use(node->child1()); | 
|  | use(node->child2()); | 
|  | m_indexInBlock = branchIndexInBlock; | 
|  | m_currentNode = branchNode; | 
|  | return true; | 
|  | } | 
|  | compileDoubleCompare(node, MacroAssembler::DoubleEqualAndOrdered); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | 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(HeapBigIntUse)) { | 
|  | compileHeapBigIntEquality(node); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (node->isBinaryUseKind(SymbolUse, UntypedUse)) { | 
|  | compileSymbolUntypedEquality(node, node->child1(), node->child2()); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (node->isBinaryUseKind(UntypedUse, SymbolUse)) { | 
|  | compileSymbolUntypedEquality(node, node->child2(), node->child1()); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (node->isBinaryUseKind(StringUse)) { | 
|  | compileStringEquality(node); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (node->isBinaryUseKind(StringIdentUse)) { | 
|  | compileStringIdentEquality(node); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (node->isBinaryUseKind(ObjectUse, UntypedUse)) { | 
|  | unsigned branchIndexInBlock = detectPeepHoleBranch(); | 
|  | if (branchIndexInBlock != UINT_MAX) { | 
|  | 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()); | 
|  | m_indexInBlock = branchIndexInBlock; | 
|  | m_currentNode = branchNode; | 
|  | return true; | 
|  | } | 
|  | compileObjectEquality(node); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (node->isBinaryUseKind(MiscUse, UntypedUse) | 
|  | || node->isBinaryUseKind(UntypedUse, MiscUse)) { | 
|  | compileMiscStrictEq(node); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | ASSERT(node->isBinaryUseKind(UntypedUse) || node->isBinaryUseKind(AnyBigIntUse)); | 
|  | return genericJSValueStrictEq(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileBooleanCompare(Node* node, MacroAssembler::RelationalCondition condition) | 
|  | { | 
|  | SpeculateBooleanOperand op1(this, node->child1()); | 
|  | SpeculateBooleanOperand op2(this, node->child2()); | 
|  | GPRTemporary result(this); | 
|  |  | 
|  | m_jit.compare32(condition, op1.gpr(), op2.gpr(), result.gpr()); | 
|  |  | 
|  | unblessedBooleanResult(result.gpr(), node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileInt32Compare(Node* node, MacroAssembler::RelationalCondition condition) | 
|  | { | 
|  | if (node->child1()->isInt32Constant()) { | 
|  | SpeculateInt32Operand op2(this, node->child2()); | 
|  | GPRTemporary result(this, Reuse, op2); | 
|  | int32_t imm = node->child1()->asInt32(); | 
|  | m_jit.compare32(condition, JITCompiler::Imm32(imm), op2.gpr(), result.gpr()); | 
|  |  | 
|  | unblessedBooleanResult(result.gpr(), node); | 
|  | } else if (node->child2()->isInt32Constant()) { | 
|  | SpeculateInt32Operand op1(this, node->child1()); | 
|  | GPRTemporary result(this, Reuse, op1); | 
|  | int32_t imm = node->child2()->asInt32(); | 
|  | m_jit.compare32(condition, op1.gpr(), JITCompiler::Imm32(imm), result.gpr()); | 
|  |  | 
|  | unblessedBooleanResult(result.gpr(), node); | 
|  | } else { | 
|  | SpeculateInt32Operand op1(this, node->child1()); | 
|  | SpeculateInt32Operand op2(this, node->child2()); | 
|  | GPRTemporary result(this, Reuse, op1, op2); | 
|  | m_jit.compare32(condition, op1.gpr(), op2.gpr(), result.gpr()); | 
|  |  | 
|  | unblessedBooleanResult(result.gpr(), node); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileDoubleCompare(Node* node, MacroAssembler::DoubleCondition condition) | 
|  | { | 
|  | SpeculateDoubleOperand op1(this, node->child1()); | 
|  | SpeculateDoubleOperand op2(this, node->child2()); | 
|  | GPRTemporary result(this); | 
|  |  | 
|  | FPRReg op1FPR = op1.fpr(); | 
|  | FPRReg op2FPR = op2.fpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | m_jit.compareDouble(condition, op1FPR, op2FPR, resultGPR); | 
|  |  | 
|  | unblessedBooleanResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileObjectEquality(Node* node) | 
|  | { | 
|  | SpeculateCellOperand op1(this, node->child1()); | 
|  | SpeculateCellOperand op2(this, node->child2()); | 
|  | GPRTemporary result(this, Reuse, op1); | 
|  |  | 
|  | GPRReg op1GPR = op1.gpr(); | 
|  | GPRReg op2GPR = op2.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | if (masqueradesAsUndefinedWatchpointIsStillValid()) { | 
|  | DFG_TYPE_CHECK( | 
|  | JSValueSource::unboxedCell(op1GPR), node->child1(), SpecObject, m_jit.branchIfNotObject(op1GPR)); | 
|  | DFG_TYPE_CHECK( | 
|  | JSValueSource::unboxedCell(op2GPR), node->child2(), SpecObject, m_jit.branchIfNotObject(op2GPR)); | 
|  | } else { | 
|  | DFG_TYPE_CHECK( | 
|  | JSValueSource::unboxedCell(op1GPR), node->child1(), SpecObject, m_jit.branchIfNotObject(op1GPR)); | 
|  | speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), node->child1(), | 
|  | m_jit.branchTest8( | 
|  | MacroAssembler::NonZero, | 
|  | MacroAssembler::Address(op1GPR, JSCell::typeInfoFlagsOffset()), | 
|  | MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); | 
|  |  | 
|  | DFG_TYPE_CHECK( | 
|  | JSValueSource::unboxedCell(op2GPR), node->child2(), SpecObject, m_jit.branchIfNotObject(op2GPR)); | 
|  | speculationCheck(BadType, JSValueSource::unboxedCell(op2GPR), node->child2(), | 
|  | m_jit.branchTest8( | 
|  | MacroAssembler::NonZero, | 
|  | MacroAssembler::Address(op2GPR, JSCell::typeInfoFlagsOffset()), | 
|  | MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); | 
|  | } | 
|  |  | 
|  | m_jit.comparePtr(MacroAssembler::Equal, op1GPR, op2GPR, resultGPR); | 
|  | unblessedBooleanResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileSymbolEquality(Node* node) | 
|  | { | 
|  | SpeculateCellOperand left(this, node->child1()); | 
|  | SpeculateCellOperand right(this, node->child2()); | 
|  | GPRTemporary result(this, Reuse, left, right); | 
|  |  | 
|  | GPRReg leftGPR = left.gpr(); | 
|  | GPRReg rightGPR = right.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | speculateSymbol(node->child1(), leftGPR); | 
|  | speculateSymbol(node->child2(), rightGPR); | 
|  |  | 
|  | m_jit.comparePtr(JITCompiler::Equal, leftGPR, rightGPR, resultGPR); | 
|  | unblessedBooleanResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compilePeepHoleSymbolEquality(Node* node, Node* branchNode) | 
|  | { | 
|  | SpeculateCellOperand left(this, node->child1()); | 
|  | SpeculateCellOperand right(this, node->child2()); | 
|  |  | 
|  | GPRReg leftGPR = left.gpr(); | 
|  | GPRReg rightGPR = right.gpr(); | 
|  |  | 
|  | speculateSymbol(node->child1(), leftGPR); | 
|  | speculateSymbol(node->child2(), rightGPR); | 
|  |  | 
|  | BasicBlock* taken = branchNode->branchData()->taken.block; | 
|  | BasicBlock* notTaken = branchNode->branchData()->notTaken.block; | 
|  |  | 
|  | if (taken == nextBlock()) { | 
|  | branchPtr(JITCompiler::NotEqual, leftGPR, rightGPR, notTaken); | 
|  | jump(taken); | 
|  | } else { | 
|  | branchPtr(JITCompiler::Equal, leftGPR, rightGPR, taken); | 
|  | jump(notTaken); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileStringEquality( | 
|  | Node* node, GPRReg leftGPR, GPRReg rightGPR, GPRReg lengthGPR, GPRReg leftTempGPR, | 
|  | GPRReg rightTempGPR, GPRReg leftTemp2GPR, GPRReg rightTemp2GPR, | 
|  | const JITCompiler::JumpList& fastTrue, const JITCompiler::JumpList& fastFalse) | 
|  | { | 
|  | JITCompiler::JumpList trueCase; | 
|  | JITCompiler::JumpList falseCase; | 
|  | JITCompiler::JumpList slowCase; | 
|  |  | 
|  | trueCase.append(fastTrue); | 
|  | falseCase.append(fastFalse); | 
|  |  | 
|  | m_jit.loadPtr(MacroAssembler::Address(leftGPR, JSString::offsetOfValue()), leftTempGPR); | 
|  | m_jit.loadPtr(MacroAssembler::Address(rightGPR, JSString::offsetOfValue()), rightTempGPR); | 
|  |  | 
|  | slowCase.append(m_jit.branchIfRopeStringImpl(leftTempGPR)); | 
|  | slowCase.append(m_jit.branchIfRopeStringImpl(rightTempGPR)); | 
|  |  | 
|  | m_jit.load32(MacroAssembler::Address(leftTempGPR, StringImpl::lengthMemoryOffset()), lengthGPR); | 
|  |  | 
|  | falseCase.append(m_jit.branch32( | 
|  | MacroAssembler::NotEqual, | 
|  | MacroAssembler::Address(rightTempGPR, StringImpl::lengthMemoryOffset()), | 
|  | lengthGPR)); | 
|  |  | 
|  | trueCase.append(m_jit.branchTest32(MacroAssembler::Zero, lengthGPR)); | 
|  |  | 
|  | slowCase.append(m_jit.branchTest32( | 
|  | MacroAssembler::Zero, | 
|  | MacroAssembler::Address(leftTempGPR, StringImpl::flagsOffset()), | 
|  | TrustedImm32(StringImpl::flagIs8Bit()))); | 
|  | slowCase.append(m_jit.branchTest32( | 
|  | MacroAssembler::Zero, | 
|  | MacroAssembler::Address(rightTempGPR, StringImpl::flagsOffset()), | 
|  | TrustedImm32(StringImpl::flagIs8Bit()))); | 
|  |  | 
|  | m_jit.loadPtr(MacroAssembler::Address(leftTempGPR, StringImpl::dataOffset()), leftTempGPR); | 
|  | m_jit.loadPtr(MacroAssembler::Address(rightTempGPR, StringImpl::dataOffset()), rightTempGPR); | 
|  |  | 
|  | MacroAssembler::Label loop = m_jit.label(); | 
|  |  | 
|  | m_jit.sub32(TrustedImm32(1), lengthGPR); | 
|  |  | 
|  | // This isn't going to generate the best code on x86. But that's OK, it's still better | 
|  | // than not inlining. | 
|  | m_jit.load8(MacroAssembler::BaseIndex(leftTempGPR, lengthGPR, MacroAssembler::TimesOne), leftTemp2GPR); | 
|  | m_jit.load8(MacroAssembler::BaseIndex(rightTempGPR, lengthGPR, MacroAssembler::TimesOne), rightTemp2GPR); | 
|  | falseCase.append(m_jit.branch32(MacroAssembler::NotEqual, leftTemp2GPR, rightTemp2GPR)); | 
|  |  | 
|  | m_jit.branchTest32(MacroAssembler::NonZero, lengthGPR).linkTo(loop, &m_jit); | 
|  |  | 
|  | trueCase.link(&m_jit); | 
|  | moveTrueTo(leftTempGPR); | 
|  |  | 
|  | JITCompiler::Jump done = m_jit.jump(); | 
|  |  | 
|  | falseCase.link(&m_jit); | 
|  | moveFalseTo(leftTempGPR); | 
|  |  | 
|  | done.link(&m_jit); | 
|  | addSlowPathGenerator( | 
|  | slowPathCall( | 
|  | slowCase, this, operationCompareStringEq, leftTempGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR)); | 
|  |  | 
|  | 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::compileStringCompare(Node* node, MacroAssembler::RelationalCondition condition) | 
|  | { | 
|  | SpeculateCellOperand left(this, node->child1()); | 
|  | SpeculateCellOperand right(this, node->child2()); | 
|  | GPRReg leftGPR = left.gpr(); | 
|  | GPRReg rightGPR = right.gpr(); | 
|  |  | 
|  | speculateString(node->child1(), leftGPR); | 
|  | speculateString(node->child2(), rightGPR); | 
|  |  | 
|  | C_JITOperation_B_GJssJss compareFunction = nullptr; | 
|  | if (condition == MacroAssembler::LessThan) | 
|  | compareFunction = operationCompareStringLess; | 
|  | else if (condition == MacroAssembler::LessThanOrEqual) | 
|  | compareFunction = operationCompareStringLessEq; | 
|  | else if (condition == MacroAssembler::GreaterThan) | 
|  | compareFunction = operationCompareStringGreater; | 
|  | else if (condition == MacroAssembler::GreaterThanOrEqual) | 
|  | compareFunction = operationCompareStringGreaterEq; | 
|  | else | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  |  | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(compareFunction, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | unblessedBooleanResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileStringIdentCompare(Node* node, MacroAssembler::RelationalCondition condition) | 
|  | { | 
|  | SpeculateCellOperand left(this, node->child1()); | 
|  | SpeculateCellOperand right(this, node->child2()); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRTemporary leftTemp(this); | 
|  | GPRTemporary rightTemp(this); | 
|  |  | 
|  | GPRReg leftGPR = left.gpr(); | 
|  | GPRReg rightGPR = right.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg leftTempGPR = leftTemp.gpr(); | 
|  | GPRReg rightTempGPR = rightTemp.gpr(); | 
|  |  | 
|  | speculateString(node->child1(), leftGPR); | 
|  | speculateString(node->child2(), rightGPR); | 
|  |  | 
|  | C_JITOperation_TT compareFunction = nullptr; | 
|  | if (condition == MacroAssembler::LessThan) | 
|  | compareFunction = operationCompareStringImplLess; | 
|  | else if (condition == MacroAssembler::LessThanOrEqual) | 
|  | compareFunction = operationCompareStringImplLessEq; | 
|  | else if (condition == MacroAssembler::GreaterThan) | 
|  | compareFunction = operationCompareStringImplGreater; | 
|  | else if (condition == MacroAssembler::GreaterThanOrEqual) | 
|  | compareFunction = operationCompareStringImplGreaterEq; | 
|  | else | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  |  | 
|  | speculateStringIdentAndLoadStorage(node->child1(), leftGPR, leftTempGPR); | 
|  | speculateStringIdentAndLoadStorage(node->child2(), rightGPR, rightTempGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(compareFunction, resultGPR, leftTempGPR, rightTempGPR); | 
|  |  | 
|  | unblessedBooleanResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileSameValue(Node* node) | 
|  | { | 
|  | if (node->isBinaryUseKind(DoubleRepUse)) { | 
|  | SpeculateDoubleOperand arg1(this, node->child1()); | 
|  | SpeculateDoubleOperand arg2(this, node->child2()); | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary temp(this); | 
|  | GPRTemporary temp2(this); | 
|  |  | 
|  | FPRReg arg1FPR = arg1.fpr(); | 
|  | FPRReg arg2FPR = arg2.fpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  | GPRReg temp2GPR = temp2.gpr(); | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | m_jit.moveDoubleTo64(arg1FPR, tempGPR); | 
|  | m_jit.moveDoubleTo64(arg2FPR, temp2GPR); | 
|  | auto trueCase = m_jit.branch64(CCallHelpers::Equal, tempGPR, temp2GPR); | 
|  | #else | 
|  | GPRTemporary temp3(this); | 
|  | GPRReg temp3GPR = temp3.gpr(); | 
|  |  | 
|  | m_jit.moveDoubleToInts(arg1FPR, tempGPR, temp2GPR); | 
|  | m_jit.moveDoubleToInts(arg2FPR, temp3GPR, resultGPR); | 
|  | auto notEqual = m_jit.branch32(CCallHelpers::NotEqual, tempGPR, temp3GPR); | 
|  | auto trueCase = m_jit.branch32(CCallHelpers::Equal, temp2GPR, resultGPR); | 
|  | notEqual.link(&m_jit); | 
|  | #endif | 
|  |  | 
|  | m_jit.compareDouble(CCallHelpers::DoubleNotEqualOrUnordered, arg1FPR, arg1FPR, tempGPR); | 
|  | m_jit.compareDouble(CCallHelpers::DoubleNotEqualOrUnordered, arg2FPR, arg2FPR, temp2GPR); | 
|  | m_jit.and32(tempGPR, temp2GPR, resultGPR); | 
|  | auto done = m_jit.jump(); | 
|  |  | 
|  | trueCase.link(&m_jit); | 
|  | m_jit.move(CCallHelpers::TrustedImm32(1), resultGPR); | 
|  | done.link(&m_jit); | 
|  |  | 
|  | unblessedBooleanResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | ASSERT(node->isBinaryUseKind(UntypedUse)); | 
|  |  | 
|  | JSValueOperand arg1(this, node->child1()); | 
|  | JSValueOperand arg2(this, node->child2()); | 
|  | JSValueRegs arg1Regs = arg1.jsValueRegs(); | 
|  | JSValueRegs arg2Regs = arg2.jsValueRegs(); | 
|  |  | 
|  | arg1.use(); | 
|  | arg2.use(); | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | callOperation(operationSameValue, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), arg1Regs, arg2Regs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | unblessedBooleanResult(resultGPR, node, UseChildrenCalledExplicitly); | 
|  | } | 
|  |  | 
|  | 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(); | 
|  |  | 
|  | m_jit.move(TrustedImmPtr::weakPointer(m_jit.graph(), jsEmptyString(vm())), eqGPR); | 
|  | m_jit.comparePtr(CCallHelpers::Equal, strGPR, eqGPR, 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(), (~SpecCellCheck) | SpecString, m_jit.branchIfNotString(cellGPR)); | 
|  |  | 
|  | m_jit.move(TrustedImmPtr::weakPointer(m_jit.graph(), jsEmptyString(vm())), tempGPR); | 
|  | m_jit.comparePtr(CCallHelpers::Equal, cellGPR, tempGPR, tempGPR); | 
|  | auto done = m_jit.jump(); | 
|  |  | 
|  | notCell.link(&m_jit); | 
|  | DFG_TYPE_CHECK( | 
|  | valueRegs, node->child1(), SpecCellCheck | 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); | 
|  |  | 
|  | GPRReg strGPR = str.gpr(); | 
|  |  | 
|  | speculateString(nodeUse, strGPR); | 
|  |  | 
|  | branchPtr(CCallHelpers::Equal, strGPR, TrustedImmPtr::weakPointer(m_jit.graph(), jsEmptyString(vm())), notTaken); | 
|  | jump(taken); | 
|  |  | 
|  | 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, (~SpecCellCheck) | SpecString, m_jit.branchIfNotString(cellGPR)); | 
|  |  | 
|  | branchPtr(CCallHelpers::Equal, cellGPR, TrustedImmPtr::weakPointer(m_jit.graph(), jsEmptyString(vm())), notTaken); | 
|  | jump(taken, ForceJump); | 
|  |  | 
|  | notCell.link(&m_jit); | 
|  | DFG_TYPE_CHECK( | 
|  | valueRegs, nodeUse, SpecCellCheck | 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::cageTypedArrayStorage(GPRReg baseReg, GPRReg storageReg) | 
|  | { | 
|  | auto untagArrayPtr = [&]() { | 
|  | #if CPU(ARM64E) | 
|  | m_jit.untagArrayPtr(MacroAssembler::Address(baseReg, JSArrayBufferView::offsetOfLength()), storageReg); | 
|  | #else | 
|  | UNUSED_PARAM(baseReg); | 
|  | UNUSED_PARAM(storageReg); | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | #if GIGACAGE_ENABLED | 
|  | UNUSED_PARAM(baseReg); | 
|  | if (!Gigacage::shouldBeEnabled()) { | 
|  | untagArrayPtr(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!Gigacage::disablingPrimitiveGigacageIsForbidden()) { | 
|  | VM& vm = this->vm(); | 
|  | if (vm.primitiveGigacageEnabled().isStillValid()) | 
|  | m_jit.graph().watchpoints().addLazily(vm.primitiveGigacageEnabled()); | 
|  | else { | 
|  | untagArrayPtr(); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | m_jit.cageWithoutUntagging(Gigacage::Primitive, storageReg); | 
|  | #endif | 
|  | untagArrayPtr(); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetIndexedPropertyStorage(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | GPRReg baseReg = base.gpr(); | 
|  |  | 
|  | GPRTemporary storage(this); | 
|  | GPRReg storageReg = storage.gpr(); | 
|  |  | 
|  | switch (node->arrayMode().type()) { | 
|  | case Array::String: | 
|  | m_jit.loadPtr(MacroAssembler::Address(baseReg, JSString::offsetOfValue()), storageReg); | 
|  |  | 
|  | addSlowPathGenerator( | 
|  | slowPathCall( | 
|  | m_jit.branchIfRopeStringImpl(storageReg), | 
|  | this, operationResolveRope, storageReg, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseReg)); | 
|  |  | 
|  | m_jit.loadPtr(MacroAssembler::Address(storageReg, StringImpl::dataOffset()), storageReg); | 
|  | break; | 
|  |  | 
|  | default: { | 
|  | auto typedArrayType = node->arrayMode().typedArrayType(); | 
|  | ASSERT_UNUSED(typedArrayType, isTypedView(typedArrayType)); | 
|  |  | 
|  | m_jit.loadPtr(JITCompiler::Address(baseReg, JSArrayBufferView::offsetOfVector()), storageReg); | 
|  | cageTypedArrayStorage(baseReg, storageReg); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | storageResult(storageReg, 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(); | 
|  | ASSERT(baseGPR != vectorGPR); | 
|  | ASSERT(baseGPR != dataGPR); | 
|  | ASSERT(vectorGPR != dataGPR); | 
|  |  | 
|  | GPRReg arrayBufferGPR = dataGPR; | 
|  |  | 
|  | JITCompiler::Jump emptyByteOffset = m_jit.branch32( | 
|  | MacroAssembler::NotEqual, | 
|  | MacroAssembler::Address(baseGPR, JSArrayBufferView::offsetOfMode()), | 
|  | TrustedImm32(WastefulTypedArray)); | 
|  |  | 
|  | m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSArrayBufferView::offsetOfVector()), vectorGPR); | 
|  |  | 
|  | JITCompiler::Jump nullVector = m_jit.branchPtr(JITCompiler::Equal, vectorGPR, TrustedImmPtr(JSArrayBufferView::nullVectorPtr())); | 
|  |  | 
|  | m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), dataGPR); | 
|  | m_jit.cageWithoutUntagging(Gigacage::JSValue, dataGPR); | 
|  |  | 
|  | cageTypedArrayStorage(baseGPR, vectorGPR); | 
|  |  | 
|  | m_jit.loadPtr(MacroAssembler::Address(dataGPR, Butterfly::offsetOfArrayBuffer()), arrayBufferGPR); | 
|  | // FIXME: This needs caging. | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=175515 | 
|  | m_jit.loadPtr(MacroAssembler::Address(arrayBufferGPR, ArrayBuffer::offsetOfData()), dataGPR); | 
|  | #if CPU(ARM64E) | 
|  | m_jit.removeArrayPtrTag(dataGPR); | 
|  | #endif | 
|  |  | 
|  | m_jit.subPtr(dataGPR, vectorGPR); | 
|  |  | 
|  | JITCompiler::Jump done = m_jit.jump(); | 
|  |  | 
|  | #if CPU(ARM64E) | 
|  | nullVector.link(&m_jit); | 
|  | #endif | 
|  | emptyByteOffset.link(&m_jit); | 
|  | m_jit.move(TrustedImmPtr(nullptr), vectorGPR); | 
|  |  | 
|  | done.link(&m_jit); | 
|  | #if !CPU(ARM64E) | 
|  | ASSERT(!JSArrayBufferView::nullVectorPtr()); | 
|  | nullVector.link(&m_jit); | 
|  | #endif | 
|  |  | 
|  | strictInt32Result(vectorGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetByValOnDirectArguments(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, m_graph.varArgChild(node, 0)); | 
|  | SpeculateStrictInt32Operand property(this, m_graph.varArgChild(node, 1)); | 
|  | JSValueRegsTemporary result(this); | 
|  | GPRTemporary scratch(this); | 
|  |  | 
|  | GPRReg baseReg = base.gpr(); | 
|  | GPRReg propertyReg = property.gpr(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | GPRReg scratchReg = scratch.gpr(); | 
|  |  | 
|  | if (!m_compileOkay) | 
|  | return; | 
|  |  | 
|  | ASSERT(ArrayMode(Array::DirectArguments, Array::Read).alreadyChecked(m_jit.graph(), node, m_state.forNode(m_graph.varArgChild(node, 0)))); | 
|  |  | 
|  | speculationCheck( | 
|  | ExoticObjectMode, JSValueSource(), nullptr, | 
|  | m_jit.branchTestPtr( | 
|  | MacroAssembler::NonZero, | 
|  | MacroAssembler::Address(baseReg, DirectArguments::offsetOfMappedArguments()))); | 
|  |  | 
|  | m_jit.load32(CCallHelpers::Address(baseReg, DirectArguments::offsetOfLength()), scratchReg); | 
|  | auto isOutOfBounds = m_jit.branch32(CCallHelpers::AboveOrEqual, propertyReg, scratchReg); | 
|  | if (node->arrayMode().isInBounds()) | 
|  | speculationCheck(OutOfBounds, JSValueSource(), nullptr, isOutOfBounds); | 
|  |  | 
|  | m_jit.loadValue( | 
|  | MacroAssembler::BaseIndex( | 
|  | baseReg, propertyReg, MacroAssembler::TimesEight, DirectArguments::storageOffset()), | 
|  | resultRegs); | 
|  |  | 
|  | if (!node->arrayMode().isInBounds()) { | 
|  | addSlowPathGenerator( | 
|  | slowPathCall( | 
|  | isOutOfBounds, this, operationGetByValObjectInt, | 
|  | extractResult(resultRegs), TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseReg, propertyReg)); | 
|  | } | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetByValOnScopedArguments(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, m_graph.varArgChild(node, 0)); | 
|  | SpeculateStrictInt32Operand property(this, m_graph.varArgChild(node, 1)); | 
|  | JSValueRegsTemporary result(this); | 
|  | GPRTemporary scratch(this); | 
|  | GPRTemporary scratch2(this); | 
|  |  | 
|  | GPRReg baseReg = base.gpr(); | 
|  | GPRReg propertyReg = property.gpr(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | GPRReg scratchReg = scratch.gpr(); | 
|  | GPRReg scratch2Reg = scratch2.gpr(); | 
|  |  | 
|  | if (!m_compileOkay) | 
|  | return; | 
|  |  | 
|  | ASSERT(ArrayMode(Array::ScopedArguments, Array::Read).alreadyChecked(m_jit.graph(), node, m_state.forNode(m_graph.varArgChild(node, 0)))); | 
|  |  | 
|  | m_jit.loadPtr( | 
|  | MacroAssembler::Address(baseReg, ScopedArguments::offsetOfStorage()), resultRegs.payloadGPR()); | 
|  |  | 
|  | speculationCheck( | 
|  | 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::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, | 
|  | JSLexicalEnvironment::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( | 
|  | resultRegs.payloadGPR(), scratch2Reg, MacroAssembler::TimesEight), | 
|  | resultRegs); | 
|  | speculationCheck(ExoticObjectMode, JSValueSource(), nullptr, m_jit.branchIfEmpty(resultRegs)); | 
|  |  | 
|  | done.link(&m_jit); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetScope(Node* node) | 
|  | { | 
|  | 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::compileGetGlobalObject(Node* node) | 
|  | { | 
|  | SpeculateCellOperand object(this, node->child1()); | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary scratch(this); | 
|  | m_jit.emitLoadStructure(vm(), object.gpr(), result.gpr(), scratch.gpr()); | 
|  | m_jit.loadPtr(JITCompiler::Address(result.gpr(), Structure::globalObjectOffset()), result.gpr()); | 
|  | cellResult(result.gpr(), node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetGlobalThis(Node* node) | 
|  | { | 
|  | GPRTemporary result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | auto* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic); | 
|  | m_jit.loadPtr(globalObject->addressOfGlobalThis(), resultGPR); | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | bool SpeculativeJIT::canBeRope(Edge& edge) | 
|  | { | 
|  | if (m_state.forNode(edge).isType(SpecStringIdent)) | 
|  | return false; | 
|  | // If this value is LazyValue, it will be converted to JSString, and the result must be non-rope string. | 
|  | String string = edge->tryGetString(m_graph); | 
|  | if (!string.isNull()) | 
|  | return false; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetArrayLength(Node* node) | 
|  | { | 
|  | switch (node->arrayMode().type()) { | 
|  | case Array::Undecided: | 
|  | case Array::Int32: | 
|  | case Array::Double: | 
|  | case Array::Contiguous: { | 
|  | StorageOperand storage(this, node->child2()); | 
|  | GPRTemporary result(this, Reuse, storage); | 
|  | GPRReg storageReg = storage.gpr(); | 
|  | GPRReg resultReg = result.gpr(); | 
|  | m_jit.load32(MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()), resultReg); | 
|  |  | 
|  | strictInt32Result(resultReg, node); | 
|  | break; | 
|  | } | 
|  | case Array::ArrayStorage: | 
|  | case Array::SlowPutArrayStorage: { | 
|  | StorageOperand storage(this, node->child2()); | 
|  | 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(), nullptr, m_jit.branch32(MacroAssembler::LessThan, resultReg, MacroAssembler::TrustedImm32(0))); | 
|  |  | 
|  | strictInt32Result(resultReg, node); | 
|  | break; | 
|  | } | 
|  | case Array::String: { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | GPRTemporary result(this, Reuse, base); | 
|  | GPRTemporary temp(this); | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  |  | 
|  | bool needsRopeCase = canBeRope(node->child1()); | 
|  |  | 
|  | m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSString::offsetOfValue()), tempGPR); | 
|  | CCallHelpers::Jump isRope; | 
|  | if (needsRopeCase) | 
|  | isRope = m_jit.branchIfRopeStringImpl(tempGPR); | 
|  | m_jit.load32(MacroAssembler::Address(tempGPR, StringImpl::lengthMemoryOffset()), resultGPR); | 
|  | if (needsRopeCase) { | 
|  | auto done = m_jit.jump(); | 
|  |  | 
|  | isRope.link(&m_jit); | 
|  | m_jit.load32(CCallHelpers::Address(baseGPR, JSRopeString::offsetOfLength()), resultGPR); | 
|  |  | 
|  | done.link(&m_jit); | 
|  | } | 
|  | strictInt32Result(resultGPR, node); | 
|  | break; | 
|  | } | 
|  | 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, Array::Read).alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1()))); | 
|  |  | 
|  | speculationCheck( | 
|  | ExoticObjectMode, JSValueSource(), nullptr, | 
|  | m_jit.branchTestPtr( | 
|  | MacroAssembler::NonZero, | 
|  | MacroAssembler::Address(baseReg, DirectArguments::offsetOfMappedArguments()))); | 
|  |  | 
|  | m_jit.load32( | 
|  | MacroAssembler::Address(baseReg, DirectArguments::offsetOfLength()), resultReg); | 
|  |  | 
|  | strictInt32Result(resultReg, node); | 
|  | break; | 
|  | } | 
|  | case Array::ScopedArguments: { | 
|  | 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::ScopedArguments, Array::Read).alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1()))); | 
|  |  | 
|  | speculationCheck( | 
|  | ExoticObjectMode, JSValueSource(), nullptr, | 
|  | m_jit.branchTest8( | 
|  | MacroAssembler::NonZero, | 
|  | MacroAssembler::Address(baseReg, ScopedArguments::offsetOfOverrodeThings()))); | 
|  |  | 
|  | m_jit.load32( | 
|  | MacroAssembler::Address(baseReg, ScopedArguments::offsetOfTotalLength()), resultReg); | 
|  |  | 
|  | strictInt32Result(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(); | 
|  | m_jit.load32(MacroAssembler::Address(baseGPR, JSArrayBufferView::offsetOfLength()), resultGPR); | 
|  | strictInt32Result(resultGPR, node); | 
|  | break; | 
|  | } } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCheckIdent(Node* node) | 
|  | { | 
|  | SpeculateCellOperand stringOrSymbol(this, node->child1()); | 
|  | GPRTemporary impl(this); | 
|  | GPRReg stringOrSymbolGPR = stringOrSymbol.gpr(); | 
|  | GPRReg implGPR = impl.gpr(); | 
|  |  | 
|  | if (node->child1().useKind() == StringIdentUse) { | 
|  | speculateString(node->child1(), stringOrSymbolGPR); | 
|  | speculateStringIdentAndLoadStorage(node->child1(), stringOrSymbolGPR, implGPR); | 
|  | } else { | 
|  | ASSERT(node->child1().useKind() == SymbolUse); | 
|  | speculateSymbol(node->child1(), stringOrSymbolGPR); | 
|  | m_jit.loadPtr(MacroAssembler::Address(stringOrSymbolGPR, Symbol::offsetOfSymbolImpl()), implGPR); | 
|  | } | 
|  |  | 
|  | UniquedStringImpl* uid = node->uidOperand(); | 
|  | speculationCheck( | 
|  | BadIdent, JSValueSource(), nullptr, | 
|  | m_jit.branchPtr(JITCompiler::NotEqual, implGPR, TrustedImmPtr(uid))); | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | template <typename ClassType> | 
|  | void SpeculativeJIT::compileNewFunctionCommon(GPRReg resultGPR, RegisteredStructure structure, GPRReg scratch1GPR, GPRReg scratch2GPR, GPRReg scopeGPR, MacroAssembler::JumpList& slowPath, size_t size, FunctionExecutable* executable) | 
|  | { | 
|  | auto butterfly = TrustedImmPtr(nullptr); | 
|  | emitAllocateJSObjectWithKnownSize<ClassType>(resultGPR, TrustedImmPtr(structure), butterfly, scratch1GPR, scratch2GPR, slowPath, size); | 
|  |  | 
|  | m_jit.storePtr(scopeGPR, JITCompiler::Address(resultGPR, JSFunction::offsetOfScopeChain())); | 
|  | m_jit.storePtr(TrustedImmPtr::weakPointer(m_jit.graph(), executable), JITCompiler::Address(resultGPR, JSFunction::offsetOfExecutableOrRareData())); | 
|  | m_jit.mutatorFence(vm()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNewFunction(Node* node) | 
|  | { | 
|  | NodeType nodeType = node->op(); | 
|  | ASSERT(nodeType == NewFunction || nodeType == NewGeneratorFunction || nodeType == NewAsyncFunction || nodeType == NewAsyncGeneratorFunction); | 
|  |  | 
|  | SpeculateCellOperand scope(this, node->child1()); | 
|  | GPRReg scopeGPR = scope.gpr(); | 
|  |  | 
|  | FunctionExecutable* executable = node->castOperand<FunctionExecutable*>(); | 
|  |  | 
|  | if (executable->singleton().isStillValid()) { | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | if (nodeType == NewGeneratorFunction) | 
|  | callOperation(operationNewGeneratorFunction, resultGPR, &vm(), scopeGPR, executable); | 
|  | else if (nodeType == NewAsyncFunction) | 
|  | callOperation(operationNewAsyncFunction, resultGPR, &vm(), scopeGPR, executable); | 
|  | else if (nodeType == NewAsyncGeneratorFunction) | 
|  | callOperation(operationNewAsyncGeneratorFunction, resultGPR, &vm(), scopeGPR, executable); | 
|  | else | 
|  | callOperation(operationNewFunction, resultGPR, &vm(), scopeGPR, executable); | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | RegisteredStructure structure = m_jit.graph().registerStructure( | 
|  | [&] () { | 
|  | JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic); | 
|  | switch (nodeType) { | 
|  | case NewGeneratorFunction: | 
|  | return globalObject->generatorFunctionStructure(); | 
|  | case NewAsyncFunction: | 
|  | return globalObject->asyncFunctionStructure(); | 
|  | case NewAsyncGeneratorFunction: | 
|  | return globalObject->asyncGeneratorFunctionStructure(); | 
|  | case NewFunction: | 
|  | return JSFunction::selectStructureForNewFuncExp(globalObject, node->castOperand<FunctionExecutable*>()); | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  | }()); | 
|  |  | 
|  | 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) { | 
|  | compileNewFunctionCommon<JSFunction>(resultGPR, structure, scratch1GPR, scratch2GPR, scopeGPR, slowPath, JSFunction::allocationSize(0), executable); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowPath, this, operationNewFunctionWithInvalidatedReallocationWatchpoint, resultGPR, &vm(), scopeGPR, executable)); | 
|  | } | 
|  |  | 
|  | if (nodeType == NewGeneratorFunction) { | 
|  | compileNewFunctionCommon<JSGeneratorFunction>(resultGPR, structure, scratch1GPR, scratch2GPR, scopeGPR, slowPath, JSGeneratorFunction::allocationSize(0), executable); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowPath, this, operationNewGeneratorFunctionWithInvalidatedReallocationWatchpoint, resultGPR, &vm(), scopeGPR, executable)); | 
|  | } | 
|  |  | 
|  | if (nodeType == NewAsyncFunction) { | 
|  | compileNewFunctionCommon<JSAsyncFunction>(resultGPR, structure, scratch1GPR, scratch2GPR, scopeGPR, slowPath, JSAsyncFunction::allocationSize(0), executable); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowPath, this, operationNewAsyncFunctionWithInvalidatedReallocationWatchpoint, resultGPR, &vm(), scopeGPR, executable)); | 
|  | } | 
|  |  | 
|  | if (nodeType == NewAsyncGeneratorFunction) { | 
|  | compileNewFunctionCommon<JSAsyncGeneratorFunction>(resultGPR, structure, scratch1GPR, scratch2GPR, scopeGPR, slowPath, JSAsyncGeneratorFunction::allocationSize(0), executable); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowPath, this, operationNewAsyncGeneratorFunctionWithInvalidatedReallocationWatchpoint, resultGPR, &vm(), scopeGPR, executable)); | 
|  | } | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileSetFunctionName(Node* node) | 
|  | { | 
|  | SpeculateCellOperand func(this, node->child1()); | 
|  | GPRReg funcGPR = func.gpr(); | 
|  | JSValueOperand nameValue(this, node->child2()); | 
|  | JSValueRegs nameValueRegs = nameValue.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(operationSetFunctionName, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), funcGPR, nameValueRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileVarargsLength(Node* node) | 
|  | { | 
|  | LoadVarargsData* data = node->loadVarargsData(); | 
|  |  | 
|  | JSValueRegs argumentsRegs; | 
|  | lock(GPRInfo::returnValueGPR); | 
|  | JSValueOperand arguments(this, node->argumentsChild()); | 
|  | argumentsRegs = arguments.jsValueRegs(); | 
|  | flushRegisters(); | 
|  | unlock(GPRInfo::returnValueGPR); | 
|  |  | 
|  | callOperation(operationSizeOfVarargs, GPRInfo::returnValueGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), argumentsRegs, data->offset); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | lock(GPRInfo::returnValueGPR); | 
|  | GPRTemporary argCountIncludingThis(this); | 
|  | GPRReg argCountIncludingThisGPR = argCountIncludingThis.gpr(); | 
|  | unlock(GPRInfo::returnValueGPR); | 
|  |  | 
|  | m_jit.add32(TrustedImm32(1), GPRInfo::returnValueGPR, argCountIncludingThisGPR); | 
|  |  | 
|  | strictInt32Result(argCountIncludingThisGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileLoadVarargs(Node* node) | 
|  | { | 
|  | LoadVarargsData* data = node->loadVarargsData(); | 
|  |  | 
|  | SpeculateStrictInt32Operand argumentCount(this, node->child1()); | 
|  | JSValueOperand arguments(this, node->argumentsChild()); | 
|  | GPRReg argumentCountIncludingThis = argumentCount.gpr(); | 
|  | JSValueRegs argumentsRegs = arguments.jsValueRegs(); | 
|  |  | 
|  | speculationCheck( | 
|  | VarargsOverflow, JSValueSource(), Edge(), m_jit.branchTest32( | 
|  | MacroAssembler::Zero, | 
|  | argumentCountIncludingThis)); | 
|  |  | 
|  | speculationCheck( | 
|  | VarargsOverflow, JSValueSource(), Edge(), m_jit.branch32( | 
|  | MacroAssembler::Above, | 
|  | argumentCountIncludingThis, | 
|  | TrustedImm32(data->limit))); | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | m_jit.store32(argumentCountIncludingThis, JITCompiler::payloadFor(data->machineCount)); | 
|  |  | 
|  | callOperation(operationLoadVarargs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), data->machineStart.offset(), argumentsRegs, data->offset, argumentCountIncludingThis, data->mandatoryMinimum); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileForwardVarargs(Node* node) | 
|  | { | 
|  | LoadVarargsData* data = node->loadVarargsData(); | 
|  | InlineCallFrame* inlineCallFrame; | 
|  | if (node->argumentsChild()) | 
|  | inlineCallFrame = node->argumentsChild()->origin.semantic.inlineCallFrame(); | 
|  | else | 
|  | inlineCallFrame = node->origin.semantic.inlineCallFrame(); | 
|  |  | 
|  | SpeculateStrictInt32Operand argumentCount(this, node->child1()); | 
|  | GPRTemporary length(this); | 
|  | JSValueRegsTemporary temp(this); | 
|  | GPRReg argumentCountIncludingThis = argumentCount.gpr(); | 
|  | GPRReg lengthGPR = argumentCount.gpr(); | 
|  | JSValueRegs tempRegs = temp.regs(); | 
|  |  | 
|  | m_jit.move(argumentCountIncludingThis, lengthGPR); | 
|  | 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*>(); | 
|  | RegisteredStructure structure = m_jit.graph().registerStructure(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->singleton().isStillValid()) { | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | #if USE(JSVALUE32_64) | 
|  | JSValueRegsTemporary initialization(this); | 
|  | JSValueRegs initializationRegs = initialization.regs(); | 
|  | m_jit.moveTrustedValue(initializationValue, initializationRegs); | 
|  | #endif | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | callOperation(operationCreateActivationDirect, | 
|  | resultGPR, &vm(), structure, scopeGPR, table, TrustedImm64(JSValue::encode(initializationValue))); | 
|  | #else | 
|  | callOperation(operationCreateActivationDirect, | 
|  | resultGPR, &vm(), structure, scopeGPR, table, initializationRegs); | 
|  | #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(); | 
|  |  | 
|  | #if USE(JSVALUE32_64) | 
|  | JSValueRegsTemporary initialization(this); | 
|  | JSValueRegs initializationRegs = initialization.regs(); | 
|  | m_jit.moveTrustedValue(initializationValue, initializationRegs); | 
|  | #endif | 
|  |  | 
|  | JITCompiler::JumpList slowPath; | 
|  | auto butterfly = TrustedImmPtr(nullptr); | 
|  | emitAllocateJSObjectWithKnownSize<JSLexicalEnvironment>( | 
|  | resultGPR, TrustedImmPtr(structure), butterfly, 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(node->cellOperand()), | 
|  | 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)))); | 
|  | } | 
|  |  | 
|  | m_jit.mutatorFence(vm()); | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | addSlowPathGenerator( | 
|  | slowPathCall( | 
|  | slowPath, this, operationCreateActivationDirect, resultGPR, &vm(), structure, scopeGPR, table, TrustedImm64(JSValue::encode(initializationValue)))); | 
|  | #else | 
|  | addSlowPathGenerator( | 
|  | slowPathCall( | 
|  | slowPath, this, operationCreateActivationDirect, resultGPR, &vm(), structure, scopeGPR, table, initializationRegs)); | 
|  | #endif | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCreateDirectArguments(Node* node) | 
|  | { | 
|  | // 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. | 
|  | auto* inlineCallFrame = node->origin.semantic.inlineCallFrame(); | 
|  | if (inlineCallFrame | 
|  | && !inlineCallFrame->isVarargs()) { | 
|  | knownLength = static_cast<unsigned>(inlineCallFrame->argumentCountIncludingThis - 1); | 
|  | lengthIsKnown = true; | 
|  | } else { | 
|  | knownLength = UINT_MAX; | 
|  | lengthIsKnown = false; | 
|  |  | 
|  | GPRTemporary realLength(this); | 
|  | length.adopt(realLength); | 
|  | lengthGPR = length.gpr(); | 
|  |  | 
|  | VirtualRegister argumentCountRegister = m_jit.argumentCount(node->origin.semantic); | 
|  | m_jit.load32(JITCompiler::payloadFor(argumentCountRegister), lengthGPR); | 
|  | m_jit.sub32(TrustedImm32(1), lengthGPR); | 
|  | } | 
|  |  | 
|  | RegisteredStructure structure = | 
|  | m_jit.graph().registerStructure(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) { | 
|  | auto butterfly = TrustedImmPtr(nullptr); | 
|  | emitAllocateJSObjectWithKnownSize<DirectArguments>( | 
|  | resultGPR, TrustedImmPtr(structure), butterfly, 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(nullptr), JITCompiler::Address(resultGPR, DirectArguments::offsetOfMappedArguments())); | 
|  |  | 
|  | m_jit.storePtr( | 
|  | TrustedImmPtr(nullptr), JITCompiler::Address(resultGPR, DirectArguments::offsetOfModifiedArgumentsDescriptor())); | 
|  |  | 
|  | if (lengthIsKnown) { | 
|  | addSlowPathGenerator( | 
|  | slowPathCall( | 
|  | slowPath, this, operationCreateDirectArguments, resultGPR, &vm(), structure, | 
|  | knownLength, minCapacity)); | 
|  | } else { | 
|  | auto generator = makeUnique<CallCreateDirectArgumentsSlowPathGenerator>( | 
|  | slowPath, this, resultGPR, structure, lengthGPR, minCapacity); | 
|  | addSlowPathGenerator(WTFMove(generator)); | 
|  | } | 
|  |  | 
|  | if (inlineCallFrame) { | 
|  | if (inlineCallFrame->isClosureCall) { | 
|  | m_jit.loadPtr( | 
|  | JITCompiler::addressFor( | 
|  | inlineCallFrame->calleeRecovery.virtualRegister()), | 
|  | scratch1GPR); | 
|  | } else { | 
|  | m_jit.move( | 
|  | TrustedImmPtr::weakPointer( | 
|  | m_jit.graph(), inlineCallFrame->calleeRecovery.constant().asCell()), | 
|  | scratch1GPR); | 
|  | } | 
|  | } else | 
|  | m_jit.loadPtr(JITCompiler::addressFor(CallFrameSlot::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); | 
|  | } | 
|  |  | 
|  | m_jit.mutatorFence(vm()); | 
|  |  | 
|  | 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::compileGetArgument(Node* node) | 
|  | { | 
|  | GPRTemporary argumentCount(this); | 
|  | JSValueRegsTemporary result(this); | 
|  | GPRReg argumentCountGPR = argumentCount.gpr(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | m_jit.load32(CCallHelpers::payloadFor(m_jit.argumentCount(node->origin.semantic)), argumentCountGPR); | 
|  | auto argumentOutOfBounds = m_jit.branch32(CCallHelpers::LessThanOrEqual, argumentCountGPR, CCallHelpers::TrustedImm32(node->argumentIndex())); | 
|  | m_jit.loadValue(CCallHelpers::addressFor(CCallHelpers::argumentsStart(node->origin.semantic) + node->argumentIndex() - 1), resultRegs); | 
|  | auto done = m_jit.jump(); | 
|  |  | 
|  | argumentOutOfBounds.link(&m_jit); | 
|  | m_jit.moveValue(jsUndefined(), resultRegs); | 
|  |  | 
|  | done.link(&m_jit); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCreateScopedArguments(Node* node) | 
|  | { | 
|  | SpeculateCellOperand scope(this, node->child1()); | 
|  | GPRReg scopeGPR = scope.gpr(); | 
|  |  | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | flushRegisters(); | 
|  |  | 
|  | JSGlobalObject* globalObject = m_jit.globalObjectFor(node->origin.semantic); | 
|  |  | 
|  | // 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:JSGlobalObject*, 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::weakPointer(m_jit.graph(), globalObject->scopedArgumentsStructure()), | 
|  | destGPR); | 
|  | }); | 
|  | m_jit.setupArgument(0, [&] (GPRReg destGPR) { m_jit.move(TrustedImmPtr::weakPointer(m_graph, globalObject), destGPR); }); | 
|  |  | 
|  | appendCallSetResult(operationCreateScopedArguments, resultGPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCreateClonedArguments(Node* node) | 
|  | { | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | flushRegisters(); | 
|  |  | 
|  | JSGlobalObject* globalObject = m_jit.globalObjectFor(node->origin.semantic); | 
|  |  | 
|  | // 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:JSGlobalObject*, 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::weakPointer( | 
|  | m_jit.graph(), globalObject->clonedArgumentsStructure()), | 
|  | destGPR); | 
|  | }); | 
|  | m_jit.setupArgument(0, [&] (GPRReg destGPR) { m_jit.move(TrustedImmPtr::weakPointer(m_graph, globalObject), destGPR); }); | 
|  |  | 
|  | appendCallSetResult(operationCreateClonedArguments, resultGPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCreateArgumentsButterfly(Node* node) | 
|  | { | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | flushRegisters(); | 
|  |  | 
|  | JSGlobalObject* globalObject = m_jit.globalObjectFor(node->origin.semantic); | 
|  |  | 
|  | // 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:JSGlobalObject*, 1:start, 3:length | 
|  | m_jit.setupArgument(2, [&] (GPRReg destGPR) { emitGetLength(node->origin.semantic, destGPR); }); | 
|  | m_jit.setupArgument(1, [&] (GPRReg destGPR) { emitGetArgumentStart(node->origin.semantic, destGPR); }); | 
|  | m_jit.setupArgument(0, [&] (GPRReg destGPR) { m_jit.move(TrustedImmPtr::weakPointer(m_graph, globalObject), destGPR); }); | 
|  |  | 
|  | appendCallSetResult(operationCreateArgumentsButterfly, resultGPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCreateRest(Node* node) | 
|  | { | 
|  | ASSERT(node->op() == CreateRest); | 
|  |  | 
|  | if (m_jit.graph().isWatchingHavingABadTimeWatchpoint(node)) { | 
|  | SpeculateStrictInt32Operand arrayLength(this, node->child1()); | 
|  | GPRTemporary arrayResult(this); | 
|  |  | 
|  | GPRReg arrayLengthGPR = arrayLength.gpr(); | 
|  | GPRReg arrayResultGPR = arrayResult.gpr(); | 
|  |  | 
|  | // We can tell compileAllocateNewArrayWithSize() that it does not need to check | 
|  | // for large arrays and use ArrayStorage structure because arrayLength here will | 
|  | // always be bounded by stack size. Realistically, we won't be able to push enough | 
|  | // arguments to have arrayLength exceed MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH. | 
|  | bool shouldAllowForArrayStorageStructureForLargeArrays = false; | 
|  | compileAllocateNewArrayWithSize(m_jit.graph().globalObjectFor(node->origin.semantic), arrayResultGPR, arrayLengthGPR, ArrayWithContiguous, shouldAllowForArrayStorageStructureForLargeArrays); | 
|  |  | 
|  | GPRTemporary argumentsStart(this); | 
|  | GPRReg argumentsStartGPR = argumentsStart.gpr(); | 
|  |  | 
|  | emitGetArgumentStart(node->origin.semantic, argumentsStartGPR); | 
|  |  | 
|  | GPRTemporary butterfly(this); | 
|  | GPRTemporary currentLength(this); | 
|  | JSValueRegsTemporary value(this); | 
|  |  | 
|  | JSValueRegs valueRegs = value.regs(); | 
|  | GPRReg currentLengthGPR = currentLength.gpr(); | 
|  | GPRReg butterflyGPR = butterfly.gpr(); | 
|  |  | 
|  | m_jit.loadPtr(MacroAssembler::Address(arrayResultGPR, JSObject::butterflyOffset()), butterflyGPR); | 
|  |  | 
|  | CCallHelpers::Jump skipLoop = m_jit.branch32(MacroAssembler::Equal, arrayLengthGPR, TrustedImm32(0)); | 
|  | m_jit.zeroExtend32ToWord(arrayLengthGPR, currentLengthGPR); | 
|  | m_jit.addPtr(Imm32(sizeof(Register) * node->numberOfArgumentsToSkip()), argumentsStartGPR); | 
|  |  | 
|  | auto loop = m_jit.label(); | 
|  | m_jit.sub32(TrustedImm32(1), currentLengthGPR); | 
|  | m_jit.loadValue(JITCompiler::BaseIndex(argumentsStartGPR, currentLengthGPR, MacroAssembler::TimesEight), valueRegs); | 
|  | m_jit.storeValue(valueRegs, MacroAssembler::BaseIndex(butterflyGPR, currentLengthGPR, MacroAssembler::TimesEight)); | 
|  | m_jit.branch32(MacroAssembler::NotEqual, currentLengthGPR, TrustedImm32(0)).linkTo(loop, &m_jit); | 
|  |  | 
|  | skipLoop.link(&m_jit); | 
|  | cellResult(arrayResultGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | SpeculateStrictInt32Operand arrayLength(this, node->child1()); | 
|  | GPRTemporary argumentsStart(this); | 
|  | GPRTemporary numberOfArgumentsToSkip(this); | 
|  |  | 
|  | GPRReg arrayLengthGPR = arrayLength.gpr(); | 
|  | GPRReg argumentsStartGPR = argumentsStart.gpr(); | 
|  |  | 
|  | emitGetArgumentStart(node->origin.semantic, argumentsStartGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | callOperation(operationCreateRest, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), argumentsStartGPR, Imm32(node->numberOfArgumentsToSkip()), arrayLengthGPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileSpread(Node* node) | 
|  | { | 
|  | ASSERT(node->op() == Spread); | 
|  |  | 
|  | SpeculateCellOperand operand(this, node->child1()); | 
|  | GPRReg argument = operand.gpr(); | 
|  |  | 
|  | if (node->child1().useKind() == ArrayUse) | 
|  | speculateArray(node->child1(), argument); | 
|  |  | 
|  | if (m_jit.graph().canDoFastSpread(node, m_state.forNode(node->child1()))) { | 
|  | #if USE(JSVALUE64) | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary scratch1(this); | 
|  | GPRTemporary scratch2(this); | 
|  | GPRTemporary length(this); | 
|  | FPRTemporary doubleRegister(this); | 
|  |  | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg scratch1GPR = scratch1.gpr(); | 
|  | GPRReg scratch2GPR = scratch2.gpr(); | 
|  | GPRReg lengthGPR = length.gpr(); | 
|  | FPRReg doubleFPR = doubleRegister.fpr(); | 
|  |  | 
|  | MacroAssembler::JumpList slowPath; | 
|  | MacroAssembler::JumpList done; | 
|  |  | 
|  | m_jit.load8(MacroAssembler::Address(argument, JSCell::indexingTypeAndMiscOffset()), scratch1GPR); | 
|  | m_jit.and32(TrustedImm32(IndexingModeMask), scratch1GPR); | 
|  | auto notShareCase = m_jit.branch32(CCallHelpers::NotEqual, scratch1GPR, TrustedImm32(CopyOnWriteArrayWithContiguous)); | 
|  | m_jit.loadPtr(MacroAssembler::Address(argument, JSObject::butterflyOffset()), resultGPR); | 
|  | m_jit.addPtr(TrustedImm32(-static_cast<ptrdiff_t>(JSImmutableButterfly::offsetOfData())), resultGPR); | 
|  | done.append(m_jit.jump()); | 
|  |  | 
|  | notShareCase.link(&m_jit); | 
|  | m_jit.and32(TrustedImm32(IndexingShapeMask), scratch1GPR); | 
|  | m_jit.sub32(TrustedImm32(Int32Shape), scratch1GPR); | 
|  |  | 
|  | slowPath.append(m_jit.branch32(MacroAssembler::Above, scratch1GPR, TrustedImm32(ContiguousShape - Int32Shape))); | 
|  |  | 
|  | m_jit.loadPtr(MacroAssembler::Address(argument, JSObject::butterflyOffset()), lengthGPR); | 
|  | m_jit.load32(MacroAssembler::Address(lengthGPR, Butterfly::offsetOfPublicLength()), lengthGPR); | 
|  | slowPath.append(m_jit.branch32(MacroAssembler::Above, lengthGPR, TrustedImm32(MAX_STORAGE_VECTOR_LENGTH))); | 
|  | static_assert(sizeof(JSValue) == 8 && 1 << 3 == 8, "This is strongly assumed in the code below."); | 
|  | m_jit.move(lengthGPR, scratch1GPR); | 
|  | m_jit.lshift32(TrustedImm32(3), scratch1GPR); | 
|  | m_jit.add32(TrustedImm32(JSImmutableButterfly::offsetOfData()), scratch1GPR); | 
|  |  | 
|  | m_jit.emitAllocateVariableSizedCell<JSImmutableButterfly>(vm(), resultGPR, TrustedImmPtr(m_jit.graph().registerStructure(m_jit.graph().m_vm.immutableButterflyStructures[arrayIndexFromIndexingType(CopyOnWriteArrayWithContiguous) - NumberOfIndexingShapes].get())), scratch1GPR, scratch1GPR, scratch2GPR, slowPath); | 
|  | m_jit.store32(lengthGPR, MacroAssembler::Address(resultGPR, JSImmutableButterfly::offsetOfPublicLength())); | 
|  | m_jit.store32(lengthGPR, MacroAssembler::Address(resultGPR, JSImmutableButterfly::offsetOfVectorLength())); | 
|  |  | 
|  | m_jit.loadPtr(MacroAssembler::Address(argument, JSObject::butterflyOffset()), scratch1GPR); | 
|  |  | 
|  | m_jit.load8(MacroAssembler::Address(argument, JSCell::indexingTypeAndMiscOffset()), scratch2GPR); | 
|  | m_jit.and32(TrustedImm32(IndexingShapeMask), scratch2GPR); | 
|  | auto isDoubleArray = m_jit.branch32(MacroAssembler::Equal, scratch2GPR, TrustedImm32(DoubleShape)); | 
|  |  | 
|  | { | 
|  | done.append(m_jit.branchTest32(MacroAssembler::Zero, lengthGPR)); | 
|  | auto loopStart = m_jit.label(); | 
|  | m_jit.sub32(TrustedImm32(1), lengthGPR); | 
|  | m_jit.load64(MacroAssembler::BaseIndex(scratch1GPR, lengthGPR, MacroAssembler::TimesEight), scratch2GPR); | 
|  | auto notEmpty = m_jit.branchIfNotEmpty(scratch2GPR); | 
|  | m_jit.move(TrustedImm64(JSValue::encode(jsUndefined())), scratch2GPR); | 
|  | notEmpty.link(&m_jit); | 
|  | m_jit.store64(scratch2GPR, MacroAssembler::BaseIndex(resultGPR, lengthGPR, MacroAssembler::TimesEight, JSImmutableButterfly::offsetOfData())); | 
|  | m_jit.branchTest32(MacroAssembler::NonZero, lengthGPR).linkTo(loopStart, &m_jit); | 
|  | done.append(m_jit.jump()); | 
|  | } | 
|  |  | 
|  | isDoubleArray.link(&m_jit); | 
|  | { | 
|  | done.append(m_jit.branchTest32(MacroAssembler::Zero, lengthGPR)); | 
|  | auto loopStart = m_jit.label(); | 
|  | m_jit.sub32(TrustedImm32(1), lengthGPR); | 
|  | m_jit.loadDouble(MacroAssembler::BaseIndex(scratch1GPR, lengthGPR, MacroAssembler::TimesEight), doubleFPR); | 
|  | auto notEmpty = m_jit.branchIfNotNaN(doubleFPR); | 
|  | m_jit.move(TrustedImm64(JSValue::encode(jsUndefined())), scratch2GPR); | 
|  | auto doStore = m_jit.jump(); | 
|  | notEmpty.link(&m_jit); | 
|  | m_jit.boxDouble(doubleFPR, scratch2GPR); | 
|  | doStore.link(&m_jit); | 
|  | m_jit.store64(scratch2GPR, MacroAssembler::BaseIndex(resultGPR, lengthGPR, MacroAssembler::TimesEight, JSImmutableButterfly::offsetOfData())); | 
|  | m_jit.branchTest32(MacroAssembler::NonZero, lengthGPR).linkTo(loopStart, &m_jit); | 
|  | done.append(m_jit.jump()); | 
|  | } | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowPath, this, operationSpreadFastArray, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), argument)); | 
|  |  | 
|  | done.link(&m_jit); | 
|  | m_jit.mutatorFence(vm()); | 
|  | cellResult(resultGPR, node); | 
|  | #else | 
|  | flushRegisters(); | 
|  |  | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | callOperation(operationSpreadFastArray, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), argument); | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(resultGPR, node); | 
|  | #endif // USE(JSVALUE64) | 
|  | } else { | 
|  | flushRegisters(); | 
|  |  | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | callOperation(operationSpreadGeneric, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), argument); | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNewArray(Node* node) | 
|  | { | 
|  | JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic); | 
|  | RegisteredStructure structure = m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(node->indexingType())); | 
|  | if (!globalObject->isHavingABadTime() && !hasAnyArrayStorage(node->indexingType())) { | 
|  | unsigned numElements = node->numChildren(); | 
|  | unsigned vectorLengthHint = node->vectorLengthHint(); | 
|  | ASSERT(vectorLengthHint >= numElements); | 
|  |  | 
|  | // Because we first speculate on all of the children here, we can never exit after creating | 
|  | // uninitialized contiguous JSArray, which ensures that we will never produce a half-baked JSArray. | 
|  | for (unsigned operandIndex = 0; operandIndex < node->numChildren(); ++operandIndex) | 
|  | speculate(node, m_jit.graph().varArgChild(node, operandIndex)); | 
|  |  | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary storage(this); | 
|  |  | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg storageGPR = storage.gpr(); | 
|  |  | 
|  | emitAllocateRawObject(resultGPR, structure, storageGPR, numElements, vectorLengthHint); | 
|  |  | 
|  | // At this point, one way or another, resultGPR and storageGPR have pointers to | 
|  | // the JSArray and the Butterfly, respectively. | 
|  |  | 
|  | ASSERT(!hasUndecided(structure->indexingType()) || !node->numChildren()); | 
|  |  | 
|  | for (unsigned operandIndex = 0; operandIndex < node->numChildren(); ++operandIndex) { | 
|  | Edge use = m_jit.graph().varArgChild(node, operandIndex); | 
|  | switch (node->indexingType()) { | 
|  | case ALL_BLANK_INDEXING_TYPES: | 
|  | case ALL_UNDECIDED_INDEXING_TYPES: | 
|  | CRASH(); | 
|  | break; | 
|  | case ALL_DOUBLE_INDEXING_TYPES: { | 
|  | SpeculateDoubleOperand operand(this, use); | 
|  | FPRReg opFPR = operand.fpr(); | 
|  | m_jit.storeDouble(opFPR, MacroAssembler::Address(storageGPR, sizeof(double) * operandIndex)); | 
|  | break; | 
|  | } | 
|  | case ALL_INT32_INDEXING_TYPES: | 
|  | case ALL_CONTIGUOUS_INDEXING_TYPES: { | 
|  | JSValueOperand operand(this, use, ManualOperandSpeculation); | 
|  | JSValueRegs operandRegs = operand.jsValueRegs(); | 
|  | m_jit.storeValue(operandRegs, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIndex)); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | CRASH(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Yuck, we should *really* have a way of also returning the storageGPR. But | 
|  | // that's the least of what's wrong with this code. We really shouldn't be | 
|  | // allocating the array after having computed - and probably spilled to the | 
|  | // stack - all of the things that will go into the array. The solution to that | 
|  | // bigger problem will also likely fix the redundancy in reloading the storage | 
|  | // pointer that we currently have. | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!node->numChildren()) { | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | callOperation(operationNewEmptyArray, result.gpr(), &vm(), structure); | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(result.gpr(), node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | size_t scratchSize = sizeof(EncodedJSValue) * node->numChildren(); | 
|  | ScratchBuffer* scratchBuffer = vm().scratchBufferForSize(scratchSize); | 
|  | EncodedJSValue* buffer = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : nullptr; | 
|  |  | 
|  | for (unsigned operandIdx = 0; operandIdx < node->numChildren(); ++operandIdx) { | 
|  | // Need to perform the speculations that this node promises to perform. If we're | 
|  | // emitting code here and the indexing type is not array storage then there is | 
|  | // probably something hilarious going on and we're already failing at all the | 
|  | // things, but at least we're going to be sound. | 
|  | Edge use = m_jit.graph().m_varArgChildren[node->firstChild() + operandIdx]; | 
|  | switch (node->indexingType()) { | 
|  | case ALL_BLANK_INDEXING_TYPES: | 
|  | case ALL_UNDECIDED_INDEXING_TYPES: | 
|  | CRASH(); | 
|  | break; | 
|  | case ALL_DOUBLE_INDEXING_TYPES: { | 
|  | SpeculateDoubleOperand operand(this, use); | 
|  | FPRReg opFPR = operand.fpr(); | 
|  | DFG_TYPE_CHECK( | 
|  | JSValueRegs(), use, SpecDoubleReal, | 
|  | m_jit.branchIfNaN(opFPR)); | 
|  | #if USE(JSVALUE64) | 
|  | JSValueRegsTemporary scratch(this); | 
|  | JSValueRegs scratchRegs = scratch.regs(); | 
|  | m_jit.boxDouble(opFPR, scratchRegs); | 
|  | m_jit.storeValue(scratchRegs, buffer + operandIdx); | 
|  | #else | 
|  | m_jit.storeDouble(opFPR, TrustedImmPtr(buffer + operandIdx)); | 
|  | #endif | 
|  | operand.use(); | 
|  | break; | 
|  | } | 
|  | case ALL_INT32_INDEXING_TYPES: | 
|  | case ALL_CONTIGUOUS_INDEXING_TYPES: | 
|  | case ALL_ARRAY_STORAGE_INDEXING_TYPES: { | 
|  | JSValueOperand operand(this, use, ManualOperandSpeculation); | 
|  | JSValueRegs operandRegs = operand.jsValueRegs(); | 
|  | if (hasInt32(node->indexingType())) { | 
|  | DFG_TYPE_CHECK( | 
|  | operandRegs, use, SpecInt32Only, | 
|  | m_jit.branchIfNotInt32(operandRegs)); | 
|  | } | 
|  | m_jit.storeValue(operandRegs, buffer + operandIdx); | 
|  | operand.use(); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | CRASH(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | if (scratchSize) { | 
|  | GPRTemporary scratch(this); | 
|  |  | 
|  | // Tell GC mark phase how much of the scratch buffer is active during call. | 
|  | m_jit.move(TrustedImmPtr(scratchBuffer->addressOfActiveLength()), scratch.gpr()); | 
|  | m_jit.storePtr(TrustedImmPtr(scratchSize), scratch.gpr()); | 
|  | } | 
|  |  | 
|  | GPRFlushedCallResult result(this); | 
|  |  | 
|  | callOperation( | 
|  | operationNewArray, result.gpr(), TrustedImmPtr::weakPointer(m_graph, globalObject), m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(node->indexingType())), | 
|  | static_cast<void*>(buffer), size_t(node->numChildren())); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | if (scratchSize) { | 
|  | GPRTemporary scratch(this); | 
|  |  | 
|  | m_jit.move(TrustedImmPtr(scratchBuffer->addressOfActiveLength()), scratch.gpr()); | 
|  | m_jit.storePtr(TrustedImmPtr(nullptr), scratch.gpr()); | 
|  | } | 
|  |  | 
|  | cellResult(result.gpr(), node, UseChildrenCalledExplicitly); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNewArrayWithSpread(Node* node) | 
|  | { | 
|  | ASSERT(node->op() == NewArrayWithSpread); | 
|  | JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic); | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | if (m_jit.graph().isWatchingHavingABadTimeWatchpoint(node)) { | 
|  | GPRTemporary result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | BitVector* bitVector = node->bitVector(); | 
|  |  | 
|  | if (node->numChildren() == 1 && bitVector->get(0)) { | 
|  | Edge use = m_jit.graph().varArgChild(node, 0); | 
|  | SpeculateCellOperand immutableButterfly(this, use); | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary butterfly(this); | 
|  | GPRTemporary scratch1(this); | 
|  | GPRTemporary scratch2(this); | 
|  |  | 
|  | GPRReg immutableButterflyGPR = immutableButterfly.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg butterflyGPR = butterfly.gpr(); | 
|  | GPRReg scratch1GPR = scratch1.gpr(); | 
|  | GPRReg scratch2GPR = scratch2.gpr(); | 
|  |  | 
|  | RegisteredStructure structure = m_jit.graph().registerStructure(globalObject->originalArrayStructureForIndexingType(CopyOnWriteArrayWithContiguous)); | 
|  |  | 
|  | MacroAssembler::JumpList slowCases; | 
|  |  | 
|  | m_jit.move(immutableButterflyGPR, butterflyGPR); | 
|  | m_jit.addPtr(TrustedImm32(JSImmutableButterfly::offsetOfData()), butterflyGPR); | 
|  |  | 
|  | emitAllocateJSObject<JSArray>(resultGPR, TrustedImmPtr(structure), butterflyGPR, scratch1GPR, scratch2GPR, slowCases); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowCases, this, operationNewArrayBuffer, resultGPR, &vm(), structure, immutableButterflyGPR)); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | { | 
|  | unsigned startLength = 0; | 
|  | for (unsigned i = 0; i < node->numChildren(); ++i) { | 
|  | if (!bitVector->get(i)) | 
|  | ++startLength; | 
|  | } | 
|  |  | 
|  | GPRTemporary length(this); | 
|  | GPRReg lengthGPR = length.gpr(); | 
|  | m_jit.move(TrustedImm32(startLength), lengthGPR); | 
|  |  | 
|  | for (unsigned i = 0; i < node->numChildren(); ++i) { | 
|  | if (bitVector->get(i)) { | 
|  | Edge use = m_jit.graph().varArgChild(node, i); | 
|  | SpeculateCellOperand immutableButterfly(this, use); | 
|  | GPRReg immutableButterflyGPR = immutableButterfly.gpr(); | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, m_jit.branchAdd32(MacroAssembler::Overflow, MacroAssembler::Address(immutableButterflyGPR, JSImmutableButterfly::offsetOfPublicLength()), lengthGPR)); | 
|  | } | 
|  | } | 
|  |  | 
|  | speculationCheck(Overflow, JSValueRegs(), nullptr, m_jit.branch32(MacroAssembler::AboveOrEqual, lengthGPR, TrustedImm32(MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH))); | 
|  |  | 
|  | // We can tell compileAllocateNewArrayWithSize() that it does not need to | 
|  | // check for large arrays and use ArrayStorage structure because we already | 
|  | // ensured above that the spread array length will definitely fit in a | 
|  | // non-ArrayStorage shaped array. | 
|  | bool shouldAllowForArrayStorageStructureForLargeArrays = false; | 
|  | compileAllocateNewArrayWithSize(globalObject, resultGPR, lengthGPR, ArrayWithContiguous, shouldAllowForArrayStorageStructureForLargeArrays); | 
|  | } | 
|  |  | 
|  | GPRTemporary index(this); | 
|  | GPRReg indexGPR = index.gpr(); | 
|  |  | 
|  | GPRTemporary storage(this); | 
|  | GPRReg storageGPR = storage.gpr(); | 
|  |  | 
|  | m_jit.move(TrustedImm32(0), indexGPR); | 
|  | m_jit.loadPtr(MacroAssembler::Address(resultGPR, JSObject::butterflyOffset()), storageGPR); | 
|  |  | 
|  | for (unsigned i = 0; i < node->numChildren(); ++i) { | 
|  | Edge use = m_jit.graph().varArgChild(node, i); | 
|  | if (bitVector->get(i)) { | 
|  | SpeculateCellOperand immutableButterfly(this, use); | 
|  | GPRReg immutableButterflyGPR = immutableButterfly.gpr(); | 
|  |  | 
|  | GPRTemporary immutableButterflyIndex(this); | 
|  | GPRReg immutableButterflyIndexGPR = immutableButterflyIndex.gpr(); | 
|  |  | 
|  | GPRTemporary item(this); | 
|  | GPRReg itemGPR = item.gpr(); | 
|  |  | 
|  | GPRTemporary immutableButterflyLength(this); | 
|  | GPRReg immutableButterflyLengthGPR = immutableButterflyLength.gpr(); | 
|  |  | 
|  | m_jit.load32(MacroAssembler::Address(immutableButterflyGPR, JSImmutableButterfly::offsetOfPublicLength()), immutableButterflyLengthGPR); | 
|  | m_jit.move(TrustedImm32(0), immutableButterflyIndexGPR); | 
|  | auto done = m_jit.branchPtr(MacroAssembler::AboveOrEqual, immutableButterflyIndexGPR, immutableButterflyLengthGPR); | 
|  | auto loopStart = m_jit.label(); | 
|  | m_jit.load64( | 
|  | MacroAssembler::BaseIndex(immutableButterflyGPR, immutableButterflyIndexGPR, MacroAssembler::TimesEight, JSImmutableButterfly::offsetOfData()), | 
|  | itemGPR); | 
|  |  | 
|  | m_jit.store64(itemGPR, MacroAssembler::BaseIndex(storageGPR, indexGPR, MacroAssembler::TimesEight)); | 
|  | m_jit.addPtr(TrustedImm32(1), immutableButterflyIndexGPR); | 
|  | m_jit.addPtr(TrustedImm32(1), indexGPR); | 
|  | m_jit.branchPtr(MacroAssembler::Below, immutableButterflyIndexGPR, immutableButterflyLengthGPR).linkTo(loopStart, &m_jit); | 
|  |  | 
|  | done.link(&m_jit); | 
|  | } else { | 
|  | JSValueOperand item(this, use); | 
|  | GPRReg itemGPR = item.gpr(); | 
|  | m_jit.store64(itemGPR, MacroAssembler::BaseIndex(storageGPR, indexGPR, MacroAssembler::TimesEight)); | 
|  | m_jit.addPtr(TrustedImm32(1), indexGPR); | 
|  | } | 
|  | } | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  | #endif // USE(JSVALUE64) | 
|  |  | 
|  | ASSERT(node->numChildren()); | 
|  | size_t scratchSize = sizeof(EncodedJSValue) * node->numChildren(); | 
|  | ScratchBuffer* scratchBuffer = vm().scratchBufferForSize(scratchSize); | 
|  | EncodedJSValue* buffer = static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()); | 
|  |  | 
|  | BitVector* bitVector = node->bitVector(); | 
|  | for (unsigned i = 0; i < node->numChildren(); ++i) { | 
|  | Edge use = m_jit.graph().m_varArgChildren[node->firstChild() + i]; | 
|  | if (bitVector->get(i)) { | 
|  | SpeculateCellOperand immutableButterfly(this, use); | 
|  | GPRReg immutableButterflyGPR = immutableButterfly.gpr(); | 
|  | #if USE(JSVALUE64) | 
|  | m_jit.store64(immutableButterflyGPR, &buffer[i]); | 
|  | #else | 
|  | char* pointer = static_cast<char*>(static_cast<void*>(&buffer[i])); | 
|  | m_jit.store32(immutableButterflyGPR, pointer + PayloadOffset); | 
|  | m_jit.store32(TrustedImm32(JSValue::CellTag), pointer + TagOffset); | 
|  | #endif | 
|  | } else { | 
|  | JSValueOperand input(this, use); | 
|  | JSValueRegs inputRegs = input.jsValueRegs(); | 
|  | m_jit.storeValue(inputRegs, &buffer[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  | { | 
|  | GPRTemporary scratch(this); | 
|  | m_jit.move(TrustedImmPtr(scratchBuffer->addressOfActiveLength()), scratch.gpr()); | 
|  | m_jit.storePtr(TrustedImmPtr(scratchSize), MacroAssembler::Address(scratch.gpr())); | 
|  | } | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | callOperation(operationNewArrayWithSpreadSlow, resultGPR, TrustedImmPtr::weakPointer(m_graph, globalObject), buffer, node->numChildren()); | 
|  | m_jit.exceptionCheck(); | 
|  | { | 
|  | GPRTemporary scratch(this); | 
|  | m_jit.move(TrustedImmPtr(scratchBuffer->addressOfActiveLength()), scratch.gpr()); | 
|  | m_jit.storePtr(TrustedImmPtr(nullptr), MacroAssembler::Address(scratch.gpr())); | 
|  | } | 
|  |  | 
|  | cellResult(resultGPR, 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); | 
|  | strictInt32Result(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::emitPopulateSliceIndex(Edge& target, Optional<GPRReg> indexGPR, GPRReg lengthGPR, GPRReg resultGPR) | 
|  | { | 
|  | if (target->isInt32Constant()) { | 
|  | int32_t value = target->asInt32(); | 
|  | if (value == 0) { | 
|  | m_jit.move(TrustedImm32(0), resultGPR); | 
|  | return; | 
|  | } | 
|  |  | 
|  | MacroAssembler::JumpList done; | 
|  | if (value > 0) { | 
|  | m_jit.move(TrustedImm32(value), resultGPR); | 
|  | done.append(m_jit.branch32(MacroAssembler::BelowOrEqual, resultGPR, lengthGPR)); | 
|  | m_jit.move(lengthGPR, resultGPR); | 
|  | } else { | 
|  | ASSERT(value != 0); | 
|  | m_jit.move(lengthGPR, resultGPR); | 
|  | done.append(m_jit.branchAdd32(MacroAssembler::PositiveOrZero, TrustedImm32(value), resultGPR)); | 
|  | m_jit.move(TrustedImm32(0), resultGPR); | 
|  | } | 
|  | done.link(&m_jit); | 
|  | return; | 
|  | } | 
|  |  | 
|  | Optional<SpeculateInt32Operand> index; | 
|  | if (!indexGPR) { | 
|  | index.emplace(this, target); | 
|  | indexGPR = index->gpr(); | 
|  | } | 
|  | MacroAssembler::JumpList done; | 
|  |  | 
|  | auto isPositive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, indexGPR.value(), TrustedImm32(0)); | 
|  | m_jit.move(lengthGPR, resultGPR); | 
|  | done.append(m_jit.branchAdd32(MacroAssembler::PositiveOrZero, indexGPR.value(), resultGPR)); | 
|  | m_jit.move(TrustedImm32(0), resultGPR); | 
|  | done.append(m_jit.jump()); | 
|  |  | 
|  | isPositive.link(&m_jit); | 
|  | m_jit.move(indexGPR.value(), resultGPR); | 
|  | done.append(m_jit.branch32(MacroAssembler::BelowOrEqual, resultGPR, lengthGPR)); | 
|  | m_jit.move(lengthGPR, resultGPR); | 
|  |  | 
|  | done.link(&m_jit); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileArraySlice(Node* node) | 
|  | { | 
|  | ASSERT(node->op() == ArraySlice); | 
|  |  | 
|  | JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic); | 
|  |  | 
|  | GPRTemporary temp(this); | 
|  | StorageOperand storage(this, m_jit.graph().varArgChild(node, node->numChildren() - 1)); | 
|  | GPRTemporary result(this); | 
|  |  | 
|  | GPRReg storageGPR = storage.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  |  | 
|  | if (node->numChildren() == 2) | 
|  | m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), tempGPR); | 
|  | else { | 
|  | ASSERT(node->numChildren() == 3 || node->numChildren() == 4); | 
|  | GPRTemporary tempLength(this); | 
|  | GPRReg lengthGPR = tempLength.gpr(); | 
|  | m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), lengthGPR); | 
|  |  | 
|  | if (node->numChildren() == 4) | 
|  | emitPopulateSliceIndex(m_jit.graph().varArgChild(node, 2), WTF::nullopt, lengthGPR, tempGPR); | 
|  | else | 
|  | m_jit.move(lengthGPR, tempGPR); | 
|  |  | 
|  | if (m_jit.graph().varArgChild(node, 1)->isInt32Constant() && m_jit.graph().varArgChild(node, 1)->asInt32() == 0) { | 
|  | // Do nothing for array.slice(0, end) or array.slice(0) cases. | 
|  | // `tempGPR` already points to the size of a newly created array. | 
|  | } else { | 
|  | GPRTemporary tempStartIndex(this); | 
|  | GPRReg startGPR = tempStartIndex.gpr(); | 
|  | emitPopulateSliceIndex(m_jit.graph().varArgChild(node, 1), WTF::nullopt, lengthGPR, startGPR); | 
|  |  | 
|  | auto tooBig = m_jit.branch32(MacroAssembler::Above, startGPR, tempGPR); | 
|  | m_jit.sub32(startGPR, tempGPR); // the size of the array we'll make. | 
|  | auto done = m_jit.jump(); | 
|  |  | 
|  | tooBig.link(&m_jit); | 
|  | m_jit.move(TrustedImm32(0), tempGPR); | 
|  | done.link(&m_jit); | 
|  | } | 
|  | } | 
|  |  | 
|  | GPRTemporary temp3(this); | 
|  | GPRReg tempValue = temp3.gpr(); | 
|  |  | 
|  | { | 
|  | // We need to keep the source array alive at least until after we're done | 
|  | // with anything that can GC (e.g. allocating the result array below). | 
|  | SpeculateCellOperand cell(this, m_jit.graph().varArgChild(node, 0)); | 
|  |  | 
|  | m_jit.load8(MacroAssembler::Address(cell.gpr(), JSCell::indexingTypeAndMiscOffset()), tempValue); | 
|  | // We can ignore the writability of the cell since we won't write to the source. | 
|  | m_jit.and32(TrustedImm32(AllWritableArrayTypesAndHistory), tempValue); | 
|  |  | 
|  | JSValueRegsTemporary emptyValue(this); | 
|  | JSValueRegs emptyValueRegs = emptyValue.regs(); | 
|  |  | 
|  | GPRTemporary storage(this); | 
|  | GPRReg storageResultGPR = storage.gpr(); | 
|  |  | 
|  | GPRReg sizeGPR = tempGPR; | 
|  |  | 
|  | CCallHelpers::JumpList done; | 
|  |  | 
|  | auto emitMoveEmptyValue = [&] (JSValue v) { | 
|  | m_jit.moveValue(v, emptyValueRegs); | 
|  | }; | 
|  |  | 
|  | auto isContiguous = m_jit.branch32(MacroAssembler::Equal, tempValue, TrustedImm32(ArrayWithContiguous)); | 
|  | auto isInt32 = m_jit.branch32(MacroAssembler::Equal, tempValue, TrustedImm32(ArrayWithInt32)); | 
|  | // When we emit an ArraySlice, we dominate the use of the array by a CheckStructure | 
|  | // to ensure the incoming array is one to be one of the original array structures | 
|  | // with one of the following indexing shapes: Int32, Contiguous, Double. Therefore, | 
|  | // we're a double array here. | 
|  | m_jit.move(TrustedImmPtr(m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithDouble))), tempValue); | 
|  | emitMoveEmptyValue(jsNaN()); | 
|  | done.append(m_jit.jump()); | 
|  |  | 
|  | isContiguous.link(&m_jit); | 
|  | m_jit.move(TrustedImmPtr(m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous))), tempValue); | 
|  | emitMoveEmptyValue(JSValue()); | 
|  | done.append(m_jit.jump()); | 
|  |  | 
|  | isInt32.link(&m_jit); | 
|  | m_jit.move(TrustedImmPtr(m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithInt32))), tempValue); | 
|  | emitMoveEmptyValue(JSValue()); | 
|  |  | 
|  | done.link(&m_jit); | 
|  |  | 
|  | MacroAssembler::JumpList slowCases; | 
|  | m_jit.move(TrustedImmPtr(nullptr), storageResultGPR); | 
|  | // Enable the fast case on 64-bit platforms, where a sufficient amount of GP registers should be available. | 
|  | // Other platforms could support the same approach with custom code, but that is not currently worth the extra code maintenance. | 
|  | if (is64Bit()) { | 
|  | GPRTemporary scratch(this); | 
|  | GPRTemporary scratch2(this); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  | GPRReg scratch2GPR = scratch2.gpr(); | 
|  |  | 
|  | emitAllocateButterfly(storageResultGPR, sizeGPR, scratchGPR, scratch2GPR, resultGPR, slowCases); | 
|  | emitInitializeButterfly(storageResultGPR, sizeGPR, emptyValueRegs, scratchGPR); | 
|  | emitAllocateJSObject<JSArray>(resultGPR, tempValue, storageResultGPR, scratchGPR, scratch2GPR, slowCases); | 
|  | m_jit.mutatorFence(vm()); | 
|  | } else { | 
|  | slowCases.append(m_jit.jump()); | 
|  | } | 
|  |  | 
|  | addSlowPathGenerator(makeUnique<CallArrayAllocatorWithVariableStructureVariableSizeSlowPathGenerator>( | 
|  | slowCases, this, operationNewArrayWithSize, resultGPR, TrustedImmPtr::weakPointer(m_graph, globalObject), tempValue, sizeGPR, storageResultGPR)); | 
|  | } | 
|  |  | 
|  | GPRTemporary temp4(this); | 
|  | GPRReg loadIndex = temp4.gpr(); | 
|  |  | 
|  | if (node->numChildren() == 2) { | 
|  | m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), tempGPR); | 
|  | m_jit.move(TrustedImm32(0), loadIndex); | 
|  | } else { | 
|  | m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), tempValue); | 
|  | if (node->numChildren() == 4) | 
|  | emitPopulateSliceIndex(m_jit.graph().varArgChild(node, 2), WTF::nullopt, tempValue, tempGPR); | 
|  | else | 
|  | m_jit.move(tempValue, tempGPR); | 
|  | emitPopulateSliceIndex(m_jit.graph().varArgChild(node, 1), WTF::nullopt, tempValue, loadIndex); | 
|  | } | 
|  |  | 
|  | GPRTemporary temp5(this); | 
|  | GPRReg storeIndex = temp5.gpr(); | 
|  | m_jit.move(TrustedImmPtr(nullptr), storeIndex); | 
|  |  | 
|  | GPRTemporary temp2(this); | 
|  | GPRReg resultButterfly = temp2.gpr(); | 
|  |  | 
|  | m_jit.loadPtr(MacroAssembler::Address(resultGPR, JSObject::butterflyOffset()), resultButterfly); | 
|  | m_jit.zeroExtend32ToWord(tempGPR, tempGPR); | 
|  | m_jit.zeroExtend32ToWord(loadIndex, loadIndex); | 
|  | auto done = m_jit.branchPtr(MacroAssembler::AboveOrEqual, loadIndex, tempGPR); | 
|  |  | 
|  | auto loop = m_jit.label(); | 
|  | #if USE(JSVALUE64) | 
|  | m_jit.load64( | 
|  | MacroAssembler::BaseIndex(storageGPR, loadIndex, MacroAssembler::TimesEight), tempValue); | 
|  | m_jit.store64( | 
|  | tempValue, MacroAssembler::BaseIndex(resultButterfly, storeIndex, MacroAssembler::TimesEight)); | 
|  | #else | 
|  | m_jit.load32( | 
|  | MacroAssembler::BaseIndex(storageGPR, loadIndex, MacroAssembler::TimesEight, PayloadOffset), tempValue); | 
|  | m_jit.store32( | 
|  | tempValue, MacroAssembler::BaseIndex(resultButterfly, storeIndex, MacroAssembler::TimesEight, PayloadOffset)); | 
|  | m_jit.load32( | 
|  | MacroAssembler::BaseIndex(storageGPR, loadIndex, MacroAssembler::TimesEight, TagOffset), tempValue); | 
|  | m_jit.store32( | 
|  | tempValue, MacroAssembler::BaseIndex(resultButterfly, storeIndex, MacroAssembler::TimesEight, TagOffset)); | 
|  | #endif // USE(JSVALUE64) | 
|  | m_jit.addPtr(TrustedImm32(1), loadIndex); | 
|  | m_jit.addPtr(TrustedImm32(1), storeIndex); | 
|  | m_jit.branchPtr(MacroAssembler::Below, loadIndex, tempGPR).linkTo(loop, &m_jit); | 
|  |  | 
|  | done.link(&m_jit); | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileArrayIndexOf(Node* node) | 
|  | { | 
|  | ASSERT(node->op() == ArrayIndexOf); | 
|  |  | 
|  | StorageOperand storage(this, m_jit.graph().varArgChild(node, node->numChildren() == 3 ? 2 : 3)); | 
|  | GPRTemporary index(this); | 
|  | GPRTemporary tempLength(this); | 
|  |  | 
|  | GPRReg storageGPR = storage.gpr(); | 
|  | GPRReg indexGPR = index.gpr(); | 
|  | GPRReg lengthGPR = tempLength.gpr(); | 
|  |  | 
|  | m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), lengthGPR); | 
|  |  | 
|  | if (node->numChildren() == 4) | 
|  | emitPopulateSliceIndex(m_jit.graph().varArgChild(node, 2), WTF::nullopt, lengthGPR, indexGPR); | 
|  | else | 
|  | m_jit.move(TrustedImm32(0), indexGPR); | 
|  |  | 
|  | Edge& searchElementEdge = m_jit.graph().varArgChild(node, 1); | 
|  | switch (searchElementEdge.useKind()) { | 
|  | case Int32Use: | 
|  | case ObjectUse: | 
|  | case SymbolUse: | 
|  | case OtherUse: { | 
|  | auto emitLoop = [&] (auto emitCompare) { | 
|  | #if ENABLE(DFG_REGISTER_ALLOCATION_VALIDATION) | 
|  | m_jit.clearRegisterAllocationOffsets(); | 
|  | #endif | 
|  |  | 
|  | m_jit.zeroExtend32ToWord(lengthGPR, lengthGPR); | 
|  | m_jit.zeroExtend32ToWord(indexGPR, indexGPR); | 
|  |  | 
|  | auto loop = m_jit.label(); | 
|  | auto notFound = m_jit.branch32(CCallHelpers::Equal, indexGPR, lengthGPR); | 
|  |  | 
|  | auto found = emitCompare(); | 
|  |  | 
|  | m_jit.add32(TrustedImm32(1), indexGPR); | 
|  | m_jit.jump().linkTo(loop, &m_jit); | 
|  |  | 
|  | notFound.link(&m_jit); | 
|  | m_jit.move(TrustedImm32(-1), indexGPR); | 
|  | found.link(&m_jit); | 
|  | strictInt32Result(indexGPR, node); | 
|  | }; | 
|  |  | 
|  | if (searchElementEdge.useKind() == Int32Use) { | 
|  | ASSERT(node->arrayMode().type() == Array::Int32); | 
|  | #if USE(JSVALUE64) | 
|  | JSValueOperand searchElement(this, searchElementEdge, ManualOperandSpeculation); | 
|  | JSValueRegs searchElementRegs = searchElement.jsValueRegs(); | 
|  | speculateInt32(searchElementEdge, searchElementRegs); | 
|  | GPRReg searchElementGPR = searchElementRegs.payloadGPR(); | 
|  | #else | 
|  | SpeculateInt32Operand searchElement(this, searchElementEdge); | 
|  | GPRReg searchElementGPR = searchElement.gpr(); | 
|  |  | 
|  | GPRTemporary temp(this); | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  | #endif | 
|  | emitLoop([&] () { | 
|  | #if USE(JSVALUE64) | 
|  | auto found = m_jit.branch64(CCallHelpers::Equal, MacroAssembler::BaseIndex(storageGPR, indexGPR, MacroAssembler::TimesEight), searchElementGPR); | 
|  | #else | 
|  | auto skip = m_jit.branch32(CCallHelpers::NotEqual, MacroAssembler::BaseIndex(storageGPR, indexGPR, MacroAssembler::TimesEight, TagOffset), TrustedImm32(JSValue::Int32Tag)); | 
|  | m_jit.load32(MacroAssembler::BaseIndex(storageGPR, indexGPR, MacroAssembler::TimesEight, PayloadOffset), tempGPR); | 
|  | auto found = m_jit.branch32(CCallHelpers::Equal, tempGPR, searchElementGPR); | 
|  | skip.link(&m_jit); | 
|  | #endif | 
|  | return found; | 
|  | }); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (searchElementEdge.useKind() == OtherUse) { | 
|  | ASSERT(node->arrayMode().type() == Array::Contiguous); | 
|  | JSValueOperand searchElement(this, searchElementEdge, ManualOperandSpeculation); | 
|  | GPRTemporary temp(this); | 
|  |  | 
|  | JSValueRegs searchElementRegs = searchElement.jsValueRegs(); | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  | speculateOther(searchElementEdge, searchElementRegs, tempGPR); | 
|  |  | 
|  | emitLoop([&] () { | 
|  | #if USE(JSVALUE64) | 
|  | auto found = m_jit.branch64(CCallHelpers::Equal, MacroAssembler::BaseIndex(storageGPR, indexGPR, MacroAssembler::TimesEight), searchElementRegs.payloadGPR()); | 
|  | #else | 
|  | m_jit.load32(MacroAssembler::BaseIndex(storageGPR, indexGPR, MacroAssembler::TimesEight, TagOffset), tempGPR); | 
|  | auto found = m_jit.branch32(CCallHelpers::Equal, tempGPR, searchElementRegs.tagGPR()); | 
|  | #endif | 
|  | return found; | 
|  | }); | 
|  | return; | 
|  | } | 
|  |  | 
|  | ASSERT(node->arrayMode().type() == Array::Contiguous); | 
|  | SpeculateCellOperand searchElement(this, searchElementEdge); | 
|  | GPRReg searchElementGPR = searchElement.gpr(); | 
|  |  | 
|  | if (searchElementEdge.useKind() == ObjectUse) | 
|  | speculateObject(searchElementEdge, searchElementGPR); | 
|  | else { | 
|  | ASSERT(searchElementEdge.useKind() == SymbolUse); | 
|  | speculateSymbol(searchElementEdge, searchElementGPR); | 
|  | } | 
|  |  | 
|  | #if USE(JSVALUE32_64) | 
|  | GPRTemporary temp(this); | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  | #endif | 
|  |  | 
|  | emitLoop([&] () { | 
|  | #if USE(JSVALUE64) | 
|  | auto found = m_jit.branch64(CCallHelpers::Equal, MacroAssembler::BaseIndex(storageGPR, indexGPR, MacroAssembler::TimesEight), searchElementGPR); | 
|  | #else | 
|  | auto skip = m_jit.branch32(CCallHelpers::NotEqual, MacroAssembler::BaseIndex(storageGPR, indexGPR, MacroAssembler::TimesEight, TagOffset), TrustedImm32(JSValue::CellTag)); | 
|  | m_jit.load32(MacroAssembler::BaseIndex(storageGPR, indexGPR, MacroAssembler::TimesEight, PayloadOffset), tempGPR); | 
|  | auto found = m_jit.branch32(CCallHelpers::Equal, tempGPR, searchElementGPR); | 
|  | skip.link(&m_jit); | 
|  | #endif | 
|  | return found; | 
|  | }); | 
|  | return; | 
|  | } | 
|  |  | 
|  | case DoubleRepUse: { | 
|  | ASSERT(node->arrayMode().type() == Array::Double); | 
|  | SpeculateDoubleOperand searchElement(this, searchElementEdge); | 
|  | FPRTemporary tempDouble(this); | 
|  |  | 
|  | FPRReg searchElementFPR = searchElement.fpr(); | 
|  | FPRReg tempFPR = tempDouble.fpr(); | 
|  |  | 
|  | #if ENABLE(DFG_REGISTER_ALLOCATION_VALIDATION) | 
|  | m_jit.clearRegisterAllocationOffsets(); | 
|  | #endif | 
|  |  | 
|  | m_jit.zeroExtend32ToWord(lengthGPR, lengthGPR); | 
|  | m_jit.zeroExtend32ToWord(indexGPR, indexGPR); | 
|  |  | 
|  | auto loop = m_jit.label(); | 
|  | auto notFound = m_jit.branch32(CCallHelpers::Equal, indexGPR, lengthGPR); | 
|  | m_jit.loadDouble(MacroAssembler::BaseIndex(storageGPR, indexGPR, MacroAssembler::TimesEight), tempFPR); | 
|  | auto found = m_jit.branchDouble(CCallHelpers::DoubleEqualAndOrdered, tempFPR, searchElementFPR); | 
|  | m_jit.add32(TrustedImm32(1), indexGPR); | 
|  | m_jit.jump().linkTo(loop, &m_jit); | 
|  |  | 
|  | notFound.link(&m_jit); | 
|  | m_jit.move(TrustedImm32(-1), indexGPR); | 
|  | found.link(&m_jit); | 
|  | strictInt32Result(indexGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | case StringUse: { | 
|  | ASSERT(node->arrayMode().type() == Array::Contiguous); | 
|  | SpeculateCellOperand searchElement(this, searchElementEdge); | 
|  |  | 
|  | GPRReg searchElementGPR = searchElement.gpr(); | 
|  |  | 
|  | speculateString(searchElementEdge, searchElementGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | callOperation(operationArrayIndexOfString, lengthGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), storageGPR, searchElementGPR, indexGPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | strictInt32Result(lengthGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | case UntypedUse: { | 
|  | JSValueOperand searchElement(this, searchElementEdge); | 
|  |  | 
|  | JSValueRegs searchElementRegs = searchElement.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | switch (node->arrayMode().type()) { | 
|  | case Array::Double: | 
|  | callOperation(operationArrayIndexOfValueDouble, lengthGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), storageGPR, searchElementRegs, indexGPR); | 
|  | break; | 
|  | case Array::Int32: | 
|  | case Array::Contiguous: | 
|  | callOperation(operationArrayIndexOfValueInt32OrContiguous, lengthGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), storageGPR, searchElementRegs, indexGPR); | 
|  | break; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | strictInt32Result(lengthGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileArrayPush(Node* node) | 
|  | { | 
|  | ASSERT(node->arrayMode().isJSArray()); | 
|  |  | 
|  | Edge& storageEdge = m_jit.graph().varArgChild(node, 0); | 
|  | Edge& arrayEdge = m_jit.graph().varArgChild(node, 1); | 
|  |  | 
|  | SpeculateCellOperand base(this, arrayEdge); | 
|  | GPRTemporary storageLength(this); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | GPRReg storageLengthGPR = storageLength.gpr(); | 
|  |  | 
|  | StorageOperand storage(this, storageEdge); | 
|  | GPRReg storageGPR = storage.gpr(); | 
|  | unsigned elementOffset = 2; | 
|  | unsigned elementCount = node->numChildren() - elementOffset; | 
|  |  | 
|  | #if USE(JSVALUE32_64) | 
|  | GPRTemporary tag(this); | 
|  | GPRReg tagGPR = tag.gpr(); | 
|  | JSValueRegs resultRegs { tagGPR, storageLengthGPR }; | 
|  | #else | 
|  | JSValueRegs resultRegs { storageLengthGPR }; | 
|  | #endif | 
|  |  | 
|  | auto getStorageBufferAddress = [&] (GPRReg storageGPR, GPRReg indexGPR, int32_t offset, GPRReg bufferGPR) { | 
|  | static_assert(sizeof(JSValue) == 8 && 1 << 3 == 8, "This is strongly assumed in the code below."); | 
|  | m_jit.getEffectiveAddress(MacroAssembler::BaseIndex(storageGPR, indexGPR, MacroAssembler::TimesEight, offset), bufferGPR); | 
|  | }; | 
|  |  | 
|  | switch (node->arrayMode().type()) { | 
|  | case Array::Int32: | 
|  | case Array::Contiguous: { | 
|  | if (elementCount == 1) { | 
|  | Edge& element = m_jit.graph().varArgChild(node, elementOffset); | 
|  | if (node->arrayMode().type() == Array::Int32) { | 
|  | ASSERT(element.useKind() == Int32Use); | 
|  | speculateInt32(element); | 
|  | } | 
|  | JSValueOperand value(this, element, ManualOperandSpeculation); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  |  | 
|  | m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR); | 
|  | MacroAssembler::Jump slowPath = m_jit.branch32(MacroAssembler::AboveOrEqual, storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength())); | 
|  | m_jit.storeValue(valueRegs, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight)); | 
|  | m_jit.add32(TrustedImm32(1), storageLengthGPR); | 
|  | m_jit.store32(storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); | 
|  | m_jit.boxInt32(storageLengthGPR, resultRegs); | 
|  |  | 
|  | addSlowPathGenerator( | 
|  | slowPathCall(slowPath, this, operationArrayPush, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), valueRegs, baseGPR)); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (node->arrayMode().type() == Array::Int32) { | 
|  | for (unsigned elementIndex = 0; elementIndex < elementCount; ++elementIndex) { | 
|  | Edge element = m_jit.graph().varArgChild(node, elementIndex + elementOffset); | 
|  | ASSERT(element.useKind() == Int32Use); | 
|  | speculateInt32(element); | 
|  | } | 
|  | } | 
|  |  | 
|  | GPRTemporary buffer(this); | 
|  | GPRReg bufferGPR = buffer.gpr(); | 
|  |  | 
|  | m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR); | 
|  | m_jit.move(storageLengthGPR, bufferGPR); | 
|  | m_jit.add32(TrustedImm32(elementCount), bufferGPR); | 
|  | MacroAssembler::Jump slowPath = m_jit.branch32(MacroAssembler::Above, bufferGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength())); | 
|  |  | 
|  | m_jit.store32(bufferGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); | 
|  | getStorageBufferAddress(storageGPR, storageLengthGPR, 0, bufferGPR); | 
|  | m_jit.add32(TrustedImm32(elementCount), storageLengthGPR); | 
|  | m_jit.boxInt32(storageLengthGPR, resultRegs); | 
|  | auto storageDone = m_jit.jump(); | 
|  |  | 
|  | slowPath.link(&m_jit); | 
|  |  | 
|  | size_t scratchSize = sizeof(EncodedJSValue) * elementCount; | 
|  | ScratchBuffer* scratchBuffer = vm().scratchBufferForSize(scratchSize); | 
|  | m_jit.move(TrustedImmPtr(static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer())), bufferGPR); | 
|  | m_jit.move(TrustedImmPtr(scratchBuffer->addressOfActiveLength()), storageLengthGPR); | 
|  | m_jit.storePtr(TrustedImmPtr(scratchSize), MacroAssembler::Address(storageLengthGPR)); | 
|  |  | 
|  | storageDone.link(&m_jit); | 
|  | for (unsigned elementIndex = 0; elementIndex < elementCount; ++elementIndex) { | 
|  | Edge& element = m_jit.graph().varArgChild(node, elementIndex + elementOffset); | 
|  | JSValueOperand value(this, element, ManualOperandSpeculation); // We did type checks above. | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  |  | 
|  | m_jit.storeValue(valueRegs, MacroAssembler::Address(bufferGPR, sizeof(EncodedJSValue) * elementIndex)); | 
|  | value.use(); | 
|  | } | 
|  |  | 
|  | MacroAssembler::Jump fastPath = m_jit.branchPtr(MacroAssembler::NotEqual, bufferGPR, TrustedImmPtr(static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()))); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(m_jit.jump(), this, operationArrayPushMultiple, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, bufferGPR, TrustedImm32(elementCount))); | 
|  |  | 
|  | m_jit.move(TrustedImmPtr(scratchBuffer->addressOfActiveLength()), bufferGPR); | 
|  | m_jit.storePtr(TrustedImmPtr(nullptr), MacroAssembler::Address(bufferGPR)); | 
|  |  | 
|  | base.use(); | 
|  | storage.use(); | 
|  |  | 
|  | fastPath.link(&m_jit); | 
|  | jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly); | 
|  | return; | 
|  | } | 
|  |  | 
|  | case Array::Double: { | 
|  | if (elementCount == 1) { | 
|  | Edge& element = m_jit.graph().varArgChild(node, elementOffset); | 
|  | speculate(node, element); | 
|  | SpeculateDoubleOperand value(this, element); | 
|  | FPRReg valueFPR = value.fpr(); | 
|  |  | 
|  | m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR); | 
|  | MacroAssembler::Jump slowPath = m_jit.branch32(MacroAssembler::AboveOrEqual, storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength())); | 
|  | m_jit.storeDouble(valueFPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight)); | 
|  | m_jit.add32(TrustedImm32(1), storageLengthGPR); | 
|  | m_jit.store32(storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); | 
|  | m_jit.boxInt32(storageLengthGPR, resultRegs); | 
|  |  | 
|  | addSlowPathGenerator( | 
|  | slowPathCall(slowPath, this, operationArrayPushDouble, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), valueFPR, baseGPR)); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (unsigned elementIndex = 0; elementIndex < elementCount; ++elementIndex) { | 
|  | Edge element = m_jit.graph().varArgChild(node, elementIndex + elementOffset); | 
|  | ASSERT(element.useKind() == DoubleRepRealUse); | 
|  | speculate(node, element); | 
|  | } | 
|  |  | 
|  | GPRTemporary buffer(this); | 
|  | GPRReg bufferGPR = buffer.gpr(); | 
|  |  | 
|  | m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR); | 
|  | m_jit.move(storageLengthGPR, bufferGPR); | 
|  | m_jit.add32(TrustedImm32(elementCount), bufferGPR); | 
|  | MacroAssembler::Jump slowPath = m_jit.branch32(MacroAssembler::Above, bufferGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength())); | 
|  |  | 
|  | m_jit.store32(bufferGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); | 
|  | getStorageBufferAddress(storageGPR, storageLengthGPR, 0, bufferGPR); | 
|  | m_jit.add32(TrustedImm32(elementCount), storageLengthGPR); | 
|  | m_jit.boxInt32(storageLengthGPR, resultRegs); | 
|  | auto storageDone = m_jit.jump(); | 
|  |  | 
|  | slowPath.link(&m_jit); | 
|  |  | 
|  | size_t scratchSize = sizeof(double) * elementCount; | 
|  | ScratchBuffer* scratchBuffer = vm().scratchBufferForSize(scratchSize); | 
|  | m_jit.move(TrustedImmPtr(static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer())), bufferGPR); | 
|  | m_jit.move(TrustedImmPtr(scratchBuffer->addressOfActiveLength()), storageLengthGPR); | 
|  | m_jit.storePtr(TrustedImmPtr(scratchSize), MacroAssembler::Address(storageLengthGPR)); | 
|  |  | 
|  | storageDone.link(&m_jit); | 
|  | for (unsigned elementIndex = 0; elementIndex < elementCount; ++elementIndex) { | 
|  | Edge& element = m_jit.graph().varArgChild(node, elementIndex + elementOffset); | 
|  | SpeculateDoubleOperand value(this, element); | 
|  | FPRReg valueFPR = value.fpr(); | 
|  |  | 
|  | m_jit.storeDouble(valueFPR, MacroAssembler::Address(bufferGPR, sizeof(double) * elementIndex)); | 
|  | value.use(); | 
|  | } | 
|  |  | 
|  | MacroAssembler::Jump fastPath = m_jit.branchPtr(MacroAssembler::NotEqual, bufferGPR, TrustedImmPtr(static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()))); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(m_jit.jump(), this, operationArrayPushDoubleMultiple, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, bufferGPR, TrustedImm32(elementCount))); | 
|  |  | 
|  | m_jit.move(TrustedImmPtr(scratchBuffer->addressOfActiveLength()), bufferGPR); | 
|  | m_jit.storePtr(TrustedImmPtr(nullptr), MacroAssembler::Address(bufferGPR)); | 
|  |  | 
|  | base.use(); | 
|  | storage.use(); | 
|  |  | 
|  | fastPath.link(&m_jit); | 
|  | jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly); | 
|  | return; | 
|  | } | 
|  |  | 
|  | case Array::ArrayStorage: { | 
|  | // This ensures that the result of ArrayPush is Int32 in AI. | 
|  | int32_t largestPositiveInt32Length = 0x7fffffff - elementCount; | 
|  | if (elementCount == 1) { | 
|  | Edge& element = m_jit.graph().varArgChild(node, elementOffset); | 
|  | JSValueOperand value(this, element); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  |  | 
|  | m_jit.load32(MacroAssembler::Address(storageGPR, ArrayStorage::lengthOffset()), storageLengthGPR); | 
|  |  | 
|  | // Refuse to handle bizarre lengths. | 
|  | speculationCheck(Uncountable, JSValueRegs(), nullptr, m_jit.branch32(MacroAssembler::Above, storageLengthGPR, TrustedImm32(largestPositiveInt32Length))); | 
|  |  | 
|  | MacroAssembler::Jump slowPath = m_jit.branch32(MacroAssembler::AboveOrEqual, storageLengthGPR, MacroAssembler::Address(storageGPR, ArrayStorage::vectorLengthOffset())); | 
|  |  | 
|  | m_jit.storeValue(valueRegs, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight, ArrayStorage::vectorOffset())); | 
|  |  | 
|  | m_jit.add32(TrustedImm32(1), storageLengthGPR); | 
|  | m_jit.store32(storageLengthGPR, MacroAssembler::Address(storageGPR, ArrayStorage::lengthOffset())); | 
|  | m_jit.add32(TrustedImm32(1), MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); | 
|  | m_jit.boxInt32(storageLengthGPR, resultRegs); | 
|  |  | 
|  | addSlowPathGenerator( | 
|  | slowPathCall(slowPath, this, operationArrayPush, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), valueRegs, baseGPR)); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | GPRTemporary buffer(this); | 
|  | GPRReg bufferGPR = buffer.gpr(); | 
|  |  | 
|  | m_jit.load32(MacroAssembler::Address(storageGPR, ArrayStorage::lengthOffset()), storageLengthGPR); | 
|  |  | 
|  | // Refuse to handle bizarre lengths. | 
|  | speculationCheck(Uncountable, JSValueRegs(), nullptr, m_jit.branch32(MacroAssembler::Above, storageLengthGPR, TrustedImm32(largestPositiveInt32Length))); | 
|  |  | 
|  | m_jit.move(storageLengthGPR, bufferGPR); | 
|  | m_jit.add32(TrustedImm32(elementCount), bufferGPR); | 
|  | MacroAssembler::Jump slowPath = m_jit.branch32(MacroAssembler::Above, bufferGPR, MacroAssembler::Address(storageGPR, ArrayStorage::vectorLengthOffset())); | 
|  |  | 
|  | m_jit.store32(bufferGPR, MacroAssembler::Address(storageGPR, ArrayStorage::lengthOffset())); | 
|  | getStorageBufferAddress(storageGPR, storageLengthGPR, ArrayStorage::vectorOffset(), bufferGPR); | 
|  | m_jit.add32(TrustedImm32(elementCount), MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); | 
|  | m_jit.add32(TrustedImm32(elementCount), storageLengthGPR); | 
|  | m_jit.boxInt32(storageLengthGPR, resultRegs); | 
|  | auto storageDone = m_jit.jump(); | 
|  |  | 
|  | slowPath.link(&m_jit); | 
|  |  | 
|  | size_t scratchSize = sizeof(EncodedJSValue) * elementCount; | 
|  | ScratchBuffer* scratchBuffer = vm().scratchBufferForSize(scratchSize); | 
|  | m_jit.move(TrustedImmPtr(static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer())), bufferGPR); | 
|  | m_jit.move(TrustedImmPtr(scratchBuffer->addressOfActiveLength()), storageLengthGPR); | 
|  | m_jit.storePtr(TrustedImmPtr(scratchSize), MacroAssembler::Address(storageLengthGPR)); | 
|  |  | 
|  | storageDone.link(&m_jit); | 
|  | for (unsigned elementIndex = 0; elementIndex < elementCount; ++elementIndex) { | 
|  | Edge& element = m_jit.graph().varArgChild(node, elementIndex + elementOffset); | 
|  | JSValueOperand value(this, element); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  |  | 
|  | m_jit.storeValue(valueRegs, MacroAssembler::Address(bufferGPR, sizeof(EncodedJSValue) * elementIndex)); | 
|  | value.use(); | 
|  | } | 
|  |  | 
|  | MacroAssembler::Jump fastPath = m_jit.branchPtr(MacroAssembler::NotEqual, bufferGPR, TrustedImmPtr(static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()))); | 
|  |  | 
|  | addSlowPathGenerator( | 
|  | slowPathCall(m_jit.jump(), this, operationArrayPushMultiple, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, bufferGPR, TrustedImm32(elementCount))); | 
|  |  | 
|  | m_jit.move(TrustedImmPtr(scratchBuffer->addressOfActiveLength()), bufferGPR); | 
|  | m_jit.storePtr(TrustedImmPtr(nullptr), MacroAssembler::Address(bufferGPR)); | 
|  |  | 
|  | base.use(); | 
|  | storage.use(); | 
|  |  | 
|  | fastPath.link(&m_jit); | 
|  | jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly); | 
|  | return; | 
|  | } | 
|  |  | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNotifyWrite(Node* node) | 
|  | { | 
|  | WatchpointSet* set = node->watchpointSet(); | 
|  |  | 
|  | JITCompiler::Jump slowCase = m_jit.branch8( | 
|  | JITCompiler::NotEqual, | 
|  | JITCompiler::AbsoluteAddress(set->addressOfState()), | 
|  | TrustedImm32(IsInvalidated)); | 
|  |  | 
|  | addSlowPathGenerator( | 
|  | slowPathCall(slowCase, this, operationNotifyWrite, NeedToSpill, ExceptionCheckRequirement::CheckNotNeeded, NoResult, &vm(), set)); | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileIsObject(Node* node) | 
|  | { | 
|  | JSValueOperand value(this, node->child1()); | 
|  | GPRTemporary result(this, Reuse, value, TagWord); | 
|  |  | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | JITCompiler::Jump isNotCell = m_jit.branchIfNotCell(valueRegs); | 
|  |  | 
|  | m_jit.compare8(JITCompiler::AboveOrEqual, | 
|  | JITCompiler::Address(valueRegs.payloadGPR(), JSCell::typeInfoTypeOffset()), | 
|  | TrustedImm32(ObjectType), | 
|  | resultGPR); | 
|  | JITCompiler::Jump done = m_jit.jump(); | 
|  |  | 
|  | isNotCell.link(&m_jit); | 
|  | m_jit.move(TrustedImm32(0), resultGPR); | 
|  |  | 
|  | done.link(&m_jit); | 
|  | unblessedBooleanResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileTypeOfIsObject(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 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 | OverridesGetCallData)); | 
|  |  | 
|  | 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, operationTypeOfIsObject, resultGPR, globalObject, | 
|  | valueRegs.payloadGPR())); | 
|  |  | 
|  | done.link(&m_jit); | 
|  |  | 
|  | unblessedBooleanResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileIsCallable(Node* node, S_JITOperation_GC slowPathOperation) | 
|  | { | 
|  | 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 | OverridesGetCallData)); | 
|  |  | 
|  | 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, slowPathOperation, resultGPR, globalObject, | 
|  | valueRegs.payloadGPR())); | 
|  |  | 
|  | done.link(&m_jit); | 
|  |  | 
|  | unblessedBooleanResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileIsConstructor(Node* node) | 
|  | { | 
|  | JSValueOperand input(this, node->child1()); | 
|  | JSValueRegs inputRegs = input.jsValueRegs(); | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | callOperation(operationIsConstructor, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), inputRegs); | 
|  | unblessedBooleanResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | 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::weakPointer(m_jit.graph(), vm().smallStrings.typeString(type)), resultGPR); | 
|  | if (!fallsThrough) | 
|  | done.append(m_jit.jump()); | 
|  | }, | 
|  | [&] (JITCompiler::Jump theSlowPath) { | 
|  | slowPath = theSlowPath; | 
|  | }); | 
|  | done.link(&m_jit); | 
|  |  | 
|  | addSlowPathGenerator( | 
|  | slowPathCall( | 
|  | slowPath, this, operationTypeOfObject, resultGPR, globalObject, | 
|  | valueRegs.payloadGPR())); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::emitStructureCheck(Node* node, GPRReg cellGPR, GPRReg tempGPR) | 
|  | { | 
|  | ASSERT(node->structureSet().size()); | 
|  |  | 
|  | if (node->structureSet().size() == 1) { | 
|  | speculationCheck( | 
|  | BadCache, JSValueSource::unboxedCell(cellGPR), nullptr, | 
|  | 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 = makeUnique<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), nullptr, | 
|  | m_jit.branchWeakStructure( | 
|  | JITCompiler::NotEqual, structureGPR, node->structureSet().last())); | 
|  |  | 
|  | done.link(&m_jit); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCheckIsConstant(Node* node) | 
|  | { | 
|  | if (node->child1().useKind() == CellUse) { | 
|  | SpeculateCellOperand cell(this, node->child1()); | 
|  | speculationCheck(BadConstantValue, JSValueSource::unboxedCell(cell.gpr()), node->child1(), m_jit.branchWeakPtr(JITCompiler::NotEqual, cell.gpr(), node->cellOperand()->cell())); | 
|  | } else { | 
|  | ASSERT(!node->constant()->value().isCell() || !node->constant()->value()); | 
|  | JSValueOperand operand(this, node->child1()); | 
|  | JSValueRegs regs = operand.jsValueRegs(); | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | speculationCheck(BadConstantValue, regs, node->child1(), m_jit.branch64(JITCompiler::NotEqual, regs.gpr(), TrustedImm64(JSValue::encode(node->constant()->value())))); | 
|  | #else | 
|  | speculationCheck(BadConstantValue, regs, node->child1(), m_jit.branch32(JITCompiler::NotEqual, regs.tagGPR(), TrustedImm32(node->constant()->value().tag()))); | 
|  | speculationCheck(BadConstantValue, regs, node->child1(), m_jit.branch32(JITCompiler::NotEqual, regs.payloadGPR(), TrustedImm32(node->constant()->value().payload()))); | 
|  | #endif | 
|  | } | 
|  |  | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCheckNotEmpty(Node* node) | 
|  | { | 
|  | JSValueOperand operand(this, node->child1()); | 
|  | JSValueRegs regs = operand.jsValueRegs(); | 
|  | speculationCheck(TDZFailure, JSValueSource(), nullptr, m_jit.branchIfEmpty(regs)); | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCheckStructure(Node* node) | 
|  | { | 
|  | switch (node->child1().useKind()) { | 
|  | case CellUse: | 
|  | case KnownCellUse: { | 
|  | SpeculateCellOperand cell(this, node->child1()); | 
|  | emitStructureCheck(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); | 
|  | emitStructureCheck(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) | 
|  | { | 
|  | ASSERT(!node->transition()->previous->outOfLineCapacity()); | 
|  | ASSERT(initialOutOfLineCapacity == node->transition()->next->outOfLineCapacity()); | 
|  |  | 
|  | size_t size = initialOutOfLineCapacity * sizeof(JSValue); | 
|  |  | 
|  | Allocator allocator = vm().jsValueGigacageAuxiliarySpace.allocatorForNonVirtual(size, AllocatorForMode::AllocatorIfExists); | 
|  |  | 
|  | if (!allocator || node->transition()->previous->couldHaveIndexingHeader()) { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | GPRFlushedCallResult result(this); | 
|  | callOperation(operationAllocateComplexPropertyStorageWithInitialCapacity, result.gpr(), &vm(), baseGPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | storageResult(result.gpr(), node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | GPRTemporary scratch1(this); | 
|  | GPRTemporary scratch2(this); | 
|  | GPRTemporary scratch3(this); | 
|  |  | 
|  | GPRReg scratchGPR1 = scratch1.gpr(); | 
|  | GPRReg scratchGPR2 = scratch2.gpr(); | 
|  | GPRReg scratchGPR3 = scratch3.gpr(); | 
|  |  | 
|  | JITCompiler::JumpList slowPath; | 
|  | m_jit.emitAllocate(scratchGPR1, JITAllocator::constant(allocator), scratchGPR2, scratchGPR3, slowPath); | 
|  | m_jit.addPtr(JITCompiler::TrustedImm32(size + sizeof(IndexingHeader)), scratchGPR1); | 
|  |  | 
|  | addSlowPathGenerator( | 
|  | slowPathCall(slowPath, this, operationAllocateSimplePropertyStorageWithInitialCapacity, scratchGPR1, &vm())); | 
|  |  | 
|  | for (ptrdiff_t offset = 0; offset < static_cast<ptrdiff_t>(size); offset += sizeof(void*)) | 
|  | m_jit.storePtr(TrustedImmPtr(nullptr), JITCompiler::Address(scratchGPR1, -(offset + sizeof(JSValue) + sizeof(void*)))); | 
|  |  | 
|  | storageResult(scratchGPR1, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileReallocatePropertyStorage(Node* node) | 
|  | { | 
|  | size_t oldSize = node->transition()->previous->outOfLineCapacity() * sizeof(JSValue); | 
|  | size_t newSize = oldSize * outOfLineGrowthFactor; | 
|  | ASSERT(newSize == node->transition()->next->outOfLineCapacity() * sizeof(JSValue)); | 
|  |  | 
|  | Allocator allocator = vm().jsValueGigacageAuxiliarySpace.allocatorForNonVirtual(newSize, AllocatorForMode::AllocatorIfExists); | 
|  |  | 
|  | if (!allocator || node->transition()->previous->couldHaveIndexingHeader()) { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | GPRFlushedCallResult result(this); | 
|  | callOperation(operationAllocateComplexPropertyStorage, result.gpr(), &vm(), baseGPR, newSize / sizeof(JSValue)); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | storageResult(result.gpr(), node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | StorageOperand oldStorage(this, node->child2()); | 
|  | GPRTemporary scratch1(this); | 
|  | GPRTemporary scratch2(this); | 
|  | GPRTemporary scratch3(this); | 
|  |  | 
|  | GPRReg oldStorageGPR = oldStorage.gpr(); | 
|  | GPRReg scratchGPR1 = scratch1.gpr(); | 
|  | GPRReg scratchGPR2 = scratch2.gpr(); | 
|  | GPRReg scratchGPR3 = scratch3.gpr(); | 
|  |  | 
|  | JITCompiler::JumpList slowPath; | 
|  | m_jit.emitAllocate(scratchGPR1, JITAllocator::constant(allocator), scratchGPR2, scratchGPR3, slowPath); | 
|  |  | 
|  | m_jit.addPtr(JITCompiler::TrustedImm32(newSize + sizeof(IndexingHeader)), scratchGPR1); | 
|  |  | 
|  | addSlowPathGenerator( | 
|  | slowPathCall(slowPath, this, operationAllocateSimplePropertyStorage, scratchGPR1, &vm(), newSize / sizeof(JSValue))); | 
|  |  | 
|  | for (ptrdiff_t offset = oldSize; offset < static_cast<ptrdiff_t>(newSize); offset += sizeof(void*)) | 
|  | m_jit.storePtr(TrustedImmPtr(nullptr), JITCompiler::Address(scratchGPR1, -(offset + sizeof(JSValue) + sizeof(void*)))); | 
|  |  | 
|  | // 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*))), scratchGPR2); | 
|  | m_jit.storePtr(scratchGPR2, JITCompiler::Address(scratchGPR1, -(offset + sizeof(JSValue) + sizeof(void*)))); | 
|  | } | 
|  |  | 
|  | storageResult(scratchGPR1, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNukeStructureAndSetButterfly(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | StorageOperand storage(this, node->child2()); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | GPRReg storageGPR = storage.gpr(); | 
|  |  | 
|  | m_jit.nukeStructureAndStoreButterfly(vm(), storageGPR, baseGPR); | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetButterfly(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | GPRTemporary result(this, Reuse, base); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | m_jit.loadPtr(JITCompiler::Address(baseGPR, JSObject::butterflyOffset()), resultGPR); | 
|  |  | 
|  | storageResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | static void allocateTemporaryRegistersForSnippet(SpeculativeJIT* jit, Vector<GPRTemporary>& gpHolders, Vector<FPRTemporary>& fpHolders, Vector<GPRReg>& gpScratch, Vector<FPRReg>& fpScratch, Snippet& snippet) | 
|  | { | 
|  | for (unsigned i = 0; i < snippet.numGPScratchRegisters; ++i) { | 
|  | GPRTemporary temporary(jit); | 
|  | gpScratch.append(temporary.gpr()); | 
|  | gpHolders.append(WTFMove(temporary)); | 
|  | } | 
|  |  | 
|  | for (unsigned i = 0; i < snippet.numFPScratchRegisters; ++i) { | 
|  | FPRTemporary temporary(jit); | 
|  | fpScratch.append(temporary.fpr()); | 
|  | fpHolders.append(WTFMove(temporary)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCallDOM(Node* node) | 
|  | { | 
|  | const DOMJIT::Signature* signature = node->signature(); | 
|  |  | 
|  | // FIXME: We should have a way to call functions with the vector of registers. | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=163099 | 
|  | Vector<Variant<SpeculateCellOperand, SpeculateInt32Operand, SpeculateBooleanOperand>, JSC_DOMJIT_SIGNATURE_MAX_ARGUMENTS_INCLUDING_THIS> operands; | 
|  | Vector<GPRReg, JSC_DOMJIT_SIGNATURE_MAX_ARGUMENTS_INCLUDING_THIS> regs; | 
|  |  | 
|  | auto appendCell = [&](Edge& edge) { | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | regs.append(operand.gpr()); | 
|  | operands.append(WTFMove(operand)); | 
|  | }; | 
|  |  | 
|  | auto appendString = [&](Edge& edge) { | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | GPRReg gpr = operand.gpr(); | 
|  | regs.append(gpr); | 
|  | speculateString(edge, gpr); | 
|  | operands.append(WTFMove(operand)); | 
|  | }; | 
|  |  | 
|  | auto appendInt32 = [&](Edge& edge) { | 
|  | SpeculateInt32Operand operand(this, edge); | 
|  | regs.append(operand.gpr()); | 
|  | operands.append(WTFMove(operand)); | 
|  | }; | 
|  |  | 
|  | auto appendBoolean = [&](Edge& edge) { | 
|  | SpeculateBooleanOperand operand(this, edge); | 
|  | regs.append(operand.gpr()); | 
|  | operands.append(WTFMove(operand)); | 
|  | }; | 
|  |  | 
|  | unsigned index = 0; | 
|  | m_jit.graph().doToChildren(node, [&](Edge edge) { | 
|  | if (!index) | 
|  | appendCell(edge); | 
|  | else { | 
|  | switch (signature->arguments[index - 1]) { | 
|  | case SpecString: | 
|  | appendString(edge); | 
|  | break; | 
|  | case SpecInt32Only: | 
|  | appendInt32(edge); | 
|  | break; | 
|  | case SpecBoolean: | 
|  | appendBoolean(edge); | 
|  | break; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  | ++index; | 
|  | }); | 
|  |  | 
|  | JSValueRegsTemporary result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | // FIXME: Revisit JSGlobalObject. | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=203204 | 
|  | auto function = CFunctionPtr(signature->functionWithoutTypeCheck); | 
|  | unsigned argumentCountIncludingThis = signature->argumentCount + 1; | 
|  | switch (argumentCountIncludingThis) { | 
|  | case 1: | 
|  | callOperation(reinterpret_cast<J_JITOperation_GP>(function.get()), extractResult(resultRegs), TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), regs[0]); | 
|  | break; | 
|  | case 2: | 
|  | callOperation(reinterpret_cast<J_JITOperation_GPP>(function.get()), extractResult(resultRegs), TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), regs[0], regs[1]); | 
|  | break; | 
|  | case 3: | 
|  | callOperation(reinterpret_cast<J_JITOperation_GPPP>(function.get()), extractResult(resultRegs), TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), regs[0], regs[1], regs[2]); | 
|  | break; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  |  | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCallDOMGetter(Node* node) | 
|  | { | 
|  | DOMJIT::CallDOMGetterSnippet* snippet = node->callDOMGetterData()->snippet; | 
|  | if (!snippet) { | 
|  | FunctionPtr<OperationPtrTag> getter = node->callDOMGetterData()->customAccessorGetter; | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | JSValueRegsTemporary result(this); | 
|  |  | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | GPRReg baseGPR = base.gpr(); | 
|  |  | 
|  | flushRegisters(); | 
|  | m_jit.setupArguments<J_JITOperation_GJI>(TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), CCallHelpers::CellValue(baseGPR), identifierUID(node->callDOMGetterData()->identifierNumber)); | 
|  | m_jit.storePtr(GPRInfo::callFrameRegister, &vm().topCallFrame); | 
|  | m_jit.emitStoreCodeOrigin(m_currentNode->origin.semantic); | 
|  | m_jit.appendCall(getter.retagged<CFunctionPtrTag>()); | 
|  | m_jit.setupResults(resultRegs); | 
|  |  | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | Vector<GPRReg> gpScratch; | 
|  | Vector<FPRReg> fpScratch; | 
|  | Vector<SnippetParams::Value> regs; | 
|  |  | 
|  | JSValueRegsTemporary result(this); | 
|  | regs.append(result.regs()); | 
|  |  | 
|  | Edge& baseEdge = node->child1(); | 
|  | SpeculateCellOperand base(this, baseEdge); | 
|  | regs.append(SnippetParams::Value(base.gpr(), m_state.forNode(baseEdge).value())); | 
|  |  | 
|  | Optional<SpeculateCellOperand> globalObject; | 
|  | if (snippet->requireGlobalObject) { | 
|  | Edge& globalObjectEdge = node->child2(); | 
|  | globalObject.emplace(this, globalObjectEdge); | 
|  | regs.append(SnippetParams::Value(globalObject->gpr(), m_state.forNode(globalObjectEdge).value())); | 
|  | } | 
|  |  | 
|  | Vector<GPRTemporary> gpTempraries; | 
|  | Vector<FPRTemporary> fpTempraries; | 
|  | allocateTemporaryRegistersForSnippet(this, gpTempraries, fpTempraries, gpScratch, fpScratch, *snippet); | 
|  | SnippetParams params(this, WTFMove(regs), WTFMove(gpScratch), WTFMove(fpScratch)); | 
|  | snippet->generator()->run(m_jit, params); | 
|  | jsValueResult(result.regs(), node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCheckJSCast(Node* node) | 
|  | { | 
|  | DFG_ASSERT(m_jit.graph(), node, node->op() == CheckJSCast || node->op() == CheckNotJSCast); | 
|  | const ClassInfo* classInfo = node->classInfo(); | 
|  | if (classInfo->inheritsJSTypeRange) { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | GPRReg baseGPR = base.gpr(); | 
|  |  | 
|  | CCallHelpers::Jump checkFailed; | 
|  | if (node->op() == CheckJSCast) | 
|  | checkFailed = m_jit.branchIfNotType(baseGPR, classInfo->inheritsJSTypeRange.value()); | 
|  | else | 
|  | checkFailed = m_jit.branchIfType(baseGPR, classInfo->inheritsJSTypeRange.value()); | 
|  | speculationCheck(BadType, JSValueSource::unboxedCell(baseGPR), node->child1(), checkFailed); | 
|  | noResult(node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!classInfo->checkSubClassSnippet) { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | GPRTemporary other(this); | 
|  | GPRTemporary specified(this); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | GPRReg otherGPR = other.gpr(); | 
|  | GPRReg specifiedGPR = specified.gpr(); | 
|  |  | 
|  | m_jit.emitLoadStructure(vm(), baseGPR, otherGPR, specifiedGPR); | 
|  | m_jit.loadPtr(CCallHelpers::Address(otherGPR, Structure::classInfoOffset()), otherGPR); | 
|  | m_jit.move(CCallHelpers::TrustedImmPtr(node->classInfo()), specifiedGPR); | 
|  |  | 
|  | CCallHelpers::Label loop = m_jit.label(); | 
|  | auto found = m_jit.branchPtr(CCallHelpers::Equal, otherGPR, specifiedGPR); | 
|  | m_jit.loadPtr(CCallHelpers::Address(otherGPR, ClassInfo::offsetOfParentClass()), otherGPR); | 
|  | m_jit.branchTestPtr(CCallHelpers::NonZero, otherGPR).linkTo(loop, &m_jit); | 
|  | if (node->op() == CheckJSCast) { | 
|  | speculationCheck(BadType, JSValueSource::unboxedCell(baseGPR), node->child1(), m_jit.jump()); | 
|  | found.link(&m_jit); | 
|  | } else { | 
|  | auto notFound = m_jit.jump(); | 
|  | speculationCheck(BadType, JSValueSource::unboxedCell(baseGPR), node->child1(), found); | 
|  | notFound.link(&m_jit); | 
|  | } | 
|  | noResult(node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | Ref<Snippet> snippet = classInfo->checkSubClassSnippet(); | 
|  |  | 
|  | Vector<GPRReg> gpScratch; | 
|  | Vector<FPRReg> fpScratch; | 
|  | Vector<SnippetParams::Value> regs; | 
|  |  | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | regs.append(SnippetParams::Value(baseGPR, m_state.forNode(node->child1()).value())); | 
|  |  | 
|  | Vector<GPRTemporary> gpTempraries; | 
|  | Vector<FPRTemporary> fpTempraries; | 
|  | allocateTemporaryRegistersForSnippet(this, gpTempraries, fpTempraries, gpScratch, fpScratch, snippet.get()); | 
|  |  | 
|  | SnippetParams params(this, WTFMove(regs), WTFMove(gpScratch), WTFMove(fpScratch)); | 
|  | CCallHelpers::JumpList failureCases = snippet->generator()->run(m_jit, params); | 
|  | if (node->op() == CheckJSCast) | 
|  | speculationCheck(BadType, JSValueSource::unboxedCell(baseGPR), node->child1(), failureCases); | 
|  | else { | 
|  | speculationCheck(BadType, JSValueSource::unboxedCell(baseGPR), node->child1(), m_jit.jump()); | 
|  | failureCases.link(&m_jit); | 
|  | } | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | GPRReg SpeculativeJIT::temporaryRegisterForPutByVal(GPRTemporary& temporary, ArrayMode arrayMode) | 
|  | { | 
|  | if (!putByValWillNeedExtraRegister(arrayMode)) | 
|  | return InvalidGPRReg; | 
|  |  | 
|  | GPRTemporary realTemporary(this); | 
|  | temporary.adopt(realTemporary); | 
|  | return temporary.gpr(); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileToStringOrCallStringConstructorOrStringValueOf(Node* node) | 
|  | { | 
|  | ASSERT(node->op() != StringValueOf || node->child1().useKind() == UntypedUse); | 
|  | switch (node->child1().useKind()) { | 
|  | case NotCellUse: { | 
|  | JSValueOperand op1(this, node->child1(), ManualOperandSpeculation); | 
|  | JSValueRegs op1Regs = op1.jsValueRegs(); | 
|  |  | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | speculateNotCell(node->child1(), op1Regs); | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | if (node->op() == ToString) | 
|  | callOperation(operationToString, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), op1Regs); | 
|  | else { | 
|  | ASSERT(node->op() == CallStringConstructor); | 
|  | callOperation(operationCallStringConstructor, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), op1Regs); | 
|  | } | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | case UntypedUse: { | 
|  | JSValueOperand op1(this, node->child1()); | 
|  | JSValueRegs op1Regs = op1.jsValueRegs(); | 
|  | GPRReg op1PayloadGPR = op1Regs.payloadGPR(); | 
|  |  | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | JITCompiler::Jump done; | 
|  | if (node->child1()->prediction() & SpecString) { | 
|  | JITCompiler::Jump slowPath1 = m_jit.branchIfNotCell(op1.jsValueRegs()); | 
|  | JITCompiler::Jump slowPath2 = m_jit.branchIfNotString(op1PayloadGPR); | 
|  | m_jit.move(op1PayloadGPR, resultGPR); | 
|  | done = m_jit.jump(); | 
|  | slowPath1.link(&m_jit); | 
|  | slowPath2.link(&m_jit); | 
|  | } | 
|  | if (node->op() == ToString) | 
|  | callOperation(operationToString, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), op1Regs); | 
|  | else if (node->op() == StringValueOf) | 
|  | callOperation(operationStringValueOf, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), op1Regs); | 
|  | else { | 
|  | ASSERT(node->op() == CallStringConstructor); | 
|  | callOperation(operationCallStringConstructor, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), op1Regs); | 
|  | } | 
|  | m_jit.exceptionCheck(); | 
|  | if (done.isSet()) | 
|  | done.link(&m_jit); | 
|  | cellResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | case Int32Use: | 
|  | case Int52RepUse: | 
|  | case DoubleRepUse: | 
|  | compileNumberToStringWithValidRadixConstant(node, 10); | 
|  | return; | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | SpeculateCellOperand op1(this, node->child1()); | 
|  | GPRReg op1GPR = op1.gpr(); | 
|  |  | 
|  | switch (node->child1().useKind()) { | 
|  | case StringObjectUse: { | 
|  | GPRTemporary result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | speculateStringObject(node->child1(), op1GPR); | 
|  |  | 
|  | m_jit.loadPtr(JITCompiler::Address(op1GPR, JSWrapperObject::internalValueCellOffset()), resultGPR); | 
|  | cellResult(resultGPR, node); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case StringOrStringObjectUse: { | 
|  | GPRTemporary result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | m_jit.load8(JITCompiler::Address(op1GPR, JSCell::typeInfoTypeOffset()), resultGPR); | 
|  | JITCompiler::Jump isString = m_jit.branch32(JITCompiler::Equal, resultGPR, TrustedImm32(StringType)); | 
|  |  | 
|  | speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), node->child1().node(), m_jit.branch32(JITCompiler::NotEqual, resultGPR, TrustedImm32(StringObjectType))); | 
|  | m_jit.loadPtr(JITCompiler::Address(op1GPR, JSWrapperObject::internalValueCellOffset()), resultGPR); | 
|  | JITCompiler::Jump done = m_jit.jump(); | 
|  |  | 
|  | isString.link(&m_jit); | 
|  | m_jit.move(op1GPR, resultGPR); | 
|  | done.link(&m_jit); | 
|  |  | 
|  | m_interpreter.filter(node->child1(), SpecString | SpecStringObject); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case CellUse: { | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | // We flush registers instead of silent spill/fill because in this mode we | 
|  | // believe that most likely the input is not a string, and we need to take | 
|  | // slow path. | 
|  | flushRegisters(); | 
|  | JITCompiler::Jump done; | 
|  | if (node->child1()->prediction() & SpecString) { | 
|  | JITCompiler::Jump needCall = m_jit.branchIfNotString(op1GPR); | 
|  | m_jit.move(op1GPR, resultGPR); | 
|  | done = m_jit.jump(); | 
|  | needCall.link(&m_jit); | 
|  | } | 
|  | if (node->op() == ToString) | 
|  | callOperation(operationToStringOnCell, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), op1GPR); | 
|  | else { | 
|  | ASSERT(node->op() == CallStringConstructor); | 
|  | callOperation(operationCallStringConstructorOnCell, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), op1GPR); | 
|  | } | 
|  | m_jit.exceptionCheck(); | 
|  | if (done.isSet()) | 
|  | done.link(&m_jit); | 
|  | cellResult(resultGPR, node); | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNumberToStringWithValidRadixConstant(Node* node) | 
|  | { | 
|  | compileNumberToStringWithValidRadixConstant(node, node->validRadixConstant()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNumberToStringWithValidRadixConstant(Node* node, int32_t radix) | 
|  | { | 
|  | auto callToString = [&] (auto operation, GPRReg resultGPR, auto valueReg) { | 
|  | flushRegisters(); | 
|  | callOperation(operation, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), valueReg, TrustedImm32(radix)); | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(resultGPR, node); | 
|  | }; | 
|  |  | 
|  | switch (node->child1().useKind()) { | 
|  | case Int32Use: { | 
|  | SpeculateStrictInt32Operand value(this, node->child1()); | 
|  | GPRFlushedCallResult result(this); | 
|  | callToString(operationInt32ToStringWithValidRadix, result.gpr(), value.gpr()); | 
|  | break; | 
|  | } | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | case Int52RepUse: { | 
|  | SpeculateStrictInt52Operand value(this, node->child1()); | 
|  | GPRFlushedCallResult result(this); | 
|  | callToString(operationInt52ToStringWithValidRadix, result.gpr(), value.gpr()); | 
|  | break; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | case DoubleRepUse: { | 
|  | SpeculateDoubleOperand value(this, node->child1()); | 
|  | GPRFlushedCallResult result(this); | 
|  | callToString(operationDoubleToStringWithValidRadix, result.gpr(), value.fpr()); | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNumberToStringWithRadix(Node* node) | 
|  | { | 
|  | bool validRadixIsGuaranteed = false; | 
|  | if (node->child2()->isInt32Constant()) { | 
|  | int32_t radix = node->child2()->asInt32(); | 
|  | if (radix >= 2 && radix <= 36) | 
|  | validRadixIsGuaranteed = true; | 
|  | } | 
|  |  | 
|  | auto callToString = [&] (auto operation, GPRReg resultGPR, auto valueReg, GPRReg radixGPR) { | 
|  | flushRegisters(); | 
|  | callOperation(operation, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), valueReg, radixGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(resultGPR, node); | 
|  | }; | 
|  |  | 
|  | switch (node->child1().useKind()) { | 
|  | case Int32Use: { | 
|  | SpeculateStrictInt32Operand value(this, node->child1()); | 
|  | SpeculateStrictInt32Operand radix(this, node->child2()); | 
|  | GPRFlushedCallResult result(this); | 
|  | callToString(validRadixIsGuaranteed ? operationInt32ToStringWithValidRadix : operationInt32ToString, result.gpr(), value.gpr(), radix.gpr()); | 
|  | break; | 
|  | } | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | case Int52RepUse: { | 
|  | SpeculateStrictInt52Operand value(this, node->child1()); | 
|  | SpeculateStrictInt32Operand radix(this, node->child2()); | 
|  | GPRFlushedCallResult result(this); | 
|  | callToString(validRadixIsGuaranteed ? operationInt52ToStringWithValidRadix : operationInt52ToString, result.gpr(), value.gpr(), radix.gpr()); | 
|  | break; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | case DoubleRepUse: { | 
|  | SpeculateDoubleOperand value(this, node->child1()); | 
|  | SpeculateStrictInt32Operand radix(this, node->child2()); | 
|  | GPRFlushedCallResult result(this); | 
|  | callToString(validRadixIsGuaranteed ? operationDoubleToStringWithValidRadix : operationDoubleToString, result.gpr(), value.fpr(), radix.gpr()); | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNewStringObject(Node* node) | 
|  | { | 
|  | SpeculateCellOperand operand(this, node->child1()); | 
|  |  | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary scratch1(this); | 
|  | GPRTemporary scratch2(this); | 
|  |  | 
|  | GPRReg operandGPR = operand.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg scratch1GPR = scratch1.gpr(); | 
|  | GPRReg scratch2GPR = scratch2.gpr(); | 
|  |  | 
|  | JITCompiler::JumpList slowPath; | 
|  |  | 
|  | auto butterfly = TrustedImmPtr(nullptr); | 
|  | emitAllocateJSObject<StringObject>( | 
|  | resultGPR, TrustedImmPtr(node->structure()), butterfly, scratch1GPR, scratch2GPR, | 
|  | slowPath); | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | m_jit.store64( | 
|  | operandGPR, JITCompiler::Address(resultGPR, JSWrapperObject::internalValueOffset())); | 
|  | #else | 
|  | m_jit.store32( | 
|  | TrustedImm32(JSValue::CellTag), | 
|  | JITCompiler::Address(resultGPR, JSWrapperObject::internalValueOffset() + OBJECT_OFFSETOF(JSValue, u.asBits.tag))); | 
|  | m_jit.store32( | 
|  | operandGPR, | 
|  | JITCompiler::Address(resultGPR, JSWrapperObject::internalValueOffset() + OBJECT_OFFSETOF(JSValue, u.asBits.payload))); | 
|  | #endif | 
|  |  | 
|  | m_jit.mutatorFence(vm()); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall( | 
|  | slowPath, this, operationNewStringObject, resultGPR, &vm(), operandGPR, node->structure())); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNewSymbol(Node* node) | 
|  | { | 
|  | if (!node->child1()) { | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | callOperation(operationNewSymbol, resultGPR, &vm()); | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  |  | 
|  | ASSERT(node->child1().useKind() == KnownStringUse); | 
|  | SpeculateCellOperand operand(this, node->child1()); | 
|  |  | 
|  | GPRReg stringGPR = operand.gpr(); | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | callOperation(operationNewSymbolWithDescription, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), stringGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNewTypedArrayWithSize(Node* node) | 
|  | { | 
|  | JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic); | 
|  | auto typedArrayType = node->typedArrayType(); | 
|  | RegisteredStructure structure = m_jit.graph().registerStructure(globalObject->typedArrayStructureConcurrently(typedArrayType)); | 
|  | RELEASE_ASSERT(structure.get()); | 
|  |  | 
|  | 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; | 
|  |  | 
|  | m_jit.move(TrustedImmPtr(nullptr), storageGPR); | 
|  |  | 
|  | slowCases.append(m_jit.branch32( | 
|  | MacroAssembler::Above, sizeGPR, TrustedImm32(JSArrayBufferView::fastSizeLimit))); | 
|  |  | 
|  | m_jit.move(sizeGPR, scratchGPR); | 
|  | m_jit.lshift32(TrustedImm32(logElementSize(typedArrayType)), scratchGPR); | 
|  | if (elementSize(typedArrayType) < 8) { | 
|  | m_jit.add32(TrustedImm32(7), scratchGPR); | 
|  | m_jit.and32(TrustedImm32(~7), scratchGPR); | 
|  | } | 
|  | m_jit.emitAllocateVariableSized( | 
|  | storageGPR, vm().primitiveGigacageAuxiliarySpace, scratchGPR, scratchGPR, | 
|  | scratchGPR2, slowCases); | 
|  |  | 
|  | MacroAssembler::Jump done = m_jit.branchTest32(MacroAssembler::Zero, sizeGPR); | 
|  | m_jit.move(sizeGPR, scratchGPR); | 
|  | if (elementSize(typedArrayType) != 4) { | 
|  | if (elementSize(typedArrayType) > 4) | 
|  | m_jit.lshift32(TrustedImm32(logElementSize(typedArrayType) - 2), scratchGPR); | 
|  | else { | 
|  | if (elementSize(typedArrayType) > 1) | 
|  | m_jit.lshift32(TrustedImm32(logElementSize(typedArrayType)), 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); | 
|  | #if CPU(ARM64E) | 
|  | // sizeGPR is still boxed as a number and there is no 32-bit variant of the PAC instructions. | 
|  | m_jit.zeroExtend32ToWord(sizeGPR, scratchGPR); | 
|  | m_jit.tagArrayPtr(scratchGPR, storageGPR); | 
|  | #endif | 
|  |  | 
|  | auto butterfly = TrustedImmPtr(nullptr); | 
|  | switch (typedArrayType) { | 
|  | #define TYPED_ARRAY_TYPE_CASE(name) \ | 
|  | case Type ## name: \ | 
|  | emitAllocateJSObject<JS##name##Array>(resultGPR, TrustedImmPtr(structure), butterfly, scratchGPR, scratchGPR2, slowCases); \ | 
|  | break; | 
|  | FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(TYPED_ARRAY_TYPE_CASE) | 
|  | #undef TYPED_ARRAY_TYPE_CASE | 
|  | case TypeDataView: | 
|  | emitAllocateJSObject<JSDataView>(resultGPR, TrustedImmPtr(structure), butterfly, scratchGPR, scratchGPR2, slowCases); | 
|  | break; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  |  | 
|  | 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())); | 
|  |  | 
|  | m_jit.mutatorFence(vm()); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall( | 
|  | slowCases, this, operationNewTypedArrayWithSizeForType(typedArrayType), | 
|  | resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), structure, sizeGPR, storageGPR)); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNewRegexp(Node* node) | 
|  | { | 
|  | RegExp* regexp = node->castOperand<RegExp*>(); | 
|  |  | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary scratch1(this); | 
|  | GPRTemporary scratch2(this); | 
|  | JSValueOperand lastIndex(this, node->child1()); | 
|  |  | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg scratch1GPR = scratch1.gpr(); | 
|  | GPRReg scratch2GPR = scratch2.gpr(); | 
|  | JSValueRegs lastIndexRegs = lastIndex.jsValueRegs(); | 
|  |  | 
|  | JITCompiler::JumpList slowPath; | 
|  |  | 
|  | auto structure = m_jit.graph().registerStructure(m_jit.graph().globalObjectFor(node->origin.semantic)->regExpStructure()); | 
|  | auto butterfly = TrustedImmPtr(nullptr); | 
|  | emitAllocateJSObject<RegExpObject>(resultGPR, TrustedImmPtr(structure), butterfly, scratch1GPR, scratch2GPR, slowPath); | 
|  |  | 
|  | m_jit.storePtr( | 
|  | TrustedImmPtr(node->cellOperand()), | 
|  | CCallHelpers::Address(resultGPR, RegExpObject::offsetOfRegExpAndLastIndexIsNotWritableFlag())); | 
|  | m_jit.storeValue(lastIndexRegs, CCallHelpers::Address(resultGPR, RegExpObject::offsetOfLastIndex())); | 
|  | m_jit.mutatorFence(vm()); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowPath, this, operationNewRegexpWithLastIndex, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), regexp, lastIndexRegs)); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateCellTypeWithoutTypeFiltering( | 
|  | Edge edge, GPRReg cellGPR, JSType jsType) | 
|  | { | 
|  | speculationCheck( | 
|  | BadType, JSValueSource::unboxedCell(cellGPR), edge, | 
|  | m_jit.branchIfNotType(cellGPR, jsType)); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateCellType( | 
|  | Edge edge, GPRReg cellGPR, SpeculatedType specType, JSType jsType) | 
|  | { | 
|  | DFG_TYPE_CHECK( | 
|  | JSValueSource::unboxedCell(cellGPR), edge, specType, | 
|  | m_jit.branchIfNotType(cellGPR, jsType)); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateInt32(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecInt32Only)) | 
|  | return; | 
|  |  | 
|  | (SpeculateInt32Operand(this, edge)).gpr(); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateNumber(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecBytecodeNumber)) | 
|  | return; | 
|  |  | 
|  | JSValueOperand value(this, edge, ManualOperandSpeculation); | 
|  | #if USE(JSVALUE64) | 
|  | GPRReg gpr = value.gpr(); | 
|  | typeCheck( | 
|  | JSValueRegs(gpr), edge, SpecBytecodeNumber, | 
|  | m_jit.branchIfNotNumber(gpr)); | 
|  | #else | 
|  | IGNORE_WARNINGS_BEGIN("enum-compare") | 
|  | static_assert(JSValue::Int32Tag >= JSValue::LowestTag, "Int32Tag is included in >= JSValue::LowestTag range."); | 
|  | IGNORE_WARNINGS_END | 
|  | GPRReg tagGPR = value.tagGPR(); | 
|  | DFG_TYPE_CHECK( | 
|  | value.jsValueRegs(), edge, ~SpecInt32Only, | 
|  | m_jit.branchIfInt32(tagGPR)); | 
|  | 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, 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.branchIfNotNaN(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(); | 
|  | typeCheck( | 
|  | JSValueRegs(), edge, SpecDoubleReal, | 
|  | m_jit.branchIfNaN(fpr)); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateBoolean(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecBoolean)) | 
|  | return; | 
|  |  | 
|  | (SpeculateBooleanOperand(this, edge)).gpr(); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateCell(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecCellCheck)) | 
|  | return; | 
|  |  | 
|  | (SpeculateCellOperand(this, edge)).gpr(); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateCellOrOther(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecCellCheck | 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, SpecCellCheck | SpecOther, | 
|  | m_jit.branchIfNotOther(operand.jsValueRegs(), tempGPR)); | 
|  | ok.link(&m_jit); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateObject(Edge edge, GPRReg cell) | 
|  | { | 
|  | DFG_TYPE_CHECK(JSValueSource::unboxedCell(cell), edge, SpecObject, m_jit.branchIfNotObject(cell)); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateObject(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecObject)) | 
|  | return; | 
|  |  | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | speculateObject(edge, operand.gpr()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateFunction(Edge edge, GPRReg cell) | 
|  | { | 
|  | speculateCellType(edge, cell, SpecFunction, JSFunctionType); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateFunction(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecFunction)) | 
|  | return; | 
|  |  | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | speculateFunction(edge, operand.gpr()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateFinalObject(Edge edge, GPRReg cell) | 
|  | { | 
|  | speculateCellType(edge, cell, SpecFinalObject, FinalObjectType); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateFinalObject(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecFinalObject)) | 
|  | return; | 
|  |  | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | speculateFinalObject(edge, operand.gpr()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateRegExpObject(Edge edge, GPRReg cell) | 
|  | { | 
|  | speculateCellType(edge, cell, SpecRegExpObject, RegExpObjectType); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateRegExpObject(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecRegExpObject)) | 
|  | return; | 
|  |  | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | speculateRegExpObject(edge, operand.gpr()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateArray(Edge edge, GPRReg cell) | 
|  | { | 
|  | speculateCellType(edge, cell, SpecArray, ArrayType); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateArray(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecArray)) | 
|  | return; | 
|  |  | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | speculateArray(edge, operand.gpr()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateProxyObject(Edge edge, GPRReg cell) | 
|  | { | 
|  | speculateCellType(edge, cell, SpecProxyObject, ProxyObjectType); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateProxyObject(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecProxyObject)) | 
|  | return; | 
|  |  | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | speculateProxyObject(edge, operand.gpr()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateDerivedArray(Edge edge, GPRReg cell) | 
|  | { | 
|  | speculateCellType(edge, cell, SpecDerivedArray, DerivedArrayType); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateDerivedArray(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecDerivedArray)) | 
|  | return; | 
|  |  | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | speculateDerivedArray(edge, operand.gpr()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculatePromiseObject(Edge edge, GPRReg cell) | 
|  | { | 
|  | speculateCellType(edge, cell, SpecPromiseObject, JSPromiseType); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculatePromiseObject(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecPromiseObject)) | 
|  | return; | 
|  |  | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | speculatePromiseObject(edge, operand.gpr()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateDateObject(Edge edge, GPRReg cell) | 
|  | { | 
|  | speculateCellType(edge, cell, SpecDateObject, JSDateType); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateDateObject(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecDateObject)) | 
|  | return; | 
|  |  | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | speculateDateObject(edge, operand.gpr()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateMapObject(Edge edge, GPRReg cell) | 
|  | { | 
|  | speculateCellType(edge, cell, SpecMapObject, JSMapType); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateMapObject(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecMapObject)) | 
|  | return; | 
|  |  | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | speculateMapObject(edge, operand.gpr()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateSetObject(Edge edge, GPRReg cell) | 
|  | { | 
|  | speculateCellType(edge, cell, SpecSetObject, JSSetType); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateSetObject(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecSetObject)) | 
|  | return; | 
|  |  | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | speculateSetObject(edge, operand.gpr()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateWeakMapObject(Edge edge, GPRReg cell) | 
|  | { | 
|  | speculateCellType(edge, cell, SpecWeakMapObject, JSWeakMapType); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateWeakMapObject(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecWeakMapObject)) | 
|  | return; | 
|  |  | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | speculateWeakMapObject(edge, operand.gpr()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateWeakSetObject(Edge edge, GPRReg cell) | 
|  | { | 
|  | speculateCellType(edge, cell, SpecWeakSetObject, JSWeakSetType); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateWeakSetObject(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecWeakSetObject)) | 
|  | return; | 
|  |  | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | speculateWeakSetObject(edge, operand.gpr()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateDataViewObject(Edge edge, GPRReg cell) | 
|  | { | 
|  | speculateCellType(edge, cell, SpecDataViewObject, DataViewType); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateDataViewObject(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecDataViewObject)) | 
|  | return; | 
|  |  | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | speculateDataViewObject(edge, operand.gpr()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateObjectOrOther(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecObject | SpecOther)) | 
|  | return; | 
|  |  | 
|  | JSValueOperand operand(this, edge, ManualOperandSpeculation); | 
|  | GPRTemporary temp(this); | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  | MacroAssembler::Jump notCell = m_jit.branchIfNotCell(operand.jsValueRegs()); | 
|  | GPRReg gpr = operand.jsValueRegs().payloadGPR(); | 
|  | DFG_TYPE_CHECK( | 
|  | operand.jsValueRegs(), edge, (~SpecCellCheck) | SpecObject, m_jit.branchIfNotObject(gpr)); | 
|  | MacroAssembler::Jump done = m_jit.jump(); | 
|  | notCell.link(&m_jit); | 
|  | DFG_TYPE_CHECK( | 
|  | operand.jsValueRegs(), edge, SpecCellCheck | SpecOther, | 
|  | m_jit.branchIfNotOther(operand.jsValueRegs(), tempGPR)); | 
|  | done.link(&m_jit); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateString(Edge edge, GPRReg cell) | 
|  | { | 
|  | DFG_TYPE_CHECK( | 
|  | JSValueSource::unboxedCell(cell), edge, SpecString | ~SpecCellCheck, 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, (~SpecCellCheck) | SpecString, m_jit.branchIfNotString(cell)); | 
|  | JITCompiler::Jump done = m_jit.jump(); | 
|  | notCell.link(&m_jit); | 
|  | DFG_TYPE_CHECK(regs, edge, SpecCellCheck | SpecOther, m_jit.branchIfNotOther(regs, scratch)); | 
|  | done.link(&m_jit); | 
|  | } | 
|  |  | 
|  | 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.branchIfRopeStringImpl(storage)); | 
|  | speculationCheck( | 
|  | BadType, JSValueSource::unboxedCell(string), edge, m_jit.branchTest32( | 
|  | MacroAssembler::Zero, | 
|  | MacroAssembler::Address(storage, StringImpl::flagsOffset()), | 
|  | MacroAssembler::TrustedImm32(StringImpl::flagIsAtom()))); | 
|  |  | 
|  | 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) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecString)) | 
|  | return; | 
|  |  | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | speculateString(edge, operand.gpr()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateStringObject(Edge edge, GPRReg cellGPR) | 
|  | { | 
|  | DFG_TYPE_CHECK(JSValueSource::unboxedCell(cellGPR), edge, ~SpecCellCheck | SpecStringObject, m_jit.branchIfNotType(cellGPR, StringObjectType)); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateStringObject(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecStringObject)) | 
|  | return; | 
|  |  | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | GPRReg gpr = operand.gpr(); | 
|  | speculateStringObject(edge, gpr); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateStringOrStringObject(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecString | SpecStringObject)) | 
|  | return; | 
|  |  | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | GPRReg gpr = operand.gpr(); | 
|  | if (!needsTypeCheck(edge, SpecString | SpecStringObject)) | 
|  | return; | 
|  |  | 
|  | GPRTemporary typeTemp(this); | 
|  | GPRReg typeGPR = typeTemp.gpr(); | 
|  |  | 
|  | m_jit.load8(JITCompiler::Address(gpr, JSCell::typeInfoTypeOffset()), typeGPR); | 
|  |  | 
|  | JITCompiler::Jump isString = m_jit.branch32(JITCompiler::Equal, typeGPR, TrustedImm32(StringType)); | 
|  | speculationCheck(BadType, JSValueSource::unboxedCell(gpr), edge.node(), m_jit.branch32(JITCompiler::NotEqual, typeGPR, TrustedImm32(StringObjectType))); | 
|  | isString.link(&m_jit); | 
|  |  | 
|  | m_interpreter.filter(edge, SpecString | SpecStringObject); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateNotStringVar(Edge edge) | 
|  | { | 
|  | JSValueOperand operand(this, edge, ManualOperandSpeculation); | 
|  | GPRTemporary temp(this); | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  |  | 
|  | JITCompiler::Jump notCell = m_jit.branchIfNotCell(operand.jsValueRegs()); | 
|  | GPRReg cell = operand.jsValueRegs().payloadGPR(); | 
|  |  | 
|  | JITCompiler::Jump notString = m_jit.branchIfNotString(cell); | 
|  |  | 
|  | speculateStringIdentAndLoadStorage(edge, cell, tempGPR); | 
|  |  | 
|  | notString.link(&m_jit); | 
|  | notCell.link(&m_jit); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateNotSymbol(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, ~SpecSymbol)) | 
|  | return; | 
|  |  | 
|  | JSValueOperand operand(this, edge, ManualOperandSpeculation); | 
|  | auto valueRegs = operand.jsValueRegs(); | 
|  | GPRReg value = valueRegs.payloadGPR(); | 
|  | JITCompiler::Jump notCell; | 
|  |  | 
|  | bool needsCellCheck = needsTypeCheck(edge, SpecCell); | 
|  | if (needsCellCheck) | 
|  | notCell = m_jit.branchIfNotCell(valueRegs); | 
|  |  | 
|  | speculationCheck(BadType, JSValueSource::unboxedCell(value), edge.node(), m_jit.branchIfSymbol(value)); | 
|  |  | 
|  | if (needsCellCheck) | 
|  | notCell.link(&m_jit); | 
|  |  | 
|  | m_interpreter.filter(edge, ~SpecSymbol); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateSymbol(Edge edge, GPRReg cell) | 
|  | { | 
|  | DFG_TYPE_CHECK(JSValueSource::unboxedCell(cell), edge, ~SpecCellCheck | 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::speculateHeapBigInt(Edge edge, GPRReg cell) | 
|  | { | 
|  | DFG_TYPE_CHECK(JSValueSource::unboxedCell(cell), edge, ~SpecCellCheck | SpecHeapBigInt, m_jit.branchIfNotHeapBigInt(cell)); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateHeapBigInt(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecHeapBigInt)) | 
|  | return; | 
|  |  | 
|  | SpeculateCellOperand operand(this, edge); | 
|  | speculateHeapBigInt(edge, operand.gpr()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateNotCell(Edge edge, JSValueRegs regs) | 
|  | { | 
|  | DFG_TYPE_CHECK(regs, edge, ~SpecCellCheck, m_jit.branchIfCell(regs)); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateNotCell(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, ~SpecCellCheck)) | 
|  | return; | 
|  |  | 
|  | JSValueOperand operand(this, edge, ManualOperandSpeculation); | 
|  | speculateNotCell(edge, operand.jsValueRegs()); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateNotCellNorBigInt(Edge edge) | 
|  | { | 
|  | #if USE(BIGINT32) | 
|  | if (!needsTypeCheck(edge, ~SpecCellCheck & ~SpecBigInt)) | 
|  | return; | 
|  |  | 
|  | JSValueOperand operand(this, edge, ManualOperandSpeculation); | 
|  | GPRTemporary temp(this); | 
|  |  | 
|  | JSValueRegs regs = operand.jsValueRegs(); | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  |  | 
|  | DFG_TYPE_CHECK(regs, edge, ~SpecCellCheck, m_jit.branchIfCell(regs)); | 
|  | DFG_TYPE_CHECK(regs, edge, ~SpecCellCheck & ~SpecBigInt, m_jit.branchIfBigInt32(regs, tempGPR)); | 
|  | #else | 
|  | speculateNotCell(edge); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateOther(Edge edge, JSValueRegs regs, GPRReg tempGPR) | 
|  | { | 
|  | DFG_TYPE_CHECK(regs, edge, SpecOther, m_jit.branchIfNotOther(regs, tempGPR)); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateOther(Edge edge, JSValueRegs regs) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecOther)) | 
|  | return; | 
|  |  | 
|  | GPRTemporary temp(this); | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  | speculateOther(edge, regs, tempGPR); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::speculateOther(Edge edge) | 
|  | { | 
|  | if (!needsTypeCheck(edge, SpecOther)) | 
|  | return; | 
|  |  | 
|  | JSValueOperand operand(this, edge, ManualOperandSpeculation); | 
|  | speculateOther(edge, operand.jsValueRegs()); | 
|  | } | 
|  |  | 
|  | 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(JSValue::MiscTag))); | 
|  | #else | 
|  | IGNORE_WARNINGS_BEGIN("enum-compare") | 
|  | static_assert(JSValue::Int32Tag >= JSValue::UndefinedTag, "Int32Tag is included in >= JSValue::UndefinedTag range."); | 
|  | IGNORE_WARNINGS_END | 
|  | DFG_TYPE_CHECK( | 
|  | regs, edge, ~SpecInt32Only, | 
|  | m_jit.branchIfInt32(regs.tagGPR())); | 
|  | 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) | 
|  | { | 
|  | switch (edge.useKind()) { | 
|  | case UntypedUse: | 
|  | break; | 
|  | case DoubleRepUse: | 
|  | case Int52RepUse: | 
|  | case KnownInt32Use: | 
|  | case KnownCellUse: | 
|  | case KnownStringUse: | 
|  | case KnownPrimitiveUse: | 
|  | case KnownOtherUse: | 
|  | case KnownBooleanUse: | 
|  | ASSERT(!m_interpreter.needsTypeCheck(edge)); | 
|  | break; | 
|  | case Int32Use: | 
|  | speculateInt32(edge); | 
|  | break; | 
|  | case NumberUse: | 
|  | speculateNumber(edge); | 
|  | break; | 
|  | case RealNumberUse: | 
|  | speculateRealNumber(edge); | 
|  | break; | 
|  | case DoubleRepRealUse: | 
|  | speculateDoubleRepReal(edge); | 
|  | break; | 
|  | #if USE(JSVALUE64) | 
|  | case AnyIntUse: | 
|  | speculateAnyInt(edge); | 
|  | break; | 
|  | case DoubleRepAnyIntUse: | 
|  | speculateDoubleRepAnyInt(edge); | 
|  | break; | 
|  | #endif | 
|  | case BooleanUse: | 
|  | speculateBoolean(edge); | 
|  | break; | 
|  | case CellUse: | 
|  | speculateCell(edge); | 
|  | break; | 
|  | case CellOrOtherUse: | 
|  | speculateCellOrOther(edge); | 
|  | break; | 
|  | case ObjectUse: | 
|  | speculateObject(edge); | 
|  | break; | 
|  | case FunctionUse: | 
|  | speculateFunction(edge); | 
|  | break; | 
|  | case ArrayUse: | 
|  | speculateArray(edge); | 
|  | break; | 
|  | case FinalObjectUse: | 
|  | speculateFinalObject(edge); | 
|  | break; | 
|  | case RegExpObjectUse: | 
|  | speculateRegExpObject(edge); | 
|  | break; | 
|  | case PromiseObjectUse: | 
|  | speculatePromiseObject(edge); | 
|  | break; | 
|  | case ProxyObjectUse: | 
|  | speculateProxyObject(edge); | 
|  | break; | 
|  | case DerivedArrayUse: | 
|  | speculateDerivedArray(edge); | 
|  | break; | 
|  | case DateObjectUse: | 
|  | speculateDateObject(edge); | 
|  | break; | 
|  | case MapObjectUse: | 
|  | speculateMapObject(edge); | 
|  | break; | 
|  | case SetObjectUse: | 
|  | speculateSetObject(edge); | 
|  | break; | 
|  | case WeakMapObjectUse: | 
|  | speculateWeakMapObject(edge); | 
|  | break; | 
|  | case WeakSetObjectUse: | 
|  | speculateWeakSetObject(edge); | 
|  | break; | 
|  | case DataViewObjectUse: | 
|  | speculateDataViewObject(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; | 
|  | #if USE(BIGINT32) | 
|  | case BigInt32Use: | 
|  | speculateBigInt32(edge); | 
|  | break; | 
|  | case AnyBigIntUse: | 
|  | speculateAnyBigInt(edge); | 
|  | break; | 
|  | #endif | 
|  | case HeapBigIntUse: | 
|  | speculateHeapBigInt(edge); | 
|  | break; | 
|  | case StringObjectUse: | 
|  | speculateStringObject(edge); | 
|  | break; | 
|  | case StringOrStringObjectUse: | 
|  | speculateStringOrStringObject(edge); | 
|  | break; | 
|  | case NotStringVarUse: | 
|  | speculateNotStringVar(edge); | 
|  | break; | 
|  | case NotSymbolUse: | 
|  | speculateNotSymbol(edge); | 
|  | break; | 
|  | case NotCellUse: | 
|  | speculateNotCell(edge); | 
|  | break; | 
|  | case NotCellNorBigIntUse: | 
|  | speculateNotCellNorBigInt(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::ScalePtr), scratch); | 
|  |  | 
|  | m_jit.farJump(scratch, JSSwitchPtrTag); | 
|  | 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(); | 
|  |  | 
|  | auto notInt32 = m_jit.branchIfNotInt32(valueRegs); | 
|  | emitSwitchIntJump(data, valueRegs.payloadGPR(), scratch); | 
|  | notInt32.link(&m_jit); | 
|  | addBranch(m_jit.branchIfNotNumber(valueRegs, scratch), data->fallThrough.block); | 
|  | silentSpillAllRegisters(scratch); | 
|  | callOperation(operationFindSwitchImmTargetForDouble, scratch, &vm(), valueRegs, data->switchTableIndex); | 
|  | silentFillAllRegisters(); | 
|  |  | 
|  | m_jit.farJump(scratch, JSSwitchPtrTag); | 
|  | noResult(node, UseChildrenCalledExplicitly); | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::emitSwitchCharStringJump(Node* node, SwitchData* data, GPRReg value, GPRReg scratch) | 
|  | { | 
|  | m_jit.loadPtr(MacroAssembler::Address(value, JSString::offsetOfValue()), scratch); | 
|  | auto isRope = m_jit.branchIfRopeStringImpl(scratch); | 
|  | addSlowPathGenerator(slowPathCall(isRope, this, operationResolveRope, scratch, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), value)); | 
|  |  | 
|  | addBranch( | 
|  | m_jit.branch32( | 
|  | MacroAssembler::NotEqual, | 
|  | MacroAssembler::Address(scratch, StringImpl::lengthMemoryOffset()), | 
|  | TrustedImm32(1)), | 
|  | data->fallThrough.block); | 
|  |  | 
|  | 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(node, 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(node, 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 constexpr 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_ENABLED) { | 
|  | 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(Node* node, 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, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), static_cast<size_t>(data->switchTableIndex), string); | 
|  | m_jit.exceptionCheck(); | 
|  | m_jit.farJump(string, JSSwitchPtrTag); | 
|  | return; | 
|  | } | 
|  |  | 
|  | GPRTemporary length(this); | 
|  | GPRTemporary temp(this); | 
|  |  | 
|  | GPRReg lengthGPR = length.gpr(); | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  |  | 
|  | MacroAssembler::JumpList slowCases; | 
|  | m_jit.loadPtr(MacroAssembler::Address(string, JSString::offsetOfValue()), tempGPR); | 
|  | slowCases.append(m_jit.branchIfRopeStringImpl(tempGPR)); | 
|  | m_jit.load32(MacroAssembler::Address(tempGPR, StringImpl::lengthMemoryOffset()), lengthGPR); | 
|  |  | 
|  | 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, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), static_cast<size_t>(data->switchTableIndex), string); | 
|  | silentFillAllRegisters(); | 
|  | m_jit.exceptionCheck(); | 
|  | m_jit.farJump(string, JSSwitchPtrTag); | 
|  | } | 
|  |  | 
|  | 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(node, 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(node, 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 (auto& branch : m_branches) | 
|  | branch.jump.linkTo(m_jit.blockHeads()[branch.destination->index], &m_jit); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileStoreBarrier(Node* node) | 
|  | { | 
|  | ASSERT(node->op() == StoreBarrier || node->op() == FencedStoreBarrier); | 
|  |  | 
|  | bool isFenced = node->op() == FencedStoreBarrier; | 
|  |  | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | GPRTemporary scratch1(this); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | GPRReg scratch1GPR = scratch1.gpr(); | 
|  |  | 
|  | JITCompiler::JumpList ok; | 
|  |  | 
|  | if (isFenced) { | 
|  | ok.append(m_jit.barrierBranch(vm(), baseGPR, scratch1GPR)); | 
|  |  | 
|  | JITCompiler::Jump noFence = m_jit.jumpIfMutatorFenceNotNeeded(vm()); | 
|  | m_jit.memoryFence(); | 
|  | ok.append(m_jit.barrierBranchWithoutFence(baseGPR)); | 
|  | noFence.link(&m_jit); | 
|  | } else | 
|  | ok.append(m_jit.barrierBranchWithoutFence(baseGPR)); | 
|  |  | 
|  | silentSpillAllRegisters(InvalidGPRReg); | 
|  | callOperation(operationWriteBarrierSlowPath, &vm(), baseGPR); | 
|  | silentFillAllRegisters(); | 
|  |  | 
|  | ok.link(&m_jit); | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | 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, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), 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, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), 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, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, identifierUID(node->identifierNumber()), node->accessorAttributes(), getterRegs.payloadGPR(), setterRegs.payloadGPR()); | 
|  | #endif | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileResolveScope(Node* node) | 
|  | { | 
|  | SpeculateCellOperand scope(this, node->child1()); | 
|  | GPRReg scopeGPR = scope.gpr(); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | flushRegisters(); | 
|  | callOperation(operationResolveScope, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), scopeGPR, identifierUID(node->identifierNumber())); | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileResolveScopeForHoistingFuncDeclInEval(Node* node) | 
|  | { | 
|  | SpeculateCellOperand scope(this, node->child1()); | 
|  | GPRReg scopeGPR = scope.gpr(); | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationResolveScopeForHoistingFuncDeclInEval, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), scopeGPR, identifierUID(node->identifierNumber())); | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetGlobalVariable(Node* node) | 
|  | { | 
|  | JSValueRegsTemporary result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | m_jit.loadValue(node->variablePointer(), resultRegs); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compilePutGlobalVariable(Node* node) | 
|  | { | 
|  | JSValueOperand value(this, node->child2()); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  | m_jit.storeValue(valueRegs, node->variablePointer()); | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetDynamicVar(Node* node) | 
|  | { | 
|  | SpeculateCellOperand scope(this, node->child1()); | 
|  | GPRReg scopeGPR = scope.gpr(); | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationGetDynamicVar, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), scopeGPR, identifierUID(node->identifierNumber()), node->getPutInfo()); | 
|  | m_jit.exceptionCheck(); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compilePutDynamicVar(Node* node) | 
|  | { | 
|  | SpeculateCellOperand scope(this, node->child1()); | 
|  | JSValueOperand value(this, node->child2()); | 
|  |  | 
|  | GPRReg scopeGPR = scope.gpr(); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(node->ecmaMode().isStrict() ? operationPutDynamicVarStrict : operationPutDynamicVarNonStrict, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), scopeGPR, valueRegs, identifierUID(node->identifierNumber()), node->getPutInfo()); | 
|  | m_jit.exceptionCheck(); | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetClosureVar(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | JSValueRegsTemporary result(this); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | m_jit.loadValue(JITCompiler::Address(baseGPR, JSLexicalEnvironment::offsetOfVariable(node->scopeOffset())), resultRegs); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compilePutClosureVar(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | JSValueOperand value(this, node->child2()); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  |  | 
|  | m_jit.storeValue(valueRegs, JITCompiler::Address(baseGPR, JSLexicalEnvironment::offsetOfVariable(node->scopeOffset()))); | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetInternalField(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | JSValueRegsTemporary result(this); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | m_jit.loadValue(JITCompiler::Address(baseGPR, JSInternalFieldObjectImpl<>::offsetOfInternalField(node->internalFieldIndex())), resultRegs); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compilePutInternalField(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | JSValueOperand value(this, node->child2()); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  |  | 
|  | m_jit.storeValue(valueRegs, JITCompiler::Address(baseGPR, JSInternalFieldObjectImpl<>::offsetOfInternalField(node->internalFieldIndex()))); | 
|  | 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; | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | JSValueRegs subscriptRegs = subscript.jsValueRegs(); | 
|  | GPRReg accessorGPR = accessor.gpr(); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(operation, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, subscriptRegs, node->accessorAttributes(), accessorGPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetRegExpObjectLastIndex(Node* node) | 
|  | { | 
|  | SpeculateCellOperand regExp(this, node->child1()); | 
|  | JSValueRegsTemporary result(this); | 
|  | GPRReg regExpGPR = regExp.gpr(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | speculateRegExpObject(node->child1(), regExpGPR); | 
|  | m_jit.loadValue(JITCompiler::Address(regExpGPR, RegExpObject::offsetOfLastIndex()), resultRegs); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileSetRegExpObjectLastIndex(Node* node) | 
|  | { | 
|  | SpeculateCellOperand regExp(this, node->child1()); | 
|  | JSValueOperand value(this, node->child2()); | 
|  | GPRReg regExpGPR = regExp.gpr(); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  |  | 
|  | if (!node->ignoreLastIndexIsWritable()) { | 
|  | speculateRegExpObject(node->child1(), regExpGPR); | 
|  | speculationCheck( | 
|  | ExoticObjectMode, JSValueRegs(), nullptr, | 
|  | m_jit.branchTestPtr( | 
|  | JITCompiler::NonZero, | 
|  | JITCompiler::Address(regExpGPR, RegExpObject::offsetOfRegExpAndLastIndexIsNotWritableFlag()), | 
|  | JITCompiler::TrustedImm32(RegExpObject::lastIndexIsNotWritableFlag))); | 
|  | } | 
|  |  | 
|  | m_jit.storeValue(valueRegs, JITCompiler::Address(regExpGPR, RegExpObject::offsetOfLastIndex())); | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileRegExpExec(Node* node) | 
|  | { | 
|  | bool sample = false; | 
|  | if (sample) | 
|  | m_jit.incrementSuperSamplerCount(); | 
|  |  | 
|  | SpeculateCellOperand globalObject(this, node->child1()); | 
|  | GPRReg globalObjectGPR = globalObject.gpr(); | 
|  |  | 
|  | if (node->child2().useKind() == RegExpObjectUse) { | 
|  | if (node->child3().useKind() == StringUse) { | 
|  | SpeculateCellOperand base(this, node->child2()); | 
|  | SpeculateCellOperand argument(this, node->child3()); | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | GPRReg argumentGPR = argument.gpr(); | 
|  | speculateRegExpObject(node->child2(), baseGPR); | 
|  | speculateString(node->child3(), argumentGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationRegExpExecString, resultRegs, globalObjectGPR, baseGPR, argumentGPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  |  | 
|  | if (sample) | 
|  | m_jit.decrementSuperSamplerCount(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | SpeculateCellOperand base(this, node->child2()); | 
|  | JSValueOperand argument(this, node->child3()); | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | JSValueRegs argumentRegs = argument.jsValueRegs(); | 
|  | speculateRegExpObject(node->child2(), baseGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationRegExpExec, resultRegs, globalObjectGPR, baseGPR, argumentRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  |  | 
|  | if (sample) | 
|  | m_jit.decrementSuperSamplerCount(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | JSValueOperand base(this, node->child2()); | 
|  | JSValueOperand argument(this, node->child3()); | 
|  | JSValueRegs baseRegs = base.jsValueRegs(); | 
|  | JSValueRegs argumentRegs = argument.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationRegExpExecGeneric, resultRegs, globalObjectGPR, baseRegs, argumentRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  |  | 
|  | if (sample) | 
|  | m_jit.decrementSuperSamplerCount(); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileRegExpTest(Node* node) | 
|  | { | 
|  | SpeculateCellOperand globalObject(this, node->child1()); | 
|  | GPRReg globalObjectGPR = globalObject.gpr(); | 
|  |  | 
|  | if (node->child2().useKind() == RegExpObjectUse) { | 
|  | if (node->child3().useKind() == StringUse) { | 
|  | SpeculateCellOperand base(this, node->child2()); | 
|  | SpeculateCellOperand argument(this, node->child3()); | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | GPRReg argumentGPR = argument.gpr(); | 
|  | speculateRegExpObject(node->child2(), baseGPR); | 
|  | speculateString(node->child3(), argumentGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | callOperation(operationRegExpTestString, result.gpr(), globalObjectGPR, baseGPR, argumentGPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | unblessedBooleanResult(result.gpr(), node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | SpeculateCellOperand base(this, node->child2()); | 
|  | JSValueOperand argument(this, node->child3()); | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | JSValueRegs argumentRegs = argument.jsValueRegs(); | 
|  | speculateRegExpObject(node->child2(), baseGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | callOperation(operationRegExpTest, result.gpr(), globalObjectGPR, baseGPR, argumentRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | unblessedBooleanResult(result.gpr(), node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | JSValueOperand base(this, node->child2()); | 
|  | JSValueOperand argument(this, node->child3()); | 
|  | JSValueRegs baseRegs = base.jsValueRegs(); | 
|  | JSValueRegs argumentRegs = argument.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | callOperation(operationRegExpTestGeneric, result.gpr(), globalObjectGPR, baseRegs, argumentRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | unblessedBooleanResult(result.gpr(), node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileStringReplace(Node* node) | 
|  | { | 
|  | ASSERT(node->op() == StringReplace || node->op() == StringReplaceRegExp); | 
|  | bool sample = false; | 
|  | if (sample) | 
|  | m_jit.incrementSuperSamplerCount(); | 
|  |  | 
|  | if (node->child1().useKind() == StringUse | 
|  | && node->child2().useKind() == RegExpObjectUse | 
|  | && node->child3().useKind() == StringUse) { | 
|  | if (JSString* replace = node->child3()->dynamicCastConstant<JSString*>(vm())) { | 
|  | if (!replace->length()) { | 
|  | SpeculateCellOperand string(this, node->child1()); | 
|  | SpeculateCellOperand regExp(this, node->child2()); | 
|  | GPRReg stringGPR = string.gpr(); | 
|  | GPRReg regExpGPR = regExp.gpr(); | 
|  | speculateString(node->child1(), stringGPR); | 
|  | speculateRegExpObject(node->child2(), regExpGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | callOperation(operationStringProtoFuncReplaceRegExpEmptyStr, result.gpr(), TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), stringGPR, regExpGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(result.gpr(), node); | 
|  | if (sample) | 
|  | m_jit.decrementSuperSamplerCount(); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | SpeculateCellOperand string(this, node->child1()); | 
|  | SpeculateCellOperand regExp(this, node->child2()); | 
|  | SpeculateCellOperand replace(this, node->child3()); | 
|  | GPRReg stringGPR = string.gpr(); | 
|  | GPRReg regExpGPR = regExp.gpr(); | 
|  | GPRReg replaceGPR = replace.gpr(); | 
|  | speculateString(node->child1(), stringGPR); | 
|  | speculateRegExpObject(node->child2(), regExpGPR); | 
|  | speculateString(node->child3(), replaceGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | callOperation(operationStringProtoFuncReplaceRegExpString, result.gpr(), TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), stringGPR, regExpGPR, replaceGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(result.gpr(), node); | 
|  | if (sample) | 
|  | m_jit.decrementSuperSamplerCount(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // If we fixed up the edge of child2, we inserted a Check(@child2, String). | 
|  | OperandSpeculationMode child2SpeculationMode = AutomaticOperandSpeculation; | 
|  | if (node->child2().useKind() == StringUse) | 
|  | child2SpeculationMode = ManualOperandSpeculation; | 
|  |  | 
|  | JSValueOperand string(this, node->child1()); | 
|  | JSValueOperand search(this, node->child2(), child2SpeculationMode); | 
|  | JSValueOperand replace(this, node->child3()); | 
|  | JSValueRegs stringRegs = string.jsValueRegs(); | 
|  | JSValueRegs searchRegs = search.jsValueRegs(); | 
|  | JSValueRegs replaceRegs = replace.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | callOperation(operationStringProtoFuncReplaceGeneric, result.gpr(), TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), stringRegs, searchRegs, replaceRegs); | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(result.gpr(), node); | 
|  | if (sample) | 
|  | m_jit.decrementSuperSamplerCount(); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileRegExpExecNonGlobalOrSticky(Node* node) | 
|  | { | 
|  | SpeculateCellOperand globalObject(this, node->child1()); | 
|  | SpeculateCellOperand argument(this, node->child2()); | 
|  | GPRReg globalObjectGPR = globalObject.gpr(); | 
|  | GPRReg argumentGPR = argument.gpr(); | 
|  |  | 
|  | speculateString(node->child2(), argumentGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation( | 
|  | operationRegExpExecNonGlobalOrSticky, resultRegs, | 
|  | globalObjectGPR, TrustedImmPtr(node->cellOperand()), argumentGPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileRegExpMatchFastGlobal(Node* node) | 
|  | { | 
|  | SpeculateCellOperand globalObject(this, node->child1()); | 
|  | SpeculateCellOperand argument(this, node->child2()); | 
|  | GPRReg globalObjectGPR = globalObject.gpr(); | 
|  | GPRReg argumentGPR = argument.gpr(); | 
|  |  | 
|  | speculateString(node->child2(), argumentGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation( | 
|  | operationRegExpMatchFastGlobalString, resultRegs, | 
|  | globalObjectGPR, TrustedImmPtr(node->cellOperand()), argumentGPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileRegExpMatchFast(Node* node) | 
|  | { | 
|  | SpeculateCellOperand globalObject(this, node->child1()); | 
|  | SpeculateCellOperand base(this, node->child2()); | 
|  | SpeculateCellOperand argument(this, node->child3()); | 
|  | GPRReg globalObjectGPR = globalObject.gpr(); | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | GPRReg argumentGPR = argument.gpr(); | 
|  | speculateRegExpObject(node->child2(), baseGPR); | 
|  | speculateString(node->child3(), argumentGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation( | 
|  | operationRegExpMatchFastString, resultRegs, | 
|  | globalObjectGPR, baseGPR, argumentGPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileLazyJSConstant(Node* node) | 
|  | { | 
|  | JSValueRegsTemporary result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | node->lazyJSValue().emit(m_jit, resultRegs); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileMaterializeNewObject(Node* node) | 
|  | { | 
|  | RegisteredStructure structure = node->structureSet().at(0); | 
|  | ASSERT(m_jit.graph().varArgChild(node, 0)->dynamicCastConstant<Structure*>(vm()) == structure.get()); | 
|  |  | 
|  | ObjectMaterializationData& data = node->objectMaterializationData(); | 
|  |  | 
|  | IndexingType indexingType = structure->indexingType(); | 
|  | bool hasIndexingHeader = hasIndexedProperties(indexingType); | 
|  | int32_t publicLength = 0; | 
|  | int32_t vectorLength = 0; | 
|  |  | 
|  | if (hasIndexingHeader) { | 
|  | for (unsigned i = data.m_properties.size(); i--;) { | 
|  | Edge edge = m_jit.graph().varArgChild(node, 1 + i); | 
|  | switch (data.m_properties[i].kind()) { | 
|  | case PublicLengthPLoc: | 
|  | publicLength = edge->asInt32(); | 
|  | break; | 
|  | case VectorLengthPLoc: | 
|  | vectorLength = edge->asInt32(); | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary storage(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg storageGPR = storage.gpr(); | 
|  |  | 
|  | emitAllocateRawObject(resultGPR, structure, storageGPR, 0, vectorLength); | 
|  |  | 
|  | // After the allocation, we must not exit until we fill butterfly completely. | 
|  |  | 
|  | m_jit.store32( | 
|  | JITCompiler::TrustedImm32(publicLength), | 
|  | JITCompiler::Address(storageGPR, Butterfly::offsetOfPublicLength())); | 
|  |  | 
|  | for (unsigned i = data.m_properties.size(); i--;) { | 
|  | Edge edge = m_jit.graph().varArgChild(node, 1 + i); | 
|  | PromotedLocationDescriptor descriptor = data.m_properties[i]; | 
|  | switch (descriptor.kind()) { | 
|  | case IndexedPropertyPLoc: { | 
|  | JSValueOperand value(this, edge); | 
|  | m_jit.storeValue( | 
|  | value.jsValueRegs(), | 
|  | JITCompiler::Address(storageGPR, sizeof(EncodedJSValue) * descriptor.info())); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case NamedPropertyPLoc: { | 
|  | StringImpl* uid = m_jit.graph().identifiers()[descriptor.info()]; | 
|  | for (PropertyMapEntry entry : structure->getPropertiesConcurrently()) { | 
|  | if (uid != entry.key) | 
|  | continue; | 
|  |  | 
|  | JSValueOperand value(this, edge); | 
|  | GPRReg baseGPR = isInlineOffset(entry.offset) ? resultGPR : storageGPR; | 
|  | m_jit.storeValue( | 
|  | value.jsValueRegs(), | 
|  | JITCompiler::Address(baseGPR, offsetRelativeToBase(entry.offset))); | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileRecordRegExpCachedResult(Node* node) | 
|  | { | 
|  | Edge globalObjectEdge = m_jit.graph().varArgChild(node, 0); | 
|  | Edge regExpEdge = m_jit.graph().varArgChild(node, 1); | 
|  | Edge stringEdge = m_jit.graph().varArgChild(node, 2); | 
|  | Edge startEdge = m_jit.graph().varArgChild(node, 3); | 
|  | Edge endEdge = m_jit.graph().varArgChild(node, 4); | 
|  |  | 
|  | SpeculateCellOperand globalObject(this, globalObjectEdge); | 
|  | SpeculateCellOperand regExp(this, regExpEdge); | 
|  | SpeculateCellOperand string(this, stringEdge); | 
|  | SpeculateInt32Operand start(this, startEdge); | 
|  | SpeculateInt32Operand end(this, endEdge); | 
|  |  | 
|  | GPRReg globalObjectGPR = globalObject.gpr(); | 
|  | GPRReg regExpGPR = regExp.gpr(); | 
|  | GPRReg stringGPR = string.gpr(); | 
|  | GPRReg startGPR = start.gpr(); | 
|  | GPRReg endGPR = end.gpr(); | 
|  |  | 
|  | ptrdiff_t offset = JSGlobalObject::regExpGlobalDataOffset() + RegExpGlobalData::offsetOfCachedResult(); | 
|  |  | 
|  | m_jit.storePtr( | 
|  | regExpGPR, | 
|  | JITCompiler::Address(globalObjectGPR, offset + RegExpCachedResult::offsetOfLastRegExp())); | 
|  | m_jit.storePtr( | 
|  | stringGPR, | 
|  | JITCompiler::Address(globalObjectGPR, offset + RegExpCachedResult::offsetOfLastInput())); | 
|  | m_jit.store32( | 
|  | startGPR, | 
|  | JITCompiler::Address( | 
|  | globalObjectGPR, | 
|  | offset + RegExpCachedResult::offsetOfResult() + OBJECT_OFFSETOF(MatchResult, start))); | 
|  | m_jit.store32( | 
|  | endGPR, | 
|  | JITCompiler::Address( | 
|  | globalObjectGPR, | 
|  | offset + RegExpCachedResult::offsetOfResult() + OBJECT_OFFSETOF(MatchResult, end))); | 
|  | m_jit.store8( | 
|  | TrustedImm32(0), | 
|  | JITCompiler::Address(globalObjectGPR, offset + RegExpCachedResult::offsetOfReified())); | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileDefineDataProperty(Node* node) | 
|  | { | 
|  | #if USE(JSVALUE64) | 
|  | static_assert(GPRInfo::numberOfRegisters >= 5, "We are assuming we have enough registers to make this call without incrementally setting up the arguments."); | 
|  | #else | 
|  | static_assert(GPRInfo::numberOfRegisters >= 6, "We are assuming we have enough registers to make this call without incrementally setting up the arguments."); | 
|  | #endif | 
|  |  | 
|  | SpeculateCellOperand base(this, m_jit.graph().varArgChild(node, 0)); | 
|  | GPRReg baseGPR = base.gpr(); | 
|  |  | 
|  | JSValueOperand value(this, m_jit.graph().varArgChild(node, 2)); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  |  | 
|  | SpeculateInt32Operand attributes(this, m_jit.graph().varArgChild(node, 3)); | 
|  | GPRReg attributesGPR = attributes.gpr(); | 
|  |  | 
|  | Edge& propertyEdge = m_jit.graph().varArgChild(node, 1); | 
|  | switch (propertyEdge.useKind()) { | 
|  | case StringUse: { | 
|  | SpeculateCellOperand property(this, propertyEdge); | 
|  | GPRReg propertyGPR = property.gpr(); | 
|  | speculateString(propertyEdge, propertyGPR); | 
|  |  | 
|  | useChildren(node); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(operationDefineDataPropertyString, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, propertyGPR, valueRegs, attributesGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | break; | 
|  | } | 
|  | case StringIdentUse: { | 
|  | SpeculateCellOperand property(this, propertyEdge); | 
|  | GPRTemporary ident(this); | 
|  |  | 
|  | GPRReg propertyGPR = property.gpr(); | 
|  | GPRReg identGPR = ident.gpr(); | 
|  |  | 
|  | speculateString(propertyEdge, propertyGPR); | 
|  | speculateStringIdentAndLoadStorage(propertyEdge, propertyGPR, identGPR); | 
|  |  | 
|  | useChildren(node); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(operationDefineDataPropertyStringIdent, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, identGPR, valueRegs, attributesGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | break; | 
|  | } | 
|  | case SymbolUse: { | 
|  | SpeculateCellOperand property(this, propertyEdge); | 
|  | GPRReg propertyGPR = property.gpr(); | 
|  | speculateSymbol(propertyEdge, propertyGPR); | 
|  |  | 
|  | useChildren(node); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(operationDefineDataPropertySymbol, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, propertyGPR, valueRegs, attributesGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | break; | 
|  | } | 
|  | case UntypedUse: { | 
|  | JSValueOperand property(this, propertyEdge); | 
|  | JSValueRegs propertyRegs = property.jsValueRegs(); | 
|  |  | 
|  | useChildren(node); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(operationDefineDataProperty, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, propertyRegs, valueRegs, attributesGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  |  | 
|  | noResult(node, UseChildrenCalledExplicitly); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileDefineAccessorProperty(Node* node) | 
|  | { | 
|  | #if USE(JSVALUE64) | 
|  | static_assert(GPRInfo::numberOfRegisters >= 5, "We are assuming we have enough registers to make this call without incrementally setting up the arguments."); | 
|  | #else | 
|  | static_assert(GPRInfo::numberOfRegisters >= 6, "We are assuming we have enough registers to make this call without incrementally setting up the arguments."); | 
|  | #endif | 
|  |  | 
|  | SpeculateCellOperand base(this, m_jit.graph().varArgChild(node, 0)); | 
|  | GPRReg baseGPR = base.gpr(); | 
|  |  | 
|  | SpeculateCellOperand getter(this, m_jit.graph().varArgChild(node, 2)); | 
|  | GPRReg getterGPR = getter.gpr(); | 
|  |  | 
|  | SpeculateCellOperand setter(this, m_jit.graph().varArgChild(node, 3)); | 
|  | GPRReg setterGPR = setter.gpr(); | 
|  |  | 
|  | SpeculateInt32Operand attributes(this, m_jit.graph().varArgChild(node, 4)); | 
|  | GPRReg attributesGPR = attributes.gpr(); | 
|  |  | 
|  | Edge& propertyEdge = m_jit.graph().varArgChild(node, 1); | 
|  | switch (propertyEdge.useKind()) { | 
|  | case StringUse: { | 
|  | SpeculateCellOperand property(this, propertyEdge); | 
|  | GPRReg propertyGPR = property.gpr(); | 
|  | speculateString(propertyEdge, propertyGPR); | 
|  |  | 
|  | useChildren(node); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(operationDefineAccessorPropertyString, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, propertyGPR, getterGPR, setterGPR, attributesGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | break; | 
|  | } | 
|  | case StringIdentUse: { | 
|  | SpeculateCellOperand property(this, propertyEdge); | 
|  | GPRTemporary ident(this); | 
|  |  | 
|  | GPRReg propertyGPR = property.gpr(); | 
|  | GPRReg identGPR = ident.gpr(); | 
|  |  | 
|  | speculateString(propertyEdge, propertyGPR); | 
|  | speculateStringIdentAndLoadStorage(propertyEdge, propertyGPR, identGPR); | 
|  |  | 
|  | useChildren(node); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(operationDefineAccessorPropertyStringIdent, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, identGPR, getterGPR, setterGPR, attributesGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | break; | 
|  | } | 
|  | case SymbolUse: { | 
|  | SpeculateCellOperand property(this, propertyEdge); | 
|  | GPRReg propertyGPR = property.gpr(); | 
|  | speculateSymbol(propertyEdge, propertyGPR); | 
|  |  | 
|  | useChildren(node); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(operationDefineAccessorPropertySymbol, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, propertyGPR, getterGPR, setterGPR, attributesGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | break; | 
|  | } | 
|  | case UntypedUse: { | 
|  | JSValueOperand property(this, propertyEdge); | 
|  | JSValueRegs propertyRegs = property.jsValueRegs(); | 
|  |  | 
|  | useChildren(node); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(operationDefineAccessorProperty, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, propertyRegs, getterGPR, setterGPR, attributesGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  |  | 
|  | noResult(node, UseChildrenCalledExplicitly); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::emitAllocateButterfly(GPRReg storageResultGPR, GPRReg sizeGPR, GPRReg scratch1, GPRReg scratch2, GPRReg scratch3, MacroAssembler::JumpList& slowCases) | 
|  | { | 
|  | RELEASE_ASSERT(RegisterSet(storageResultGPR, sizeGPR, scratch1, scratch2, scratch3).numberOfSetGPRs() == 5); | 
|  | ASSERT((1 << 3) == sizeof(JSValue)); | 
|  | m_jit.zeroExtend32ToWord(sizeGPR, scratch1); | 
|  | m_jit.lshift32(TrustedImm32(3), scratch1); | 
|  | m_jit.add32(TrustedImm32(sizeof(IndexingHeader)), scratch1, scratch2); | 
|  | #if ASSERT_ENABLED | 
|  | MacroAssembler::Jump didNotOverflow = m_jit.branch32(MacroAssembler::AboveOrEqual, scratch2, sizeGPR); | 
|  | m_jit.abortWithReason(UncheckedOverflow); | 
|  | didNotOverflow.link(&m_jit); | 
|  | #endif | 
|  | m_jit.emitAllocateVariableSized( | 
|  | storageResultGPR, vm().jsValueGigacageAuxiliarySpace, scratch2, scratch1, scratch3, slowCases); | 
|  | m_jit.addPtr(TrustedImm32(sizeof(IndexingHeader)), storageResultGPR); | 
|  |  | 
|  | m_jit.store32(sizeGPR, MacroAssembler::Address(storageResultGPR, Butterfly::offsetOfPublicLength())); | 
|  | m_jit.store32(sizeGPR, MacroAssembler::Address(storageResultGPR, Butterfly::offsetOfVectorLength())); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNormalizeMapKey(Node* node) | 
|  | { | 
|  | ASSERT(node->child1().useKind() == UntypedUse); | 
|  | JSValueOperand key(this, node->child1()); | 
|  | JSValueRegsTemporary result(this, Reuse, key); | 
|  | GPRTemporary scratch(this); | 
|  | FPRTemporary doubleValue(this); | 
|  | FPRTemporary temp(this); | 
|  |  | 
|  | JSValueRegs keyRegs = key.jsValueRegs(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  | FPRReg doubleValueFPR = doubleValue.fpr(); | 
|  | FPRReg tempFPR = temp.fpr(); | 
|  |  | 
|  | CCallHelpers::JumpList passThroughCases; | 
|  | CCallHelpers::JumpList doneCases; | 
|  |  | 
|  | passThroughCases.append(m_jit.branchIfNotNumber(keyRegs, scratchGPR)); | 
|  | passThroughCases.append(m_jit.branchIfInt32(keyRegs)); | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | m_jit.unboxDoubleWithoutAssertions(keyRegs.gpr(), scratchGPR, doubleValueFPR); | 
|  | #else | 
|  | unboxDouble(keyRegs.tagGPR(), keyRegs.payloadGPR(), doubleValueFPR, tempFPR); | 
|  | #endif | 
|  | auto notNaN = m_jit.branchIfNotNaN(doubleValueFPR); | 
|  | m_jit.moveTrustedValue(jsNaN(), resultRegs); | 
|  | doneCases.append(m_jit.jump()); | 
|  |  | 
|  | notNaN.link(&m_jit); | 
|  | m_jit.truncateDoubleToInt32(doubleValueFPR, scratchGPR); | 
|  | m_jit.convertInt32ToDouble(scratchGPR, tempFPR); | 
|  | passThroughCases.append(m_jit.branchDouble(JITCompiler::DoubleNotEqualAndOrdered, doubleValueFPR, tempFPR)); | 
|  |  | 
|  | m_jit.boxInt32(scratchGPR, resultRegs); | 
|  | doneCases.append(m_jit.jump()); | 
|  |  | 
|  | passThroughCases.link(&m_jit); | 
|  | m_jit.moveValueRegs(keyRegs, resultRegs); | 
|  |  | 
|  | doneCases.link(&m_jit); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetMapBucketHead(Node* node) | 
|  | { | 
|  | SpeculateCellOperand map(this, node->child1()); | 
|  | GPRTemporary bucket(this); | 
|  |  | 
|  | GPRReg mapGPR = map.gpr(); | 
|  | GPRReg bucketGPR = bucket.gpr(); | 
|  |  | 
|  | if (node->child1().useKind() == MapObjectUse) | 
|  | speculateMapObject(node->child1(), mapGPR); | 
|  | else if (node->child1().useKind() == SetObjectUse) | 
|  | speculateSetObject(node->child1(), mapGPR); | 
|  | else | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  |  | 
|  | ASSERT(HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfHead() == HashMapImpl<HashMapBucket<HashMapBucketDataKeyValue>>::offsetOfHead()); | 
|  | m_jit.loadPtr(MacroAssembler::Address(mapGPR, HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfHead()), bucketGPR); | 
|  | cellResult(bucketGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetMapBucketNext(Node* node) | 
|  | { | 
|  | SpeculateCellOperand bucket(this, node->child1()); | 
|  | GPRTemporary result(this); | 
|  |  | 
|  | GPRReg bucketGPR = bucket.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | ASSERT(HashMapBucket<HashMapBucketDataKey>::offsetOfNext() == HashMapBucket<HashMapBucketDataKeyValue>::offsetOfNext()); | 
|  | ASSERT(HashMapBucket<HashMapBucketDataKey>::offsetOfKey() == HashMapBucket<HashMapBucketDataKeyValue>::offsetOfKey()); | 
|  | m_jit.loadPtr(MacroAssembler::Address(bucketGPR, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfNext()), resultGPR); | 
|  |  | 
|  | MacroAssembler::Label loop = m_jit.label(); | 
|  | auto notBucket = m_jit.branchTestPtr(MacroAssembler::Zero, resultGPR); | 
|  | #if USE(JSVALUE32_64) | 
|  | auto done = m_jit.branch32(MacroAssembler::NotEqual, MacroAssembler::Address(resultGPR, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfKey() + TagOffset), TrustedImm32(JSValue::EmptyValueTag)); | 
|  | #else | 
|  | auto done = m_jit.branchTest64(MacroAssembler::NonZero, MacroAssembler::Address(resultGPR, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfKey())); | 
|  | #endif | 
|  | m_jit.loadPtr(MacroAssembler::Address(resultGPR, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfNext()), resultGPR); | 
|  | m_jit.jump().linkTo(loop, &m_jit); | 
|  |  | 
|  | notBucket.link(&m_jit); | 
|  | JSCell* sentinel = nullptr; | 
|  | if (node->bucketOwnerType() == BucketOwnerType::Map) | 
|  | sentinel = vm().sentinelMapBucket(); | 
|  | else { | 
|  | ASSERT(node->bucketOwnerType() == BucketOwnerType::Set); | 
|  | sentinel = vm().sentinelSetBucket(); | 
|  | } | 
|  | m_jit.move(TrustedImmPtr::weakPointer(m_jit.graph(), sentinel), resultGPR); | 
|  | done.link(&m_jit); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileLoadKeyFromMapBucket(Node* node) | 
|  | { | 
|  | SpeculateCellOperand bucket(this, node->child1()); | 
|  | JSValueRegsTemporary result(this); | 
|  |  | 
|  | GPRReg bucketGPR = bucket.gpr(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | m_jit.loadValue(MacroAssembler::Address(bucketGPR, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfKey()), resultRegs); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileLoadValueFromMapBucket(Node* node) | 
|  | { | 
|  | SpeculateCellOperand bucket(this, node->child1()); | 
|  | JSValueRegsTemporary result(this); | 
|  |  | 
|  | GPRReg bucketGPR = bucket.gpr(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | m_jit.loadValue(MacroAssembler::Address(bucketGPR, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfValue()), resultRegs); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileExtractValueFromWeakMapGet(Node* node) | 
|  | { | 
|  | JSValueOperand value(this, node->child1()); | 
|  | JSValueRegsTemporary result(this, Reuse, value); | 
|  |  | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | m_jit.moveValueRegs(valueRegs, resultRegs); | 
|  | auto done = m_jit.branchTestPtr(CCallHelpers::NonZero, resultRegs.payloadGPR()); | 
|  | m_jit.moveValue(jsUndefined(), resultRegs); | 
|  | done.link(&m_jit); | 
|  | #else | 
|  | auto isEmpty = m_jit.branchIfEmpty(valueRegs.tagGPR()); | 
|  | m_jit.moveValueRegs(valueRegs, resultRegs); | 
|  | auto done = m_jit.jump(); | 
|  |  | 
|  | isEmpty.link(&m_jit); | 
|  | m_jit.moveValue(jsUndefined(), resultRegs); | 
|  |  | 
|  | done.link(&m_jit); | 
|  | #endif | 
|  |  | 
|  | jsValueResult(resultRegs, node, DataFormatJS); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileThrow(Node* node) | 
|  | { | 
|  | JSValueOperand value(this, node->child1()); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  | flushRegisters(); | 
|  | callOperation(operationThrowDFG, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), valueRegs); | 
|  | m_jit.exceptionCheck(); | 
|  | m_jit.breakpoint(); | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileThrowStaticError(Node* node) | 
|  | { | 
|  | SpeculateCellOperand message(this, node->child1()); | 
|  | GPRReg messageGPR = message.gpr(); | 
|  | speculateString(node->child1(), messageGPR); | 
|  | flushRegisters(); | 
|  | callOperation(operationThrowStaticError, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), messageGPR, node->errorType()); | 
|  | m_jit.exceptionCheck(); | 
|  | m_jit.breakpoint(); | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetEnumerableLength(Node* node) | 
|  | { | 
|  | SpeculateCellOperand enumerator(this, node->child1()); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | m_jit.load32(MacroAssembler::Address(enumerator.gpr(), JSPropertyNameEnumerator::indexedLengthOffset()), resultGPR); | 
|  | strictInt32Result(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileHasGenericProperty(Node* node) | 
|  | { | 
|  | JSValueOperand base(this, node->child1()); | 
|  | SpeculateCellOperand property(this, node->child2()); | 
|  |  | 
|  | JSValueRegs baseRegs = base.jsValueRegs(); | 
|  | GPRReg propertyGPR = property.gpr(); | 
|  |  | 
|  | flushRegisters(); | 
|  | JSValueRegsFlushedCallResult result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | callOperation(operationHasGenericProperty, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseRegs, propertyGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | blessedBooleanResult(resultRegs.payloadGPR(), node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileToIndexString(Node* node) | 
|  | { | 
|  | SpeculateInt32Operand index(this, node->child1()); | 
|  | GPRReg indexGPR = index.gpr(); | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | callOperation(operationToIndexString, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), indexGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compilePutByIdFlush(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | JSValueOperand value(this, node->child2()); | 
|  | GPRTemporary scratch(this); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  | flushRegisters(); | 
|  |  | 
|  | cachedPutById(node->origin.semantic, baseGPR, valueRegs, scratchGPR, node->cacheableIdentifier(), NotDirect, node->ecmaMode(), MacroAssembler::Jump(), DontSpill); | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compilePutById(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | JSValueOperand value(this, node->child2()); | 
|  | GPRTemporary scratch(this); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  |  | 
|  | cachedPutById(node->origin.semantic, baseGPR, valueRegs, scratchGPR, node->cacheableIdentifier(), NotDirect, node->ecmaMode()); | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compilePutByIdDirect(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | JSValueOperand value(this, node->child2()); | 
|  | GPRTemporary scratch(this); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  |  | 
|  | cachedPutById(node->origin.semantic, baseGPR, valueRegs, scratchGPR, node->cacheableIdentifier(), Direct, node->ecmaMode()); | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compilePutByIdWithThis(Node* node) | 
|  | { | 
|  | JSValueOperand base(this, node->child1()); | 
|  | JSValueRegs baseRegs = base.jsValueRegs(); | 
|  | JSValueOperand thisValue(this, node->child2()); | 
|  | JSValueRegs thisRegs = thisValue.jsValueRegs(); | 
|  | JSValueOperand value(this, node->child3()); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(node->ecmaMode().isStrict() ? operationPutByIdWithThisStrict : operationPutByIdWithThis, | 
|  | TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseRegs, thisRegs, valueRegs, node->cacheableIdentifier().rawBits()); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetByOffset(Node* node) | 
|  | { | 
|  | StorageOperand storage(this, node->child1()); | 
|  | JSValueRegsTemporary result(this, Reuse, storage); | 
|  |  | 
|  | GPRReg storageGPR = storage.gpr(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | StorageAccessData& storageAccessData = node->storageAccessData(); | 
|  |  | 
|  | m_jit.loadValue(JITCompiler::Address(storageGPR, offsetRelativeToBase(storageAccessData.offset)), resultRegs); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compilePutByOffset(Node* node) | 
|  | { | 
|  | StorageOperand storage(this, node->child1()); | 
|  | JSValueOperand value(this, node->child3()); | 
|  |  | 
|  | GPRReg storageGPR = storage.gpr(); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  |  | 
|  | speculate(node, node->child2()); | 
|  |  | 
|  | StorageAccessData& storageAccessData = node->storageAccessData(); | 
|  |  | 
|  | m_jit.storeValue(valueRegs, JITCompiler::Address(storageGPR, offsetRelativeToBase(storageAccessData.offset))); | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileMatchStructure(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | GPRTemporary temp(this); | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  |  | 
|  | m_jit.load32(JITCompiler::Address(baseGPR, JSCell::structureIDOffset()), tempGPR); | 
|  |  | 
|  | auto& variants = node->matchStructureData().variants; | 
|  | Vector<int64_t> cases; | 
|  | for (MatchStructureVariant& variant : variants) | 
|  | cases.append(bitwise_cast<int32_t>(variant.structure->id())); | 
|  |  | 
|  | BinarySwitch binarySwitch(tempGPR, cases, BinarySwitch::Int32); | 
|  | JITCompiler::JumpList done; | 
|  | while (binarySwitch.advance(m_jit)) { | 
|  | m_jit.boxBooleanPayload(variants[binarySwitch.caseIndex()].result, tempGPR); | 
|  | done.append(m_jit.jump()); | 
|  | } | 
|  | speculationCheck(BadCache, JSValueRegs(), node, binarySwitch.fallThrough()); | 
|  |  | 
|  | done.link(&m_jit); | 
|  |  | 
|  | blessedBooleanResult(tempGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileHasStructureProperty(Node* node) | 
|  | { | 
|  | JSValueOperand base(this, node->child1()); | 
|  | SpeculateCellOperand property(this, node->child2()); | 
|  | SpeculateCellOperand enumerator(this, node->child3()); | 
|  | JSValueRegsTemporary result(this); | 
|  |  | 
|  | JSValueRegs baseRegs = base.jsValueRegs(); | 
|  | GPRReg propertyGPR = property.gpr(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | CCallHelpers::JumpList wrongStructure; | 
|  |  | 
|  | wrongStructure.append(m_jit.branchIfNotCell(baseRegs)); | 
|  |  | 
|  | m_jit.load32(MacroAssembler::Address(baseRegs.payloadGPR(), JSCell::structureIDOffset()), resultRegs.payloadGPR()); | 
|  | wrongStructure.append(m_jit.branch32(MacroAssembler::NotEqual, | 
|  | resultRegs.payloadGPR(), | 
|  | MacroAssembler::Address(enumerator.gpr(), JSPropertyNameEnumerator::cachedStructureIDOffset()))); | 
|  |  | 
|  | moveTrueTo(resultRegs.payloadGPR()); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(wrongStructure, this, operationHasGenericProperty, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseRegs, propertyGPR)); | 
|  | blessedBooleanResult(resultRegs.payloadGPR(), node); | 
|  | } | 
|  |  | 
|  | template <typename Function> | 
|  | void SpeculativeJIT::compileHasOwnStructurePropertyImpl(Node* node, Function slowPath) | 
|  | { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | SpeculateCellOperand property(this, node->child2()); | 
|  | SpeculateCellOperand enumerator(this, node->child3()); | 
|  | JSValueRegsTemporary result(this); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | GPRReg propertyGPR = property.gpr(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | CCallHelpers::JumpList wrongStructure; | 
|  |  | 
|  | m_jit.load32(MacroAssembler::Address(baseGPR, JSCell::structureIDOffset()), resultRegs.payloadGPR()); | 
|  | wrongStructure.append(m_jit.branch32(MacroAssembler::NotEqual, | 
|  | resultRegs.payloadGPR(), | 
|  | MacroAssembler::Address(enumerator.gpr(), JSPropertyNameEnumerator::cachedStructureIDOffset()))); | 
|  |  | 
|  | moveTrueTo(resultRegs.payloadGPR()); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(wrongStructure, this, slowPath, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, propertyGPR)); | 
|  | blessedBooleanResult(resultRegs.payloadGPR(), node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileHasOwnStructureProperty(Node* node) | 
|  | { | 
|  | compileHasOwnStructurePropertyImpl(node, operationHasOwnStructureProperty); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileInStructureProperty(Node* node) | 
|  | { | 
|  | compileHasOwnStructurePropertyImpl(node, operationInStructureProperty); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetPropertyEnumerator(Node* node) | 
|  | { | 
|  | if (node->child1().useKind() == CellUse) { | 
|  | SpeculateCellOperand base(this, node->child1()); | 
|  | GPRReg baseGPR = base.gpr(); | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | callOperation(operationGetPropertyEnumeratorCell, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | JSValueOperand base(this, node->child1()); | 
|  | JSValueRegs baseRegs = base.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | callOperation(operationGetPropertyEnumerator, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseRegs); | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetEnumeratorPname(Node* node) | 
|  | { | 
|  | ASSERT(node->op() == GetEnumeratorStructurePname || node->op() == GetEnumeratorGenericPname); | 
|  | SpeculateCellOperand enumerator(this, node->child1()); | 
|  | SpeculateStrictInt32Operand index(this, node->child2()); | 
|  | GPRTemporary scratch(this); | 
|  | JSValueRegsTemporary result(this); | 
|  |  | 
|  | GPRReg enumeratorGPR = enumerator.gpr(); | 
|  | GPRReg indexGPR = index.gpr(); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, indexGPR, | 
|  | MacroAssembler::Address(enumeratorGPR, (node->op() == GetEnumeratorStructurePname) | 
|  | ? JSPropertyNameEnumerator::endStructurePropertyIndexOffset() | 
|  | : JSPropertyNameEnumerator::endGenericPropertyIndexOffset())); | 
|  |  | 
|  | m_jit.moveValue(jsNull(), resultRegs); | 
|  |  | 
|  | MacroAssembler::Jump done = m_jit.jump(); | 
|  | inBounds.link(&m_jit); | 
|  |  | 
|  | m_jit.loadPtr(MacroAssembler::Address(enumeratorGPR, JSPropertyNameEnumerator::cachedPropertyNamesVectorOffset()), scratchGPR); | 
|  | m_jit.loadPtr(MacroAssembler::BaseIndex(scratchGPR, indexGPR, MacroAssembler::ScalePtr), resultRegs.payloadGPR()); | 
|  | #if USE(JSVALUE32_64) | 
|  | m_jit.move(MacroAssembler::TrustedImm32(JSValue::CellTag), resultRegs.tagGPR()); | 
|  | #endif | 
|  |  | 
|  | done.link(&m_jit); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetExecutable(Node* node) | 
|  | { | 
|  | SpeculateCellOperand function(this, node->child1()); | 
|  | GPRTemporary result(this, Reuse, function); | 
|  | GPRReg functionGPR = function.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | speculateCellType(node->child1(), functionGPR, SpecFunction, JSFunctionType); | 
|  | m_jit.loadPtr(JITCompiler::Address(functionGPR, JSFunction::offsetOfExecutableOrRareData()), resultGPR); | 
|  | auto hasExecutable = m_jit.branchTestPtr(CCallHelpers::Zero, resultGPR, CCallHelpers::TrustedImm32(JSFunction::rareDataTag)); | 
|  | m_jit.loadPtr(CCallHelpers::Address(resultGPR, FunctionRareData::offsetOfExecutable() - JSFunction::rareDataTag), resultGPR); | 
|  | hasExecutable.link(&m_jit); | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetGetter(Node* node) | 
|  | { | 
|  | SpeculateCellOperand op1(this, node->child1()); | 
|  | GPRTemporary result(this, Reuse, op1); | 
|  |  | 
|  | GPRReg op1GPR = op1.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | m_jit.loadPtr(JITCompiler::Address(op1GPR, GetterSetter::offsetOfGetter()), resultGPR); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetSetter(Node* node) | 
|  | { | 
|  | SpeculateCellOperand op1(this, node->child1()); | 
|  | GPRTemporary result(this, Reuse, op1); | 
|  |  | 
|  | GPRReg op1GPR = op1.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | m_jit.loadPtr(JITCompiler::Address(op1GPR, GetterSetter::offsetOfSetter()), resultGPR); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetCallee(Node* node) | 
|  | { | 
|  | GPRTemporary result(this); | 
|  | m_jit.loadPtr(JITCompiler::payloadFor(CallFrameSlot::callee), result.gpr()); | 
|  | cellResult(result.gpr(), node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileSetCallee(Node* node) | 
|  | { | 
|  | SpeculateCellOperand callee(this, node->child1()); | 
|  | m_jit.storeCell(callee.gpr(), JITCompiler::payloadFor(CallFrameSlot::callee)); | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetArgumentCountIncludingThis(Node* node) | 
|  | { | 
|  | GPRTemporary result(this); | 
|  | VirtualRegister argumentCountRegister; | 
|  | if (InlineCallFrame* inlineCallFrame = node->argumentsInlineCallFrame()) | 
|  | argumentCountRegister = inlineCallFrame->argumentCountRegister; | 
|  | else | 
|  | argumentCountRegister = CallFrameSlot::argumentCountIncludingThis; | 
|  | m_jit.load32(JITCompiler::payloadFor(argumentCountRegister), result.gpr()); | 
|  | strictInt32Result(result.gpr(), node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileSetArgumentCountIncludingThis(Node* node) | 
|  | { | 
|  | m_jit.store32(TrustedImm32(node->argumentCountIncludingThis()), JITCompiler::payloadFor(CallFrameSlot::argumentCountIncludingThis)); | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileStrCat(Node* node) | 
|  | { | 
|  | JSValueOperand op1(this, node->child1(), ManualOperandSpeculation); | 
|  | JSValueOperand op2(this, node->child2(), ManualOperandSpeculation); | 
|  | JSValueOperand op3(this, node->child3(), ManualOperandSpeculation); | 
|  |  | 
|  | JSValueRegs op1Regs = op1.jsValueRegs(); | 
|  | JSValueRegs op2Regs = op2.jsValueRegs(); | 
|  | JSValueRegs op3Regs; | 
|  |  | 
|  | if (node->child3()) | 
|  | op3Regs = op3.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | GPRFlushedCallResult result(this); | 
|  | if (node->child3()) | 
|  | callOperation(operationStrCat3, result.gpr(), TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), op1Regs, op2Regs, op3Regs); | 
|  | else | 
|  | callOperation(operationStrCat2, result.gpr(), TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), op1Regs, op2Regs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | cellResult(result.gpr(), node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNewArrayBuffer(Node* node) | 
|  | { | 
|  | JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic); | 
|  | auto* array = node->castOperand<JSImmutableButterfly*>(); | 
|  |  | 
|  | IndexingType indexingMode = node->indexingMode(); | 
|  | RegisteredStructure structure = m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingMode)); | 
|  |  | 
|  | if (!globalObject->isHavingABadTime() && !hasAnyArrayStorage(indexingMode)) { | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary scratch1(this); | 
|  | GPRTemporary scratch2(this); | 
|  |  | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg scratch1GPR = scratch1.gpr(); | 
|  | GPRReg scratch2GPR = scratch2.gpr(); | 
|  |  | 
|  | MacroAssembler::JumpList slowCases; | 
|  |  | 
|  | emitAllocateJSObject<JSArray>(resultGPR, TrustedImmPtr(structure), TrustedImmPtr(array->toButterfly()), scratch1GPR, scratch2GPR, slowCases); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowCases, this, operationNewArrayBuffer, result.gpr(), &vm(), structure, array)); | 
|  |  | 
|  | DFG_ASSERT(m_jit.graph(), node, indexingMode & IsArray, indexingMode); | 
|  | cellResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  |  | 
|  | callOperation(operationNewArrayBuffer, result.gpr(), &vm(), structure, TrustedImmPtr(node->cellOperand())); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | cellResult(result.gpr(), node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNewArrayWithSize(Node* node) | 
|  | { | 
|  | JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic); | 
|  | if (!globalObject->isHavingABadTime() && !hasAnyArrayStorage(node->indexingType())) { | 
|  | SpeculateStrictInt32Operand size(this, node->child1()); | 
|  | GPRTemporary result(this); | 
|  |  | 
|  | GPRReg sizeGPR = size.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | compileAllocateNewArrayWithSize(globalObject, resultGPR, sizeGPR, node->indexingType()); | 
|  | cellResult(resultGPR, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | SpeculateStrictInt32Operand size(this, node->child1()); | 
|  | GPRReg sizeGPR = size.gpr(); | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg structureGPR = AssemblyHelpers::selectScratchGPR(sizeGPR); | 
|  | MacroAssembler::Jump bigLength = m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH)); | 
|  | m_jit.move(TrustedImmPtr(m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(node->indexingType()))), structureGPR); | 
|  | MacroAssembler::Jump done = m_jit.jump(); | 
|  | bigLength.link(&m_jit); | 
|  | m_jit.move(TrustedImmPtr(m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage))), structureGPR); | 
|  | done.link(&m_jit); | 
|  | callOperation(operationNewArrayWithSize, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), structureGPR, sizeGPR, nullptr); | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNewTypedArray(Node* node) | 
|  | { | 
|  | switch (node->child1().useKind()) { | 
|  | case Int32Use: | 
|  | compileNewTypedArrayWithSize(node); | 
|  | break; | 
|  | case UntypedUse: { | 
|  | JSValueOperand argument(this, node->child1()); | 
|  | JSValueRegs argumentRegs = argument.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  |  | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic); | 
|  | callOperation( | 
|  | operationNewTypedArrayWithOneArgumentForType(node->typedArrayType()), | 
|  | resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), m_jit.graph().registerStructure(globalObject->typedArrayStructureConcurrently(node->typedArrayType())), argumentRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileToThis(Node* node) | 
|  | { | 
|  | ASSERT(node->child1().useKind() == UntypedUse); | 
|  | JSValueOperand thisValue(this, node->child1()); | 
|  | JSValueRegsTemporary temp(this); | 
|  |  | 
|  | JSValueRegs thisValueRegs = thisValue.jsValueRegs(); | 
|  | JSValueRegs tempRegs = temp.regs(); | 
|  |  | 
|  | MacroAssembler::JumpList slowCases; | 
|  | slowCases.append(m_jit.branchIfNotCell(thisValueRegs)); | 
|  | slowCases.append( | 
|  | m_jit.branchTest8( | 
|  | MacroAssembler::NonZero, | 
|  | MacroAssembler::Address(thisValueRegs.payloadGPR(), JSCell::typeInfoFlagsOffset()), | 
|  | MacroAssembler::TrustedImm32(OverridesToThis))); | 
|  | m_jit.moveValueRegs(thisValueRegs, tempRegs); | 
|  |  | 
|  | J_JITOperation_GJ function; | 
|  | if (node->ecmaMode().isStrict()) | 
|  | function = operationToThisStrict; | 
|  | else | 
|  | function = operationToThis; | 
|  | addSlowPathGenerator(slowPathCall(slowCases, this, function, tempRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), thisValueRegs)); | 
|  |  | 
|  | jsValueResult(tempRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileObjectKeysOrObjectGetOwnPropertyNames(Node* node) | 
|  | { | 
|  | switch (node->child1().useKind()) { | 
|  | case ObjectUse: { | 
|  | if (m_graph.isWatchingHavingABadTimeWatchpoint(node)) { | 
|  | SpeculateCellOperand object(this, node->child1()); | 
|  | GPRTemporary structure(this); | 
|  | GPRTemporary scratch(this); | 
|  | GPRTemporary scratch2(this); | 
|  | GPRTemporary scratch3(this); | 
|  | GPRTemporary result(this); | 
|  |  | 
|  | GPRReg objectGPR = object.gpr(); | 
|  | GPRReg structureGPR = structure.gpr(); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  | GPRReg scratch2GPR = scratch2.gpr(); | 
|  | GPRReg scratch3GPR = scratch3.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | speculateObject(node->child1(), objectGPR); | 
|  |  | 
|  | CCallHelpers::JumpList slowCases; | 
|  | m_jit.emitLoadStructure(vm(), objectGPR, structureGPR, scratchGPR); | 
|  | m_jit.loadPtr(CCallHelpers::Address(structureGPR, Structure::previousOrRareDataOffset()), scratchGPR); | 
|  |  | 
|  | slowCases.append(m_jit.branchTestPtr(CCallHelpers::Zero, scratchGPR)); | 
|  | slowCases.append(m_jit.branch32(CCallHelpers::Equal, CCallHelpers::Address(scratchGPR, JSCell::structureIDOffset()), TrustedImm32(bitwise_cast<int32_t>(vm().structureStructure->structureID())))); | 
|  |  | 
|  | m_jit.loadPtr(CCallHelpers::Address(scratchGPR, StructureRareData::offsetOfCachedPropertyNames(node->op() == ObjectKeys ? CachedPropertyNamesKind::Keys : CachedPropertyNamesKind::GetOwnPropertyNames)), scratchGPR); | 
|  |  | 
|  | ASSERT(bitwise_cast<uintptr_t>(StructureRareData::cachedPropertyNamesSentinel()) == 1); | 
|  | slowCases.append(m_jit.branchPtr(CCallHelpers::BelowOrEqual, scratchGPR, TrustedImmPtr(bitwise_cast<void*>(StructureRareData::cachedPropertyNamesSentinel())))); | 
|  |  | 
|  | MacroAssembler::JumpList slowButArrayBufferCases; | 
|  |  | 
|  | JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic); | 
|  | RegisteredStructure arrayStructure = m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(CopyOnWriteArrayWithContiguous)); | 
|  |  | 
|  | m_jit.move(scratchGPR, scratch3GPR); | 
|  | m_jit.addPtr(TrustedImm32(JSImmutableButterfly::offsetOfData()), scratchGPR); | 
|  |  | 
|  | emitAllocateJSObject<JSArray>(resultGPR, TrustedImmPtr(arrayStructure), scratchGPR, structureGPR, scratch2GPR, slowButArrayBufferCases); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowButArrayBufferCases, this, operationNewArrayBuffer, resultGPR, &vm(), arrayStructure, scratch3GPR)); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowCases, this, node->op() == ObjectKeys ? operationObjectKeysObject : operationObjectGetOwnPropertyNamesObject, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), objectGPR)); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | break; | 
|  | } | 
|  |  | 
|  | SpeculateCellOperand object(this, node->child1()); | 
|  |  | 
|  | GPRReg objectGPR = object.gpr(); | 
|  |  | 
|  | speculateObject(node->child1(), objectGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | callOperation(node->op() == ObjectKeys ? operationObjectKeysObject : operationObjectGetOwnPropertyNamesObject, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), objectGPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case UntypedUse: { | 
|  | JSValueOperand object(this, node->child1()); | 
|  |  | 
|  | JSValueRegs objectRegs = object.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | callOperation(node->op() == ObjectKeys ? operationObjectKeys : operationObjectGetOwnPropertyNames, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), objectRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileObjectCreate(Node* node) | 
|  | { | 
|  | switch (node->child1().useKind()) { | 
|  | case ObjectUse: { | 
|  | SpeculateCellOperand prototype(this, node->child1()); | 
|  |  | 
|  | GPRReg prototypeGPR = prototype.gpr(); | 
|  |  | 
|  | speculateObject(node->child1(), prototypeGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | callOperation(operationObjectCreateObject, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), prototypeGPR); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case UntypedUse: { | 
|  | JSValueOperand prototype(this, node->child1()); | 
|  |  | 
|  | JSValueRegs prototypeRegs = prototype.jsValueRegs(); | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | callOperation(operationObjectCreate, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), prototypeRegs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCreateThis(Node* node) | 
|  | { | 
|  | // Note that there is not so much profit to speculate here. The only things we | 
|  | // speculate on are (1) that it's a cell, since that eliminates cell checks | 
|  | // later if the proto is reused, and (2) if we have a FinalObject prediction | 
|  | // then we speculate because we want to get recompiled if it isn't (since | 
|  | // otherwise we'd start taking slow path a lot). | 
|  |  | 
|  | SpeculateCellOperand callee(this, node->child1()); | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary allocator(this); | 
|  | GPRTemporary structure(this); | 
|  | GPRTemporary scratch(this); | 
|  |  | 
|  | GPRReg calleeGPR = callee.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg allocatorGPR = allocator.gpr(); | 
|  | GPRReg structureGPR = structure.gpr(); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  | // Rare data is only used to access the allocator & structure | 
|  | // We can avoid using an additional GPR this way | 
|  | GPRReg rareDataGPR = structureGPR; | 
|  | GPRReg inlineCapacityGPR = rareDataGPR; | 
|  |  | 
|  | MacroAssembler::JumpList slowPath; | 
|  |  | 
|  | slowPath.append(m_jit.branchIfNotFunction(calleeGPR)); | 
|  | m_jit.loadPtr(JITCompiler::Address(calleeGPR, JSFunction::offsetOfExecutableOrRareData()), rareDataGPR); | 
|  | slowPath.append(m_jit.branchTestPtr(MacroAssembler::Zero, rareDataGPR, CCallHelpers::TrustedImm32(JSFunction::rareDataTag))); | 
|  | m_jit.loadPtr(JITCompiler::Address(rareDataGPR, FunctionRareData::offsetOfObjectAllocationProfile() + ObjectAllocationProfileWithPrototype::offsetOfAllocator() - JSFunction::rareDataTag), allocatorGPR); | 
|  | m_jit.loadPtr(JITCompiler::Address(rareDataGPR, FunctionRareData::offsetOfObjectAllocationProfile() + ObjectAllocationProfileWithPrototype::offsetOfStructure() - JSFunction::rareDataTag), structureGPR); | 
|  |  | 
|  | auto butterfly = TrustedImmPtr(nullptr); | 
|  | emitAllocateJSObject(resultGPR, JITAllocator::variable(), allocatorGPR, structureGPR, butterfly, scratchGPR, slowPath); | 
|  |  | 
|  | m_jit.load8(JITCompiler::Address(structureGPR, Structure::inlineCapacityOffset()), inlineCapacityGPR); | 
|  | m_jit.emitInitializeInlineStorage(resultGPR, inlineCapacityGPR); | 
|  | m_jit.mutatorFence(vm()); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowPath, this, operationCreateThis, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), calleeGPR, node->inlineCapacity())); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCreatePromise(Node* node) | 
|  | { | 
|  | JSGlobalObject* globalObject = m_jit.globalObjectFor(node->origin.semantic); | 
|  |  | 
|  | SpeculateCellOperand callee(this, node->child1()); | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary structure(this); | 
|  | GPRTemporary scratch1(this); | 
|  | GPRTemporary scratch2(this); | 
|  |  | 
|  | GPRReg calleeGPR = callee.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg structureGPR = structure.gpr(); | 
|  | GPRReg scratch1GPR = scratch1.gpr(); | 
|  | GPRReg scratch2GPR = scratch2.gpr(); | 
|  | // Rare data is only used to access the allocator & structure | 
|  | // We can avoid using an additional GPR this way | 
|  | GPRReg rareDataGPR = structureGPR; | 
|  |  | 
|  | m_jit.move(TrustedImmPtr(m_jit.graph().registerStructure(node->isInternalPromise() ? globalObject->internalPromiseStructure() : globalObject->promiseStructure())), structureGPR); | 
|  | auto fastPromisePath = m_jit.branchPtr(CCallHelpers::Equal, calleeGPR, TrustedImmPtr::weakPointer(m_jit.graph(), node->isInternalPromise() ? globalObject->internalPromiseConstructor() : globalObject->promiseConstructor())); | 
|  |  | 
|  | MacroAssembler::JumpList slowCases; | 
|  |  | 
|  | slowCases.append(m_jit.branchIfNotFunction(calleeGPR)); | 
|  | m_jit.loadPtr(JITCompiler::Address(calleeGPR, JSFunction::offsetOfExecutableOrRareData()), rareDataGPR); | 
|  | slowCases.append(m_jit.branchTestPtr(MacroAssembler::Zero, rareDataGPR, CCallHelpers::TrustedImm32(JSFunction::rareDataTag))); | 
|  | m_jit.loadPtr(JITCompiler::Address(rareDataGPR, FunctionRareData::offsetOfInternalFunctionAllocationProfile() + InternalFunctionAllocationProfile::offsetOfStructure() - JSFunction::rareDataTag), structureGPR); | 
|  | slowCases.append(m_jit.branchTestPtr(CCallHelpers::Zero, structureGPR)); | 
|  | m_jit.move(TrustedImmPtr(node->isInternalPromise() ? JSInternalPromise::info() : JSPromise::info()), scratch1GPR); | 
|  | slowCases.append(m_jit.branchPtr(CCallHelpers::NotEqual, scratch1GPR, CCallHelpers::Address(structureGPR, Structure::classInfoOffset()))); | 
|  | m_jit.move(TrustedImmPtr::weakPointer(m_jit.graph(), globalObject), scratch1GPR); | 
|  | slowCases.append(m_jit.branchPtr(CCallHelpers::NotEqual, scratch1GPR, CCallHelpers::Address(structureGPR, Structure::globalObjectOffset()))); | 
|  |  | 
|  | fastPromisePath.link(&m_jit); | 
|  | auto butterfly = TrustedImmPtr(nullptr); | 
|  | if (node->isInternalPromise()) | 
|  | emitAllocateJSObjectWithKnownSize<JSInternalPromise>(resultGPR, structureGPR, butterfly, scratch1GPR, scratch2GPR, slowCases, sizeof(JSInternalPromise)); | 
|  | else | 
|  | emitAllocateJSObjectWithKnownSize<JSPromise>(resultGPR, structureGPR, butterfly, scratch1GPR, scratch2GPR, slowCases, sizeof(JSPromise)); | 
|  | m_jit.storeTrustedValue(jsNumber(static_cast<unsigned>(JSPromise::Status::Pending)), CCallHelpers::Address(resultGPR, JSInternalFieldObjectImpl<>::offsetOfInternalField(static_cast<unsigned>(JSPromise::Field::Flags)))); | 
|  | m_jit.storeTrustedValue(jsUndefined(), CCallHelpers::Address(resultGPR, JSInternalFieldObjectImpl<>::offsetOfInternalField(static_cast<unsigned>(JSPromise::Field::ReactionsOrResult)))); | 
|  | m_jit.mutatorFence(m_jit.vm()); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowCases, this, node->isInternalPromise() ? operationCreateInternalPromise : operationCreatePromise, resultGPR, TrustedImmPtr::weakPointer(m_jit.graph(), globalObject), calleeGPR)); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  |  | 
|  | template<typename JSClass, typename Operation> | 
|  | void SpeculativeJIT::compileCreateInternalFieldObject(Node* node, Operation operation) | 
|  | { | 
|  | JSGlobalObject* globalObject = m_jit.globalObjectFor(node->origin.semantic); | 
|  |  | 
|  | SpeculateCellOperand callee(this, node->child1()); | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary structure(this); | 
|  | GPRTemporary scratch1(this); | 
|  | GPRTemporary scratch2(this); | 
|  |  | 
|  | GPRReg calleeGPR = callee.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg structureGPR = structure.gpr(); | 
|  | GPRReg scratch1GPR = scratch1.gpr(); | 
|  | GPRReg scratch2GPR = scratch2.gpr(); | 
|  | // Rare data is only used to access the allocator & structure | 
|  | // We can avoid using an additional GPR this way | 
|  | GPRReg rareDataGPR = structureGPR; | 
|  |  | 
|  | MacroAssembler::JumpList slowCases; | 
|  |  | 
|  | slowCases.append(m_jit.branchIfNotFunction(calleeGPR)); | 
|  | m_jit.loadPtr(JITCompiler::Address(calleeGPR, JSFunction::offsetOfExecutableOrRareData()), rareDataGPR); | 
|  | slowCases.append(m_jit.branchTestPtr(MacroAssembler::Zero, rareDataGPR, CCallHelpers::TrustedImm32(JSFunction::rareDataTag))); | 
|  | m_jit.loadPtr(JITCompiler::Address(rareDataGPR, FunctionRareData::offsetOfInternalFunctionAllocationProfile() + InternalFunctionAllocationProfile::offsetOfStructure() - JSFunction::rareDataTag), structureGPR); | 
|  | slowCases.append(m_jit.branchTestPtr(CCallHelpers::Zero, structureGPR)); | 
|  | m_jit.move(TrustedImmPtr(JSClass::info()), scratch1GPR); | 
|  | slowCases.append(m_jit.branchPtr(CCallHelpers::NotEqual, scratch1GPR, CCallHelpers::Address(structureGPR, Structure::classInfoOffset()))); | 
|  | m_jit.move(TrustedImmPtr::weakPointer(m_jit.graph(), globalObject), scratch1GPR); | 
|  | slowCases.append(m_jit.branchPtr(CCallHelpers::NotEqual, scratch1GPR, CCallHelpers::Address(structureGPR, Structure::globalObjectOffset()))); | 
|  |  | 
|  | auto butterfly = TrustedImmPtr(nullptr); | 
|  | emitAllocateJSObjectWithKnownSize<JSClass>(resultGPR, structureGPR, butterfly, scratch1GPR, scratch2GPR, slowCases, sizeof(JSClass)); | 
|  | auto initialValues = JSClass::initialValues(); | 
|  | ASSERT(initialValues.size() == JSClass::numberOfInternalFields); | 
|  | for (unsigned index = 0; index < initialValues.size(); ++index) | 
|  | m_jit.storeTrustedValue(initialValues[index], CCallHelpers::Address(resultGPR, JSInternalFieldObjectImpl<>::offsetOfInternalField(index))); | 
|  | m_jit.mutatorFence(m_jit.vm()); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowCases, this, operation, resultGPR, TrustedImmPtr::weakPointer(m_jit.graph(), globalObject), calleeGPR)); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCreateGenerator(Node* node) | 
|  | { | 
|  | compileCreateInternalFieldObject<JSGenerator>(node, operationCreateGenerator); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCreateAsyncGenerator(Node* node) | 
|  | { | 
|  | compileCreateInternalFieldObject<JSAsyncGenerator>(node, operationCreateAsyncGenerator); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNewObject(Node* node) | 
|  | { | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary allocator(this); | 
|  | GPRTemporary scratch(this); | 
|  |  | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg allocatorGPR = allocator.gpr(); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  |  | 
|  | MacroAssembler::JumpList slowPath; | 
|  |  | 
|  | RegisteredStructure structure = node->structure(); | 
|  | size_t allocationSize = JSFinalObject::allocationSize(structure->inlineCapacity()); | 
|  | Allocator allocatorValue = allocatorForNonVirtualConcurrently<JSFinalObject>(vm(), allocationSize, AllocatorForMode::AllocatorIfExists); | 
|  | if (!allocatorValue) | 
|  | slowPath.append(m_jit.jump()); | 
|  | else { | 
|  | auto butterfly = TrustedImmPtr(nullptr); | 
|  | emitAllocateJSObject(resultGPR, JITAllocator::constant(allocatorValue), allocatorGPR, TrustedImmPtr(structure), butterfly, scratchGPR, slowPath); | 
|  | m_jit.emitInitializeInlineStorage(resultGPR, structure->inlineCapacity()); | 
|  | m_jit.mutatorFence(vm()); | 
|  | } | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowPath, this, operationNewObject, resultGPR, &vm(), structure)); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | template<typename JSClass, typename Operation> | 
|  | void SpeculativeJIT::compileNewInternalFieldObjectImpl(Node* node, Operation operation) | 
|  | { | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary scratch1(this); | 
|  | GPRTemporary scratch2(this); | 
|  |  | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg scratch1GPR = scratch1.gpr(); | 
|  | GPRReg scratch2GPR = scratch2.gpr(); | 
|  |  | 
|  | MacroAssembler::JumpList slowCases; | 
|  |  | 
|  | FrozenValue* structure = m_graph.freezeStrong(node->structure().get()); | 
|  | auto butterfly = TrustedImmPtr(nullptr); | 
|  | emitAllocateJSObjectWithKnownSize<JSClass>(resultGPR, TrustedImmPtr(structure), butterfly, scratch1GPR, scratch2GPR, slowCases, sizeof(JSClass)); | 
|  | auto initialValues = JSClass::initialValues(); | 
|  | static_assert(initialValues.size() == JSClass::numberOfInternalFields); | 
|  | for (unsigned index = 0; index < initialValues.size(); ++index) | 
|  | m_jit.storeTrustedValue(initialValues[index], CCallHelpers::Address(resultGPR, JSInternalFieldObjectImpl<>::offsetOfInternalField(index))); | 
|  | m_jit.mutatorFence(m_jit.vm()); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowCases, this, operation, resultGPR, &vm(), TrustedImmPtr(structure))); | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNewGenerator(Node* node) | 
|  | { | 
|  | compileNewInternalFieldObjectImpl<JSGenerator>(node, operationNewGenerator); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNewAsyncGenerator(Node* node) | 
|  | { | 
|  | compileNewInternalFieldObjectImpl<JSAsyncGenerator>(node, operationNewAsyncGenerator); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileNewInternalFieldObject(Node* node) | 
|  | { | 
|  | switch (node->structure()->typeInfo().type()) { | 
|  | case JSArrayIteratorType: | 
|  | compileNewInternalFieldObjectImpl<JSArrayIterator>(node, operationNewArrayIterator); | 
|  | break; | 
|  | case JSMapIteratorType: | 
|  | compileNewInternalFieldObjectImpl<JSMapIterator>(node, operationNewMapIterator); | 
|  | break; | 
|  | case JSSetIteratorType: | 
|  | compileNewInternalFieldObjectImpl<JSSetIterator>(node, operationNewSetIterator); | 
|  | break; | 
|  | case JSPromiseType: { | 
|  | if (node->structure()->classInfo() == JSInternalPromise::info()) | 
|  | compileNewInternalFieldObjectImpl<JSInternalPromise>(node, operationNewInternalPromise); | 
|  | else { | 
|  | ASSERT(node->structure()->classInfo() == JSPromise::info()); | 
|  | compileNewInternalFieldObjectImpl<JSPromise>(node, operationNewPromise); | 
|  | } | 
|  | break; | 
|  | } | 
|  | default: | 
|  | DFG_CRASH(m_graph, node, "Bad structure"); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileToPrimitive(Node* node) | 
|  | { | 
|  | DFG_ASSERT(m_jit.graph(), node, node->child1().useKind() == UntypedUse, node->child1().useKind()); | 
|  | JSValueOperand argument(this, node->child1()); | 
|  | JSValueRegsTemporary result(this, Reuse, argument); | 
|  |  | 
|  | JSValueRegs argumentRegs = argument.jsValueRegs(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | argument.use(); | 
|  |  | 
|  | MacroAssembler::Jump alreadyPrimitive = m_jit.branchIfNotCell(argumentRegs); | 
|  | MacroAssembler::Jump notPrimitive = m_jit.branchIfObject(argumentRegs.payloadGPR()); | 
|  |  | 
|  | alreadyPrimitive.link(&m_jit); | 
|  | m_jit.moveValueRegs(argumentRegs, resultRegs); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(notPrimitive, this, operationToPrimitive, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), argumentRegs)); | 
|  |  | 
|  | jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileToPropertyKey(Node* node) | 
|  | { | 
|  | DFG_ASSERT(m_jit.graph(), node, node->child1().useKind() == UntypedUse, node->child1().useKind()); | 
|  | JSValueOperand argument(this, node->child1()); | 
|  | JSValueRegsTemporary result(this, Reuse, argument); | 
|  |  | 
|  | JSValueRegs argumentRegs = argument.jsValueRegs(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | argument.use(); | 
|  |  | 
|  | MacroAssembler::JumpList slowCases; | 
|  | slowCases.append(m_jit.branchIfNotCell(argumentRegs)); | 
|  | MacroAssembler::Jump alreadyPropertyKey = m_jit.branchIfSymbol(argumentRegs.payloadGPR()); | 
|  | slowCases.append(m_jit.branchIfNotString(argumentRegs.payloadGPR())); | 
|  |  | 
|  | alreadyPropertyKey.link(&m_jit); | 
|  | m_jit.moveValueRegs(argumentRegs, resultRegs); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowCases, this, operationToPropertyKey, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), argumentRegs)); | 
|  |  | 
|  | jsValueResult(resultRegs, node, DataFormatJSCell, UseChildrenCalledExplicitly); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileToNumeric(Node* node) | 
|  | { | 
|  | DFG_ASSERT(m_jit.graph(), node, node->child1().useKind() == UntypedUse, node->child1().useKind()); | 
|  | JSValueOperand argument(this, node->child1()); | 
|  | JSValueRegsTemporary result(this); | 
|  | GPRTemporary temp(this); | 
|  |  | 
|  | JSValueRegs argumentRegs = argument.jsValueRegs(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | GPRReg scratch = temp.gpr(); | 
|  | // FIXME: add a fast path for BigInt32 here. | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=211064 | 
|  |  | 
|  | MacroAssembler::JumpList slowCases; | 
|  |  | 
|  | MacroAssembler::Jump notCell = m_jit.branchIfNotCell(argumentRegs); | 
|  | slowCases.append(m_jit.branchIfNotHeapBigInt(argumentRegs.payloadGPR())); | 
|  | MacroAssembler::Jump isHeapBigInt = m_jit.jump(); | 
|  |  | 
|  | notCell.link(&m_jit); | 
|  | slowCases.append(m_jit.branchIfNotNumber(argumentRegs, scratch)); | 
|  |  | 
|  | isHeapBigInt.link(&m_jit); | 
|  | m_jit.moveValueRegs(argumentRegs, resultRegs); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowCases, this, operationToNumeric, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), argumentRegs)); | 
|  |  | 
|  | jsValueResult(resultRegs, node, DataFormatJS); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileCallNumberConstructor(Node* node) | 
|  | { | 
|  | #if USE(BIGINT32) | 
|  | if (node->child1().useKind() == BigInt32Use) { | 
|  | SpeculateBigInt32Operand operand(this, node->child1()); | 
|  | GPRTemporary result(this); | 
|  |  | 
|  | GPRReg operandGPR = operand.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | m_jit.unboxBigInt32(operandGPR, resultGPR); | 
|  | strictInt32Result(resultGPR, node); | 
|  | return; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | DFG_ASSERT(m_jit.graph(), node, node->child1().useKind() == UntypedUse, node->child1().useKind()); | 
|  | JSValueOperand argument(this, node->child1()); | 
|  | JSValueRegsTemporary result(this); | 
|  | GPRTemporary temp(this); | 
|  |  | 
|  | JSValueRegs argumentRegs = argument.jsValueRegs(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  | // FIXME: add a fast path for BigInt32 here. | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=211064 | 
|  |  | 
|  | CCallHelpers::JumpList slowCases; | 
|  | slowCases.append(m_jit.branchIfNotNumber(argumentRegs, tempGPR)); | 
|  | m_jit.moveValueRegs(argumentRegs, resultRegs); | 
|  | addSlowPathGenerator(slowPathCall(slowCases, this, operationCallNumberConstructor, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), argumentRegs)); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileLogShadowChickenPrologue(Node* node) | 
|  | { | 
|  | flushRegisters(); | 
|  | prepareForExternalCall(); | 
|  | m_jit.emitStoreCodeOrigin(node->origin.semantic); | 
|  |  | 
|  | GPRTemporary scratch1(this, GPRInfo::nonArgGPR0); // This must be a non-argument GPR. | 
|  | GPRReg scratch1Reg = scratch1.gpr(); | 
|  | GPRTemporary scratch2(this); | 
|  | GPRReg scratch2Reg = scratch2.gpr(); | 
|  | GPRTemporary shadowPacket(this); | 
|  | GPRReg shadowPacketReg = shadowPacket.gpr(); | 
|  |  | 
|  | m_jit.ensureShadowChickenPacket(vm(), shadowPacketReg, scratch1Reg, scratch2Reg); | 
|  |  | 
|  | SpeculateCellOperand scope(this, node->child1()); | 
|  | GPRReg scopeReg = scope.gpr(); | 
|  |  | 
|  | m_jit.logShadowChickenProloguePacket(shadowPacketReg, scratch1Reg, scopeReg); | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileLogShadowChickenTail(Node* node) | 
|  | { | 
|  | flushRegisters(); | 
|  | prepareForExternalCall(); | 
|  | CallSiteIndex callSiteIndex = m_jit.emitStoreCodeOrigin(node->origin.semantic); | 
|  |  | 
|  | GPRTemporary scratch1(this, GPRInfo::nonArgGPR0); // This must be a non-argument GPR. | 
|  | GPRReg scratch1Reg = scratch1.gpr(); | 
|  | GPRTemporary scratch2(this); | 
|  | GPRReg scratch2Reg = scratch2.gpr(); | 
|  | GPRTemporary shadowPacket(this); | 
|  | GPRReg shadowPacketReg = shadowPacket.gpr(); | 
|  |  | 
|  | m_jit.ensureShadowChickenPacket(vm(), shadowPacketReg, scratch1Reg, scratch2Reg); | 
|  |  | 
|  | JSValueOperand thisValue(this, node->child1()); | 
|  | JSValueRegs thisRegs = thisValue.jsValueRegs(); | 
|  | SpeculateCellOperand scope(this, node->child2()); | 
|  | GPRReg scopeReg = scope.gpr(); | 
|  |  | 
|  | m_jit.logShadowChickenTailPacket(shadowPacketReg, thisRegs, scopeReg, m_jit.codeBlock(), callSiteIndex); | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileSetAdd(Node* node) | 
|  | { | 
|  | SpeculateCellOperand set(this, node->child1()); | 
|  | JSValueOperand key(this, node->child2()); | 
|  | SpeculateInt32Operand hash(this, node->child3()); | 
|  |  | 
|  | GPRReg setGPR = set.gpr(); | 
|  | JSValueRegs keyRegs = key.jsValueRegs(); | 
|  | GPRReg hashGPR = hash.gpr(); | 
|  |  | 
|  | speculateSetObject(node->child1(), setGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | callOperation(operationSetAdd, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), setGPR, keyRegs, hashGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileMapSet(Node* node) | 
|  | { | 
|  | SpeculateCellOperand map(this, m_jit.graph().varArgChild(node, 0)); | 
|  | JSValueOperand key(this, m_jit.graph().varArgChild(node, 1)); | 
|  | JSValueOperand value(this, m_jit.graph().varArgChild(node, 2)); | 
|  | SpeculateInt32Operand hash(this, m_jit.graph().varArgChild(node, 3)); | 
|  |  | 
|  | GPRReg mapGPR = map.gpr(); | 
|  | JSValueRegs keyRegs = key.jsValueRegs(); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  | GPRReg hashGPR = hash.gpr(); | 
|  |  | 
|  | speculateMapObject(m_jit.graph().varArgChild(node, 0), mapGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | callOperation(operationMapSet, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), mapGPR, keyRegs, valueRegs, hashGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | cellResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileWeakMapGet(Node* node) | 
|  | { | 
|  | GPRTemporary mask(this); | 
|  | GPRTemporary buffer(this); | 
|  | JSValueRegsTemporary result(this); | 
|  |  | 
|  | GPRReg maskGPR = mask.gpr(); | 
|  | GPRReg bufferGPR = buffer.gpr(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | GPRTemporary index; | 
|  | GPRReg indexGPR { InvalidGPRReg }; | 
|  | { | 
|  | SpeculateInt32Operand hash(this, node->child3()); | 
|  | GPRReg hashGPR = hash.gpr(); | 
|  | index = GPRTemporary(this, Reuse, hash); | 
|  | indexGPR = index.gpr(); | 
|  | m_jit.move(hashGPR, indexGPR); | 
|  | } | 
|  |  | 
|  | { | 
|  | SpeculateCellOperand weakMap(this, node->child1()); | 
|  | GPRReg weakMapGPR = weakMap.gpr(); | 
|  | if (node->child1().useKind() == WeakMapObjectUse) | 
|  | speculateWeakMapObject(node->child1(), weakMapGPR); | 
|  | else | 
|  | speculateWeakSetObject(node->child1(), weakMapGPR); | 
|  |  | 
|  | ASSERT(WeakMapImpl<WeakMapBucket<WeakMapBucketDataKey>>::offsetOfCapacity() == WeakMapImpl<WeakMapBucket<WeakMapBucketDataKeyValue>>::offsetOfCapacity()); | 
|  | ASSERT(WeakMapImpl<WeakMapBucket<WeakMapBucketDataKey>>::offsetOfBuffer() == WeakMapImpl<WeakMapBucket<WeakMapBucketDataKeyValue>>::offsetOfBuffer()); | 
|  | m_jit.load32(MacroAssembler::Address(weakMapGPR, WeakMapImpl<WeakMapBucket<WeakMapBucketDataKey>>::offsetOfCapacity()), maskGPR); | 
|  | m_jit.loadPtr(MacroAssembler::Address(weakMapGPR, WeakMapImpl<WeakMapBucket<WeakMapBucketDataKey>>::offsetOfBuffer()), bufferGPR); | 
|  | } | 
|  |  | 
|  | SpeculateCellOperand key(this, node->child2()); | 
|  | GPRReg keyGPR = key.gpr(); | 
|  | speculateObject(node->child2(), keyGPR); | 
|  |  | 
|  | #if USE(JSVALUE32_64) | 
|  | GPRReg bucketGPR = resultRegs.tagGPR(); | 
|  | #else | 
|  | GPRTemporary bucket(this); | 
|  | GPRReg bucketGPR = bucket.gpr(); | 
|  | #endif | 
|  |  | 
|  | m_jit.sub32(TrustedImm32(1), maskGPR); | 
|  |  | 
|  | MacroAssembler::Label loop = m_jit.label(); | 
|  | m_jit.and32(maskGPR, indexGPR); | 
|  | if (node->child1().useKind() == WeakSetObjectUse) { | 
|  | static_assert(sizeof(WeakMapBucket<WeakMapBucketDataKey>) == sizeof(void*), ""); | 
|  | m_jit.zeroExtend32ToWord(indexGPR, bucketGPR); | 
|  | m_jit.lshiftPtr(MacroAssembler::Imm32(sizeof(void*) == 4 ? 2 : 3), bucketGPR); | 
|  | m_jit.addPtr(bufferGPR, bucketGPR); | 
|  | } else { | 
|  | ASSERT(node->child1().useKind() == WeakMapObjectUse); | 
|  | static_assert(sizeof(WeakMapBucket<WeakMapBucketDataKeyValue>) == 16, ""); | 
|  | m_jit.zeroExtend32ToWord(indexGPR, bucketGPR); | 
|  | m_jit.lshiftPtr(MacroAssembler::Imm32(4), bucketGPR); | 
|  | m_jit.addPtr(bufferGPR, bucketGPR); | 
|  | } | 
|  |  | 
|  | m_jit.loadPtr(MacroAssembler::Address(bucketGPR, WeakMapBucket<WeakMapBucketDataKeyValue>::offsetOfKey()), resultRegs.payloadGPR()); | 
|  |  | 
|  | // They're definitely the same value, we found the bucket we were looking for! | 
|  | // The deleted key comparison is also done with this. | 
|  | auto found = m_jit.branchPtr(MacroAssembler::Equal, resultRegs.payloadGPR(), keyGPR); | 
|  |  | 
|  | auto notPresentInTable = m_jit.branchTestPtr(MacroAssembler::Zero, resultRegs.payloadGPR()); | 
|  |  | 
|  | m_jit.add32(TrustedImm32(1), indexGPR); | 
|  | m_jit.jump().linkTo(loop, &m_jit); | 
|  |  | 
|  | #if USE(JSVALUE32_64) | 
|  | notPresentInTable.link(&m_jit); | 
|  | m_jit.moveValue(JSValue(), resultRegs); | 
|  | auto notPresentInTableDone = m_jit.jump(); | 
|  |  | 
|  | found.link(&m_jit); | 
|  | if (node->child1().useKind() == WeakSetObjectUse) | 
|  | m_jit.move(TrustedImm32(JSValue::CellTag), resultRegs.tagGPR()); | 
|  | else | 
|  | m_jit.loadValue(MacroAssembler::Address(bucketGPR, WeakMapBucket<WeakMapBucketDataKeyValue>::offsetOfValue()), resultRegs); | 
|  |  | 
|  | notPresentInTableDone.link(&m_jit); | 
|  | #else | 
|  | notPresentInTable.link(&m_jit); | 
|  | found.link(&m_jit); | 
|  |  | 
|  | // In 64bit environment, Empty bucket has JSEmpty value. Empty key is JSEmpty. | 
|  | // If empty bucket is found, we can use the same path used for the case of finding a bucket. | 
|  | if (node->child1().useKind() == WeakMapObjectUse) | 
|  | m_jit.loadValue(MacroAssembler::Address(bucketGPR, WeakMapBucket<WeakMapBucketDataKeyValue>::offsetOfValue()), resultRegs); | 
|  | #endif | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileWeakSetAdd(Node* node) | 
|  | { | 
|  | SpeculateCellOperand set(this, node->child1()); | 
|  | SpeculateCellOperand key(this, node->child2()); | 
|  | SpeculateInt32Operand hash(this, node->child3()); | 
|  |  | 
|  | GPRReg setGPR = set.gpr(); | 
|  | GPRReg keyGPR = key.gpr(); | 
|  | GPRReg hashGPR = hash.gpr(); | 
|  |  | 
|  | speculateWeakSetObject(node->child1(), setGPR); | 
|  | speculateObject(node->child2(), keyGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(operationWeakSetAdd, &vm(), setGPR, keyGPR, hashGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileWeakMapSet(Node* node) | 
|  | { | 
|  | SpeculateCellOperand map(this, m_jit.graph().varArgChild(node, 0)); | 
|  | SpeculateCellOperand key(this, m_jit.graph().varArgChild(node, 1)); | 
|  | JSValueOperand value(this, m_jit.graph().varArgChild(node, 2)); | 
|  | SpeculateInt32Operand hash(this, m_jit.graph().varArgChild(node, 3)); | 
|  |  | 
|  | GPRReg mapGPR = map.gpr(); | 
|  | GPRReg keyGPR = key.gpr(); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  | GPRReg hashGPR = hash.gpr(); | 
|  |  | 
|  | speculateWeakMapObject(m_jit.graph().varArgChild(node, 0), mapGPR); | 
|  | speculateObject(m_jit.graph().varArgChild(node, 1), keyGPR); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(operationWeakMapSet, &vm(), mapGPR, keyGPR, valueRegs, hashGPR); | 
|  | m_jit.exceptionCheck(); | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetPrototypeOf(Node* node) | 
|  | { | 
|  | GPRTemporary temp(this); | 
|  | GPRTemporary temp2(this); | 
|  |  | 
|  | GPRReg tempGPR = temp.gpr(); | 
|  | GPRReg temp2GPR = temp2.gpr(); | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | JSValueRegs resultRegs(tempGPR); | 
|  | #else | 
|  | JSValueRegs resultRegs(temp2GPR, tempGPR); | 
|  | #endif | 
|  |  | 
|  | switch (node->child1().useKind()) { | 
|  | case ArrayUse: | 
|  | case FunctionUse: | 
|  | case FinalObjectUse: { | 
|  | SpeculateCellOperand object(this, node->child1()); | 
|  | GPRReg objectGPR = object.gpr(); | 
|  |  | 
|  | switch (node->child1().useKind()) { | 
|  | case ArrayUse: | 
|  | speculateArray(node->child1(), objectGPR); | 
|  | break; | 
|  | case FunctionUse: | 
|  | speculateFunction(node->child1(), objectGPR); | 
|  | break; | 
|  | case FinalObjectUse: | 
|  | speculateFinalObject(node->child1(), objectGPR); | 
|  | break; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  |  | 
|  | m_jit.emitLoadStructure(vm(), objectGPR, tempGPR, temp2GPR); | 
|  |  | 
|  | AbstractValue& value = m_state.forNode(node->child1()); | 
|  | if ((value.m_type && !(value.m_type & ~SpecObject)) && value.m_structure.isFinite()) { | 
|  | bool hasPolyProto = false; | 
|  | bool hasMonoProto = false; | 
|  | value.m_structure.forEach([&] (RegisteredStructure structure) { | 
|  | if (structure->hasPolyProto()) | 
|  | hasPolyProto = true; | 
|  | else | 
|  | hasMonoProto = true; | 
|  | }); | 
|  |  | 
|  | if (hasMonoProto && !hasPolyProto) { | 
|  | m_jit.loadValue(MacroAssembler::Address(tempGPR, Structure::prototypeOffset()), resultRegs); | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (hasPolyProto && !hasMonoProto) { | 
|  | m_jit.loadValue(JITCompiler::Address(objectGPR, offsetRelativeToBase(knownPolyProtoOffset)), resultRegs); | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | m_jit.loadValue(MacroAssembler::Address(tempGPR, Structure::prototypeOffset()), resultRegs); | 
|  | auto hasMonoProto = m_jit.branchIfNotEmpty(resultRegs); | 
|  | m_jit.loadValue(JITCompiler::Address(objectGPR, offsetRelativeToBase(knownPolyProtoOffset)), resultRegs); | 
|  | hasMonoProto.link(&m_jit); | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  | case ObjectUse: { | 
|  | SpeculateCellOperand object(this, node->child1()); | 
|  | GPRReg objectGPR = object.gpr(); | 
|  | speculateObject(node->child1(), objectGPR); | 
|  |  | 
|  | JITCompiler::JumpList slowCases; | 
|  | m_jit.emitLoadPrototype(vm(), objectGPR, resultRegs, temp2GPR, slowCases); | 
|  | addSlowPathGenerator(slowPathCall(slowCases, this, operationGetPrototypeOfObject, | 
|  | resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), objectGPR)); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  | default: { | 
|  | JSValueOperand value(this, node->child1()); | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  |  | 
|  | JITCompiler::JumpList slowCases; | 
|  | slowCases.append(m_jit.branchIfNotCell(valueRegs)); | 
|  |  | 
|  | GPRReg valueGPR = valueRegs.payloadGPR(); | 
|  | slowCases.append(m_jit.branchIfNotObject(valueGPR)); | 
|  |  | 
|  | m_jit.emitLoadPrototype(vm(), valueGPR, resultRegs, temp2GPR, slowCases); | 
|  | addSlowPathGenerator(slowPathCall(slowCases, this, operationGetPrototypeOf, | 
|  | resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), valueRegs)); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileIdentity(Node* node) | 
|  | { | 
|  | speculate(node, node->child1()); | 
|  | switch (node->child1().useKind()) { | 
|  | #if USE(JSVALUE64) | 
|  | case DoubleRepAnyIntUse: | 
|  | #endif | 
|  | case DoubleRepUse: | 
|  | case DoubleRepRealUse: { | 
|  | SpeculateDoubleOperand op(this, node->child1()); | 
|  | FPRTemporary scratch(this, op); | 
|  | m_jit.moveDouble(op.fpr(), scratch.fpr()); | 
|  | doubleResult(scratch.fpr(), node); | 
|  | break; | 
|  | } | 
|  | #if USE(JSVALUE64) | 
|  | case Int52RepUse: { | 
|  | SpeculateInt52Operand op(this, node->child1()); | 
|  | GPRTemporary result(this, Reuse, op); | 
|  | m_jit.move(op.gpr(), result.gpr()); | 
|  | int52Result(result.gpr(), node); | 
|  | break; | 
|  | } | 
|  | #endif | 
|  | default: { | 
|  | JSValueOperand op(this, node->child1(), ManualOperandSpeculation); | 
|  | JSValueRegsTemporary result(this, Reuse, op); | 
|  | JSValueRegs opRegs = op.jsValueRegs(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  | m_jit.moveValueRegs(opRegs, resultRegs); | 
|  | jsValueResult(resultRegs, node); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileMiscStrictEq(Node* node) | 
|  | { | 
|  | JSValueOperand op1(this, node->child1(), ManualOperandSpeculation); | 
|  | JSValueOperand op2(this, node->child2(), ManualOperandSpeculation); | 
|  | GPRTemporary result(this); | 
|  |  | 
|  | if (node->child1().useKind() == MiscUse) | 
|  | speculateMisc(node->child1(), op1.jsValueRegs()); | 
|  | if (node->child2().useKind() == MiscUse) | 
|  | speculateMisc(node->child2(), op2.jsValueRegs()); | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | m_jit.compare64(JITCompiler::Equal, op1.gpr(), op2.gpr(), result.gpr()); | 
|  | #else | 
|  | m_jit.move(TrustedImm32(0), result.gpr()); | 
|  | JITCompiler::Jump notEqual = m_jit.branch32(JITCompiler::NotEqual, op1.tagGPR(), op2.tagGPR()); | 
|  | m_jit.compare32(JITCompiler::Equal, op1.payloadGPR(), op2.payloadGPR(), result.gpr()); | 
|  | notEqual.link(&m_jit); | 
|  | #endif | 
|  | unblessedBooleanResult(result.gpr(), node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::emitInitializeButterfly(GPRReg storageGPR, GPRReg sizeGPR, JSValueRegs emptyValueRegs, GPRReg scratchGPR) | 
|  | { | 
|  | m_jit.zeroExtend32ToWord(sizeGPR, scratchGPR); | 
|  | MacroAssembler::Jump done = m_jit.branchTest32(MacroAssembler::Zero, scratchGPR); | 
|  | MacroAssembler::Label loop = m_jit.label(); | 
|  | m_jit.sub32(TrustedImm32(1), scratchGPR); | 
|  | m_jit.storeValue(emptyValueRegs, MacroAssembler::BaseIndex(storageGPR, scratchGPR, MacroAssembler::TimesEight)); | 
|  | m_jit.branchTest32(MacroAssembler::NonZero, scratchGPR).linkTo(loop, &m_jit); | 
|  | done.link(&m_jit); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileAllocateNewArrayWithSize(JSGlobalObject* globalObject, GPRReg resultGPR, GPRReg sizeGPR, IndexingType indexingType, bool shouldConvertLargeSizeToArrayStorage) | 
|  | { | 
|  | GPRTemporary storage(this); | 
|  | GPRTemporary scratch(this); | 
|  | GPRTemporary scratch2(this); | 
|  |  | 
|  | GPRReg storageGPR = storage.gpr(); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  | GPRReg scratch2GPR = scratch2.gpr(); | 
|  |  | 
|  | m_jit.move(TrustedImmPtr(nullptr), storageGPR); | 
|  |  | 
|  | MacroAssembler::JumpList slowCases; | 
|  | if (shouldConvertLargeSizeToArrayStorage) | 
|  | slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH))); | 
|  | #if ASSERT_ENABLED | 
|  | else { | 
|  | MacroAssembler::Jump lengthIsWithinLimits; | 
|  | lengthIsWithinLimits = m_jit.branch32(MacroAssembler::Below, sizeGPR, TrustedImm32(MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH)); | 
|  | m_jit.abortWithReason(UncheckedOverflow); | 
|  | lengthIsWithinLimits.link(&m_jit); | 
|  | } | 
|  | #endif // ASSERT_ENABLED | 
|  |  | 
|  | // We can use resultGPR as a scratch right now. | 
|  | emitAllocateButterfly(storageGPR, sizeGPR, scratchGPR, scratch2GPR, resultGPR, slowCases); | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | JSValueRegs emptyValueRegs(scratchGPR); | 
|  | if (hasDouble(indexingType)) | 
|  | m_jit.move(TrustedImm64(bitwise_cast<int64_t>(PNaN)), emptyValueRegs.gpr()); | 
|  | else | 
|  | m_jit.move(TrustedImm64(JSValue::encode(JSValue())), emptyValueRegs.gpr()); | 
|  | #else | 
|  | JSValueRegs emptyValueRegs(scratchGPR, scratch2GPR); | 
|  | if (hasDouble(indexingType)) | 
|  | m_jit.moveValue(JSValue(JSValue::EncodeAsDouble, PNaN), emptyValueRegs); | 
|  | else | 
|  | m_jit.moveValue(JSValue(), emptyValueRegs); | 
|  | #endif | 
|  | emitInitializeButterfly(storageGPR, sizeGPR, emptyValueRegs, resultGPR); | 
|  |  | 
|  | RegisteredStructure structure = m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingType)); | 
|  |  | 
|  | emitAllocateJSObject<JSArray>(resultGPR, TrustedImmPtr(structure), storageGPR, scratchGPR, scratch2GPR, slowCases); | 
|  |  | 
|  | m_jit.mutatorFence(vm()); | 
|  |  | 
|  | addSlowPathGenerator(makeUnique<CallArrayAllocatorWithVariableSizeSlowPathGenerator>( | 
|  | slowCases, this, operationNewArrayWithSize, resultGPR, | 
|  | TrustedImmPtr::weakPointer(m_graph, globalObject), | 
|  | structure, | 
|  | shouldConvertLargeSizeToArrayStorage ? m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage)) : structure, | 
|  | sizeGPR, storageGPR)); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileHasIndexedProperty(Node* node) | 
|  | { | 
|  | SpeculateCellOperand base(this, m_graph.varArgChild(node, 0)); | 
|  | SpeculateStrictInt32Operand index(this, m_graph.varArgChild(node, 1)); | 
|  | GPRTemporary result(this); | 
|  |  | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | GPRReg indexGPR = index.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | MacroAssembler::JumpList slowCases; | 
|  | ArrayMode mode = node->arrayMode(); | 
|  | switch (mode.type()) { | 
|  | case Array::Int32: | 
|  | case Array::Contiguous: { | 
|  | ASSERT(!!m_graph.varArgChild(node, 2)); | 
|  | StorageOperand storage(this, m_graph.varArgChild(node, 2)); | 
|  | GPRTemporary scratch(this); | 
|  |  | 
|  | GPRReg storageGPR = storage.gpr(); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  |  | 
|  | MacroAssembler::Jump outOfBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, indexGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); | 
|  |  | 
|  | if (mode.isInBounds()) | 
|  | speculationCheck(OutOfBounds, JSValueRegs(), nullptr, outOfBounds); | 
|  | else | 
|  | slowCases.append(outOfBounds); | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | m_jit.load64(MacroAssembler::BaseIndex(storageGPR, indexGPR, MacroAssembler::TimesEight), scratchGPR); | 
|  | #else | 
|  | m_jit.load32(MacroAssembler::BaseIndex(storageGPR, indexGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)), scratchGPR); | 
|  | #endif | 
|  |  | 
|  | if (mode.isInBoundsSaneChain()) { | 
|  | m_jit.isNotEmpty(scratchGPR, resultGPR); | 
|  | break; | 
|  | } | 
|  |  | 
|  | MacroAssembler::Jump isHole = m_jit.branchIfEmpty(scratchGPR); | 
|  | if (!mode.isInBounds()) | 
|  | slowCases.append(isHole); | 
|  | else | 
|  | speculationCheck(LoadFromHole, JSValueRegs(), nullptr, isHole); | 
|  | m_jit.move(TrustedImm32(1), resultGPR); | 
|  | break; | 
|  | } | 
|  | case Array::Double: { | 
|  | ASSERT(!!m_graph.varArgChild(node, 2)); | 
|  | StorageOperand storage(this, m_graph.varArgChild(node, 2)); | 
|  | FPRTemporary scratch(this); | 
|  | FPRReg scratchFPR = scratch.fpr(); | 
|  | GPRReg storageGPR = storage.gpr(); | 
|  |  | 
|  | MacroAssembler::Jump outOfBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, indexGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); | 
|  |  | 
|  | if (mode.isInBounds()) | 
|  | speculationCheck(OutOfBounds, JSValueRegs(), nullptr, outOfBounds); | 
|  | else | 
|  | slowCases.append(outOfBounds); | 
|  |  | 
|  | m_jit.loadDouble(MacroAssembler::BaseIndex(storageGPR, indexGPR, MacroAssembler::TimesEight), scratchFPR); | 
|  |  | 
|  | if (mode.isInBoundsSaneChain()) { | 
|  | m_jit.compareDouble(MacroAssembler::DoubleEqualAndOrdered, scratchFPR, scratchFPR, resultGPR); | 
|  | break; | 
|  | } | 
|  |  | 
|  | MacroAssembler::Jump isHole = m_jit.branchIfNaN(scratchFPR); | 
|  | if (!mode.isInBounds()) | 
|  | slowCases.append(isHole); | 
|  | else | 
|  | speculationCheck(LoadFromHole, JSValueRegs(), nullptr, isHole); | 
|  | m_jit.move(TrustedImm32(1), resultGPR); | 
|  | break; | 
|  | } | 
|  | case Array::ArrayStorage: { | 
|  | ASSERT(!!m_graph.varArgChild(node, 2)); | 
|  | StorageOperand storage(this, m_graph.varArgChild(node, 2)); | 
|  | GPRTemporary scratch(this); | 
|  |  | 
|  | GPRReg storageGPR = storage.gpr(); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  |  | 
|  | MacroAssembler::Jump outOfBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, indexGPR, MacroAssembler::Address(storageGPR, ArrayStorage::vectorLengthOffset())); | 
|  | if (mode.isInBounds()) | 
|  | speculationCheck(OutOfBounds, JSValueRegs(), nullptr, outOfBounds); | 
|  | else | 
|  | slowCases.append(outOfBounds); | 
|  |  | 
|  | #if USE(JSVALUE64) | 
|  | m_jit.load64(MacroAssembler::BaseIndex(storageGPR, indexGPR, MacroAssembler::TimesEight, ArrayStorage::vectorOffset()), scratchGPR); | 
|  | #else | 
|  | m_jit.load32(MacroAssembler::BaseIndex(storageGPR, indexGPR, MacroAssembler::TimesEight, ArrayStorage::vectorOffset() + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), scratchGPR); | 
|  | #endif | 
|  |  | 
|  | if (mode.isInBoundsSaneChain()) { | 
|  | m_jit.isNotEmpty(scratchGPR, resultGPR); | 
|  | break; | 
|  | } | 
|  |  | 
|  | MacroAssembler::Jump isHole = m_jit.branchIfEmpty(scratchGPR); | 
|  | if (!mode.isInBounds() || mode.isInBoundsSaneChain()) | 
|  | slowCases.append(isHole); | 
|  | else | 
|  | speculationCheck(LoadFromHole, JSValueRegs(), nullptr, isHole); | 
|  | m_jit.move(TrustedImm32(1), resultGPR); | 
|  | break; | 
|  | } | 
|  | default: { | 
|  | slowCases.append(m_jit.jump()); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowCases, this, operationHasIndexedPropertyByInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, indexGPR, static_cast<int32_t>(node->internalMethodType()))); | 
|  |  | 
|  | unblessedBooleanResult(resultGPR, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileGetDirectPname(Node* node) | 
|  | { | 
|  | Edge& baseEdge = m_jit.graph().varArgChild(node, 0); | 
|  | Edge& propertyEdge = m_jit.graph().varArgChild(node, 1); | 
|  | Edge& indexEdge = m_jit.graph().varArgChild(node, 2); | 
|  |  | 
|  | SpeculateCellOperand base(this, baseEdge); | 
|  | SpeculateCellOperand property(this, propertyEdge); | 
|  | GPRReg baseGPR = base.gpr(); | 
|  | GPRReg propertyGPR = property.gpr(); | 
|  |  | 
|  | Edge& enumeratorEdge = m_jit.graph().varArgChild(node, 3); | 
|  | SpeculateStrictInt32Operand index(this, indexEdge); | 
|  | SpeculateCellOperand enumerator(this, enumeratorEdge); | 
|  | GPRTemporary scratch(this); | 
|  | JSValueRegsTemporary result(this); | 
|  |  | 
|  | GPRReg indexGPR = index.gpr(); | 
|  | GPRReg enumeratorGPR = enumerator.gpr(); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | MacroAssembler::JumpList slowPath; | 
|  |  | 
|  | // Check the structure | 
|  | m_jit.load32(MacroAssembler::Address(baseGPR, JSCell::structureIDOffset()), scratchGPR); | 
|  | slowPath.append( | 
|  | m_jit.branch32( | 
|  | MacroAssembler::NotEqual, | 
|  | scratchGPR, | 
|  | MacroAssembler::Address( | 
|  | enumeratorGPR, JSPropertyNameEnumerator::cachedStructureIDOffset()))); | 
|  |  | 
|  | // Compute the offset | 
|  | // If index is less than the enumerator's cached inline storage, then it's an inline access | 
|  | MacroAssembler::Jump outOfLineAccess = m_jit.branch32(MacroAssembler::AboveOrEqual, | 
|  | indexGPR, MacroAssembler::Address(enumeratorGPR, JSPropertyNameEnumerator::cachedInlineCapacityOffset())); | 
|  |  | 
|  | m_jit.loadValue(MacroAssembler::BaseIndex(baseGPR, indexGPR, MacroAssembler::TimesEight, JSObject::offsetOfInlineStorage()), resultRegs); | 
|  |  | 
|  | MacroAssembler::Jump done = m_jit.jump(); | 
|  |  | 
|  | // Otherwise it's out of line | 
|  | outOfLineAccess.link(&m_jit); | 
|  | m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), resultRegs.payloadGPR()); | 
|  | m_jit.move(indexGPR, scratchGPR); | 
|  | m_jit.sub32(MacroAssembler::Address(enumeratorGPR, JSPropertyNameEnumerator::cachedInlineCapacityOffset()), scratchGPR); | 
|  | m_jit.neg32(scratchGPR); | 
|  | m_jit.signExtend32ToPtr(scratchGPR, scratchGPR); | 
|  | int32_t offsetOfFirstProperty = static_cast<int32_t>(offsetInButterfly(firstOutOfLineOffset)) * sizeof(EncodedJSValue); | 
|  | m_jit.loadValue(MacroAssembler::BaseIndex(resultRegs.payloadGPR(), scratchGPR, MacroAssembler::TimesEight, offsetOfFirstProperty), resultRegs); | 
|  |  | 
|  | done.link(&m_jit); | 
|  |  | 
|  | addSlowPathGenerator(slowPathCall(slowPath, this, operationGetByValCell, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR, CCallHelpers::CellValue(propertyGPR))); | 
|  |  | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileExtractCatchLocal(Node* node) | 
|  | { | 
|  | JSValueRegsTemporary result(this); | 
|  | JSValueRegs resultRegs = result.regs(); | 
|  |  | 
|  | JSValue* ptr = &reinterpret_cast<JSValue*>(m_jit.jitCode()->common.catchOSREntryBuffer->dataBuffer())[node->catchOSREntryIndex()]; | 
|  | m_jit.loadValue(ptr, resultRegs); | 
|  | jsValueResult(resultRegs, node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileClearCatchLocals(Node* node) | 
|  | { | 
|  | ScratchBuffer* scratchBuffer = m_jit.jitCode()->common.catchOSREntryBuffer; | 
|  | ASSERT(scratchBuffer); | 
|  | GPRTemporary scratch(this); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  | m_jit.move(TrustedImmPtr(scratchBuffer->addressOfActiveLength()), scratchGPR); | 
|  | m_jit.storePtr(TrustedImmPtr(nullptr), scratchGPR); | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileProfileType(Node* node) | 
|  | { | 
|  | JSValueOperand value(this, node->child1()); | 
|  | GPRTemporary scratch1(this); | 
|  | GPRTemporary scratch2(this); | 
|  | GPRTemporary scratch3(this); | 
|  |  | 
|  | JSValueRegs valueRegs = value.jsValueRegs(); | 
|  | GPRReg scratch1GPR = scratch1.gpr(); | 
|  | GPRReg scratch2GPR = scratch2.gpr(); | 
|  | GPRReg scratch3GPR = scratch3.gpr(); | 
|  |  | 
|  | MacroAssembler::JumpList jumpToEnd; | 
|  |  | 
|  | jumpToEnd.append(m_jit.branchIfEmpty(valueRegs)); | 
|  |  | 
|  | TypeLocation* cachedTypeLocation = node->typeLocation(); | 
|  | // Compile in a predictive type check, if possible, to see if we can skip writing to the log. | 
|  | // These typechecks are inlined to match those of the 64-bit JSValue type checks. | 
|  | if (cachedTypeLocation->m_lastSeenType == TypeUndefined) | 
|  | jumpToEnd.append(m_jit.branchIfUndefined(valueRegs)); | 
|  | else if (cachedTypeLocation->m_lastSeenType == TypeNull) | 
|  | jumpToEnd.append(m_jit.branchIfNull(valueRegs)); | 
|  | else if (cachedTypeLocation->m_lastSeenType == TypeBoolean) | 
|  | jumpToEnd.append(m_jit.branchIfBoolean(valueRegs, scratch1GPR)); | 
|  | else if (cachedTypeLocation->m_lastSeenType == TypeAnyInt) | 
|  | jumpToEnd.append(m_jit.branchIfInt32(valueRegs)); | 
|  | else if (cachedTypeLocation->m_lastSeenType == TypeNumber) | 
|  | jumpToEnd.append(m_jit.branchIfNumber(valueRegs, scratch1GPR)); | 
|  | else if (cachedTypeLocation->m_lastSeenType == TypeString) { | 
|  | MacroAssembler::Jump isNotCell = m_jit.branchIfNotCell(valueRegs); | 
|  | jumpToEnd.append(m_jit.branchIfString(valueRegs.payloadGPR())); | 
|  | isNotCell.link(&m_jit); | 
|  | } | 
|  |  | 
|  | // Load the TypeProfilerLog into Scratch2. | 
|  | TypeProfilerLog* cachedTypeProfilerLog = vm().typeProfilerLog(); | 
|  | m_jit.move(TrustedImmPtr(cachedTypeProfilerLog), scratch2GPR); | 
|  |  | 
|  | // Load the next LogEntry into Scratch1. | 
|  | m_jit.loadPtr(MacroAssembler::Address(scratch2GPR, TypeProfilerLog::currentLogEntryOffset()), scratch1GPR); | 
|  |  | 
|  | // Store the JSValue onto the log entry. | 
|  | m_jit.storeValue(valueRegs, MacroAssembler::Address(scratch1GPR, TypeProfilerLog::LogEntry::valueOffset())); | 
|  |  | 
|  | // Store the structureID of the cell if valueRegs is a cell, otherwise, store 0 on the log entry. | 
|  | MacroAssembler::Jump isNotCell = m_jit.branchIfNotCell(valueRegs); | 
|  | m_jit.load32(MacroAssembler::Address(valueRegs.payloadGPR(), JSCell::structureIDOffset()), scratch3GPR); | 
|  | m_jit.store32(scratch3GPR, MacroAssembler::Address(scratch1GPR, TypeProfilerLog::LogEntry::structureIDOffset())); | 
|  | MacroAssembler::Jump skipIsCell = m_jit.jump(); | 
|  | isNotCell.link(&m_jit); | 
|  | m_jit.store32(TrustedImm32(0), MacroAssembler::Address(scratch1GPR, TypeProfilerLog::LogEntry::structureIDOffset())); | 
|  | skipIsCell.link(&m_jit); | 
|  |  | 
|  | // Store the typeLocation on the log entry. | 
|  | m_jit.move(TrustedImmPtr(cachedTypeLocation), scratch3GPR); | 
|  | m_jit.storePtr(scratch3GPR, MacroAssembler::Address(scratch1GPR, TypeProfilerLog::LogEntry::locationOffset())); | 
|  |  | 
|  | // Increment the current log entry. | 
|  | m_jit.addPtr(TrustedImm32(sizeof(TypeProfilerLog::LogEntry)), scratch1GPR); | 
|  | m_jit.storePtr(scratch1GPR, MacroAssembler::Address(scratch2GPR, TypeProfilerLog::currentLogEntryOffset())); | 
|  | MacroAssembler::Jump clearLog = m_jit.branchPtr(MacroAssembler::Equal, scratch1GPR, TrustedImmPtr(cachedTypeProfilerLog->logEndPtr())); | 
|  | addSlowPathGenerator( | 
|  | slowPathCall(clearLog, this, operationProcessTypeProfilerLogDFG, NoResult, TrustedImmPtr(&vm()))); | 
|  |  | 
|  | jumpToEnd.link(&m_jit); | 
|  |  | 
|  | noResult(node); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::cachedPutById(CodeOrigin codeOrigin, GPRReg baseGPR, JSValueRegs valueRegs, GPRReg scratchGPR, CacheableIdentifier identifier, PutKind putKind, ECMAMode ecmaMode, JITCompiler::Jump slowPathTarget, SpillRegistersMode spillMode) | 
|  | { | 
|  | RegisterSet usedRegisters = this->usedRegisters(); | 
|  | if (spillMode == DontSpill) { | 
|  | // We've already flushed registers to the stack, we don't need to spill these. | 
|  | usedRegisters.set(baseGPR, false); | 
|  | usedRegisters.set(valueRegs, false); | 
|  | } | 
|  | CallSiteIndex callSite = m_jit.recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded(codeOrigin, m_stream->size()); | 
|  | JITPutByIdGenerator gen( | 
|  | m_jit.codeBlock(), codeOrigin, callSite, usedRegisters, identifier, | 
|  | JSValueRegs::payloadOnly(baseGPR), valueRegs, | 
|  | scratchGPR, ecmaMode, putKind, PrivateFieldAccessKind::None); | 
|  |  | 
|  | gen.generateFastPath(m_jit); | 
|  |  | 
|  | JITCompiler::JumpList slowCases; | 
|  | if (slowPathTarget.isSet()) | 
|  | slowCases.append(slowPathTarget); | 
|  | slowCases.append(gen.slowPathJump()); | 
|  |  | 
|  | auto slowPath = slowPathCall( | 
|  | slowCases, this, gen.slowPathFunction(), NoResult, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(codeOrigin)), gen.stubInfo(), valueRegs, | 
|  | CCallHelpers::CellValue(baseGPR), identifier.rawBits()); | 
|  |  | 
|  | m_jit.addPutById(gen, slowPath.get()); | 
|  | addSlowPathGenerator(WTFMove(slowPath)); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::genericJSValueNonPeepholeCompare(Node* node, MacroAssembler::RelationalCondition cond, S_JITOperation_GJJ helperFunction) | 
|  | { | 
|  | ASSERT(node->isBinaryUseKind(UntypedUse) || node->isBinaryUseKind(AnyBigIntUse) || node->isBinaryUseKind(HeapBigIntUse)); | 
|  | JSValueOperand arg1(this, node->child1(), ManualOperandSpeculation); | 
|  | JSValueOperand arg2(this, node->child2(), ManualOperandSpeculation); | 
|  | speculate(node, node->child1()); | 
|  | speculate(node, node->child2()); | 
|  |  | 
|  | JSValueRegs arg1Regs = arg1.jsValueRegs(); | 
|  | JSValueRegs arg2Regs = arg2.jsValueRegs(); | 
|  |  | 
|  | JITCompiler::JumpList slowPath; | 
|  |  | 
|  | if (isKnownNotInteger(node->child1().node()) || isKnownNotInteger(node->child2().node())) { | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | arg1.use(); | 
|  | arg2.use(); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(helperFunction, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), arg1Regs, arg2Regs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | unblessedBooleanResult(resultGPR, node, UseChildrenCalledExplicitly); | 
|  | return; | 
|  | } | 
|  |  | 
|  | GPRTemporary result(this, Reuse, arg1, TagWord); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | arg1.use(); | 
|  | arg2.use(); | 
|  |  | 
|  | if (!isKnownInteger(node->child1().node())) | 
|  | slowPath.append(m_jit.branchIfNotInt32(arg1Regs)); | 
|  | if (!isKnownInteger(node->child2().node())) | 
|  | slowPath.append(m_jit.branchIfNotInt32(arg2Regs)); | 
|  |  | 
|  | m_jit.compare32(cond, arg1Regs.payloadGPR(), arg2Regs.payloadGPR(), resultGPR); | 
|  |  | 
|  | if (!isKnownInteger(node->child1().node()) || !isKnownInteger(node->child2().node())) | 
|  | addSlowPathGenerator(slowPathCall(slowPath, this, helperFunction, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), arg1Regs, arg2Regs)); | 
|  |  | 
|  | unblessedBooleanResult(resultGPR, node, UseChildrenCalledExplicitly); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::genericJSValuePeepholeBranch(Node* node, Node* branchNode, MacroAssembler::RelationalCondition cond, S_JITOperation_GJJ helperFunction) | 
|  | { | 
|  | BasicBlock* taken = branchNode->branchData()->taken.block; | 
|  | BasicBlock* notTaken = branchNode->branchData()->notTaken.block; | 
|  |  | 
|  | JITCompiler::ResultCondition callResultCondition = JITCompiler::NonZero; | 
|  |  | 
|  | // 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()) { | 
|  | cond = JITCompiler::invert(cond); | 
|  | callResultCondition = JITCompiler::Zero; | 
|  | BasicBlock* tmp = taken; | 
|  | taken = notTaken; | 
|  | notTaken = tmp; | 
|  | } | 
|  |  | 
|  | JSValueOperand arg1(this, node->child1(), ManualOperandSpeculation); | 
|  | JSValueOperand arg2(this, node->child2(), ManualOperandSpeculation); | 
|  | speculate(node, node->child1()); | 
|  | speculate(node, node->child2()); | 
|  |  | 
|  | JSValueRegs arg1Regs = arg1.jsValueRegs(); | 
|  | JSValueRegs arg2Regs = arg2.jsValueRegs(); | 
|  |  | 
|  | JITCompiler::JumpList slowPath; | 
|  |  | 
|  | if (isKnownNotInteger(node->child1().node()) || isKnownNotInteger(node->child2().node())) { | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | arg1.use(); | 
|  | arg2.use(); | 
|  |  | 
|  | flushRegisters(); | 
|  | callOperation(helperFunction, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), arg1Regs, arg2Regs); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | branchTest32(callResultCondition, resultGPR, taken); | 
|  | } else { | 
|  | GPRTemporary result(this, Reuse, arg2, TagWord); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | arg1.use(); | 
|  | arg2.use(); | 
|  |  | 
|  | if (!isKnownInteger(node->child1().node())) | 
|  | slowPath.append(m_jit.branchIfNotInt32(arg1Regs)); | 
|  | if (!isKnownInteger(node->child2().node())) | 
|  | slowPath.append(m_jit.branchIfNotInt32(arg2Regs)); | 
|  |  | 
|  | branch32(cond, arg1Regs.payloadGPR(), arg2Regs.payloadGPR(), taken); | 
|  |  | 
|  | if (!isKnownInteger(node->child1().node()) || !isKnownInteger(node->child2().node())) { | 
|  | jump(notTaken, ForceJump); | 
|  |  | 
|  | slowPath.link(&m_jit); | 
|  |  | 
|  | silentSpillAllRegisters(resultGPR); | 
|  | callOperation(helperFunction, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), arg1Regs, arg2Regs); | 
|  | silentFillAllRegisters(); | 
|  | m_jit.exceptionCheck(); | 
|  |  | 
|  | branchTest32(callResultCondition, resultGPR, taken); | 
|  | } | 
|  | } | 
|  |  | 
|  | jump(notTaken); | 
|  |  | 
|  | m_indexInBlock = m_block->size() - 1; | 
|  | m_currentNode = branchNode; | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileHeapBigIntEquality(Node* node) | 
|  | { | 
|  | // FIXME: [ESNext][BigInt] Create specialized version of strict equals for big ints | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=182895 | 
|  | SpeculateCellOperand left(this, node->child1()); | 
|  | SpeculateCellOperand right(this, node->child2()); | 
|  | GPRTemporary result(this, Reuse, left); | 
|  | GPRReg leftGPR = left.gpr(); | 
|  | GPRReg rightGPR = right.gpr(); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  |  | 
|  | left.use(); | 
|  | right.use(); | 
|  |  | 
|  | speculateHeapBigInt(node->child1(), leftGPR); | 
|  | speculateHeapBigInt(node->child2(), rightGPR); | 
|  |  | 
|  | JITCompiler::Jump notEqualCase = m_jit.branchPtr(JITCompiler::NotEqual, leftGPR, rightGPR); | 
|  |  | 
|  | m_jit.move(JITCompiler::TrustedImm32(1), resultGPR); | 
|  |  | 
|  | JITCompiler::Jump done = m_jit.jump(); | 
|  |  | 
|  | notEqualCase.link(&m_jit); | 
|  |  | 
|  | silentSpillAllRegisters(resultGPR); | 
|  | callOperation(operationCompareStrictEqCell, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR); | 
|  | silentFillAllRegisters(); | 
|  |  | 
|  | done.link(&m_jit); | 
|  |  | 
|  | unblessedBooleanResult(resultGPR, node, UseChildrenCalledExplicitly); | 
|  | } | 
|  |  | 
|  | void SpeculativeJIT::compileMakeRope(Node* node) | 
|  | { | 
|  | ASSERT(node->child1().useKind() == KnownStringUse); | 
|  | ASSERT(node->child2().useKind() == KnownStringUse); | 
|  | ASSERT(!node->child3() || node->child3().useKind() == KnownStringUse); | 
|  |  | 
|  | SpeculateCellOperand op1(this, node->child1()); | 
|  | SpeculateCellOperand op2(this, node->child2()); | 
|  | SpeculateCellOperand op3(this, node->child3()); | 
|  | GPRReg opGPRs[3]; | 
|  | unsigned numOpGPRs; | 
|  | opGPRs[0] = op1.gpr(); | 
|  | opGPRs[1] = op2.gpr(); | 
|  | if (node->child3()) { | 
|  | opGPRs[2] = op3.gpr(); | 
|  | numOpGPRs = 3; | 
|  | } else { | 
|  | opGPRs[2] = InvalidGPRReg; | 
|  | numOpGPRs = 2; | 
|  | } | 
|  |  | 
|  | #if CPU(ADDRESS64) | 
|  | Edge edges[3] = { | 
|  | node->child1(), | 
|  | node->child2(), | 
|  | node->child3() | 
|  | }; | 
|  |  | 
|  | GPRTemporary result(this); | 
|  | GPRTemporary allocator(this); | 
|  | GPRTemporary scratch(this); | 
|  | GPRTemporary scratch2(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | GPRReg allocatorGPR = allocator.gpr(); | 
|  | GPRReg scratchGPR = scratch.gpr(); | 
|  | GPRReg scratch2GPR = scratch2.gpr(); | 
|  |  | 
|  | CCallHelpers::JumpList slowPath; | 
|  | Allocator allocatorValue = allocatorForNonVirtualConcurrently<JSRopeString>(vm(), sizeof(JSRopeString), AllocatorForMode::AllocatorIfExists); | 
|  | emitAllocateJSCell(resultGPR, JITAllocator::constant(allocatorValue), allocatorGPR, TrustedImmPtr(m_jit.graph().registerStructure(vm().stringStructure.get())), scratchGPR, slowPath); | 
|  |  | 
|  | // This puts nullptr for the first fiber. It makes visitChildren safe even if this JSRopeString is discarded due to the speculation failure in the following path. | 
|  | m_jit.storePtr(TrustedImmPtr(JSString::isRopeInPointer), CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber0())); | 
|  |  | 
|  | { | 
|  | if (JSString* string = edges[0]->dynamicCastConstant<JSString*>(vm())) { | 
|  | m_jit.move(TrustedImm32(string->is8Bit() ? StringImpl::flagIs8Bit() : 0), scratchGPR); | 
|  | m_jit.move(TrustedImm32(string->length()), allocatorGPR); | 
|  | } else { | 
|  | bool needsRopeCase = canBeRope(edges[0]); | 
|  | m_jit.loadPtr(CCallHelpers::Address(opGPRs[0], JSString::offsetOfValue()), scratch2GPR); | 
|  | CCallHelpers::Jump isRope; | 
|  | if (needsRopeCase) | 
|  | isRope = m_jit.branchIfRopeStringImpl(scratch2GPR); | 
|  |  | 
|  | m_jit.load32(CCallHelpers::Address(scratch2GPR, StringImpl::flagsOffset()), scratchGPR); | 
|  | m_jit.load32(CCallHelpers::Address(scratch2GPR, StringImpl::lengthMemoryOffset()), allocatorGPR); | 
|  |  | 
|  | if (needsRopeCase) { | 
|  | auto done = m_jit.jump(); | 
|  |  | 
|  | isRope.link(&m_jit); | 
|  | m_jit.load32(CCallHelpers::Address(opGPRs[0], JSRopeString::offsetOfFlags()), scratchGPR); | 
|  | m_jit.load32(CCallHelpers::Address(opGPRs[0], JSRopeString::offsetOfLength()), allocatorGPR); | 
|  | done.link(&m_jit); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (ASSERT_ENABLED) { | 
|  | CCallHelpers::Jump ok = m_jit.branch32( | 
|  | CCallHelpers::GreaterThanOrEqual, allocatorGPR, TrustedImm32(0)); | 
|  | m_jit.abortWithReason(DFGNegativeStringLength); | 
|  | ok.link(&m_jit); | 
|  | } | 
|  | } | 
|  |  | 
|  | for (unsigned i = 1; i < numOpGPRs; ++i) { | 
|  | if (JSString* string = edges[i]->dynamicCastConstant<JSString*>(vm())) { | 
|  | m_jit.and32(TrustedImm32(string->is8Bit() ? StringImpl::flagIs8Bit() : 0), scratchGPR); | 
|  | speculationCheck( | 
|  | Uncountable, JSValueSource(), nullptr, | 
|  | m_jit.branchAdd32( | 
|  | CCallHelpers::Overflow, | 
|  | TrustedImm32(string->length()), allocatorGPR)); | 
|  | } else { | 
|  | bool needsRopeCase = canBeRope(edges[i]); | 
|  | m_jit.loadPtr(CCallHelpers::Address(opGPRs[i], JSString::offsetOfValue()), scratch2GPR); | 
|  | CCallHelpers::Jump isRope; | 
|  | if (needsRopeCase) | 
|  | isRope = m_jit.branchIfRopeStringImpl(scratch2GPR); | 
|  |  | 
|  | m_jit.and32(CCallHelpers::Address(scratch2GPR, StringImpl::flagsOffset()), scratchGPR); | 
|  | speculationCheck( | 
|  | Uncountable, JSValueSource(), nullptr, | 
|  | m_jit.branchAdd32( | 
|  | CCallHelpers::Overflow, | 
|  | CCallHelpers::Address(scratch2GPR, StringImpl::lengthMemoryOffset()), allocatorGPR)); | 
|  | if (needsRopeCase) { | 
|  | auto done = m_jit.jump(); | 
|  |  | 
|  | isRope.link(&m_jit); | 
|  | m_jit.and32(CCallHelpers::Address(opGPRs[i], JSRopeString::offsetOfFlags()), scratchGPR); | 
|  | m_jit.load32(CCallHelpers::Address(opGPRs[i], JSRopeString::offsetOfLength()), scratch2GPR); | 
|  | speculationCheck( | 
|  | Uncountable, JSValueSource(), nullptr, | 
|  | m_jit.branchAdd32( | 
|  | CCallHelpers::Overflow, scratch2GPR, allocatorGPR)); | 
|  | done.link(&m_jit); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (ASSERT_ENABLED) { | 
|  | CCallHelpers::Jump ok = m_jit.branch32( | 
|  | CCallHelpers::GreaterThanOrEqual, allocatorGPR, TrustedImm32(0)); | 
|  | m_jit.abortWithReason(DFGNegativeStringLength); | 
|  | ok.link(&m_jit); | 
|  | } | 
|  |  | 
|  | static_assert(StringImpl::flagIs8Bit() == JSRopeString::is8BitInPointer, ""); | 
|  | m_jit.and32(TrustedImm32(StringImpl::flagIs8Bit()), scratchGPR); | 
|  | m_jit.orPtr(opGPRs[0], scratchGPR); | 
|  | m_jit.orPtr(TrustedImmPtr(JSString::isRopeInPointer), scratchGPR); | 
|  | m_jit.storePtr(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber0())); | 
|  |  | 
|  | m_jit.move(opGPRs[1], scratchGPR); | 
|  | m_jit.lshiftPtr(TrustedImm32(32), scratchGPR); | 
|  | m_jit.orPtr(allocatorGPR, scratchGPR); | 
|  | m_jit.storePtr(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber1())); | 
|  |  | 
|  | if (numOpGPRs == 2) { | 
|  | m_jit.move(opGPRs[1], scratchGPR); | 
|  | m_jit.rshiftPtr(TrustedImm32(32), scratchGPR); | 
|  | m_jit.storePtr(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber2())); | 
|  | } else { | 
|  | m_jit.move(opGPRs[1], scratchGPR); | 
|  | m_jit.rshiftPtr(TrustedImm32(32), scratchGPR); | 
|  | m_jit.move(opGPRs[2], scratch2GPR); | 
|  | m_jit.lshiftPtr(TrustedImm32(16), scratch2GPR); | 
|  | m_jit.orPtr(scratch2GPR, scratchGPR); | 
|  | m_jit.storePtr(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber2())); | 
|  | } | 
|  |  | 
|  | auto isNonEmptyString = m_jit.branchTest32(CCallHelpers::NonZero, allocatorGPR); | 
|  |  | 
|  | m_jit.move(TrustedImmPtr::weakPointer(m_jit.graph(), jsEmptyString(m_jit.graph().m_vm)), resultGPR); | 
|  |  | 
|  | isNonEmptyString.link(&m_jit); | 
|  | m_jit.mutatorFence(vm()); | 
|  |  | 
|  | switch (numOpGPRs) { | 
|  | case 2: | 
|  | addSlowPathGenerator(slowPathCall( | 
|  | slowPath, this, operationMakeRope2, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), opGPRs[0], opGPRs[1])); | 
|  | break; | 
|  | case 3: | 
|  | addSlowPathGenerator(slowPathCall( | 
|  | slowPath, this, operationMakeRope3, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), opGPRs[0], opGPRs[1], opGPRs[2])); | 
|  | break; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | #else | 
|  | flushRegisters(); | 
|  | GPRFlushedCallResult result(this); | 
|  | GPRReg resultGPR = result.gpr(); | 
|  | switch (numOpGPRs) { | 
|  | case 2: | 
|  | callOperation(operationMakeRope2, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), opGPRs[0], opGPRs[1]); | 
|  | m_jit.exceptionCheck(); | 
|  | break; | 
|  | case 3: | 
|  | callOperation(operationMakeRope3, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), opGPRs[0], opGPRs[1], opGPRs[2]); | 
|  | m_jit.exceptionCheck(); | 
|  | break; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  |  | 
|  | cellResult(resultGPR, node); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | } } // namespace JSC::DFG | 
|  |  | 
|  | #endif |