|  | /* | 
|  | * Copyright (C) 2013-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 "DFGOSRExitCompilerCommon.h" | 
|  |  | 
|  | #if ENABLE(DFG_JIT) | 
|  |  | 
|  | #include "DFGJITCode.h" | 
|  | #include "DFGOperations.h" | 
|  | #include "JIT.h" | 
|  | #include "JSCJSValueInlines.h" | 
|  | #include "LLIntData.h" | 
|  | #include "LLIntThunks.h" | 
|  | #include "StructureStubInfo.h" | 
|  |  | 
|  | namespace JSC { namespace DFG { | 
|  |  | 
|  | void handleExitCounts(VM& vm, CCallHelpers& jit, const OSRExitBase& exit) | 
|  | { | 
|  | if (!exitKindMayJettison(exit.m_kind)) { | 
|  | // FIXME: We may want to notice that we're frequently exiting | 
|  | // at an op_catch that we didn't compile an entrypoint for, and | 
|  | // then trigger a reoptimization of this CodeBlock: | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=175842 | 
|  | return; | 
|  | } | 
|  |  | 
|  | jit.add32(AssemblyHelpers::TrustedImm32(1), AssemblyHelpers::AbsoluteAddress(&exit.m_count)); | 
|  |  | 
|  | jit.move(AssemblyHelpers::TrustedImmPtr(jit.codeBlock()), GPRInfo::regT3); | 
|  |  | 
|  | AssemblyHelpers::Jump tooFewFails; | 
|  |  | 
|  | jit.load32(AssemblyHelpers::Address(GPRInfo::regT3, CodeBlock::offsetOfOSRExitCounter()), GPRInfo::regT2); | 
|  | jit.add32(AssemblyHelpers::TrustedImm32(1), GPRInfo::regT2); | 
|  | jit.store32(GPRInfo::regT2, AssemblyHelpers::Address(GPRInfo::regT3, CodeBlock::offsetOfOSRExitCounter())); | 
|  |  | 
|  | jit.move(AssemblyHelpers::TrustedImmPtr(jit.baselineCodeBlock()), GPRInfo::regT0); | 
|  | AssemblyHelpers::Jump reoptimizeNow = jit.branch32( | 
|  | AssemblyHelpers::GreaterThanOrEqual, | 
|  | AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter()), | 
|  | AssemblyHelpers::TrustedImm32(0)); | 
|  |  | 
|  | // We want to figure out if there's a possibility that we're in a loop. For the outermost | 
|  | // code block in the inline stack, we handle this appropriately by having the loop OSR trigger | 
|  | // check the exit count of the replacement of the CodeBlock from which we are OSRing. The | 
|  | // problem is the inlined functions, which might also have loops, but whose baseline versions | 
|  | // don't know where to look for the exit count. Figure out if those loops are severe enough | 
|  | // that we had tried to OSR enter. If so, then we should use the loop reoptimization trigger. | 
|  | // Otherwise, we should use the normal reoptimization trigger. | 
|  |  | 
|  | AssemblyHelpers::JumpList loopThreshold; | 
|  |  | 
|  | for (InlineCallFrame* inlineCallFrame = exit.m_codeOrigin.inlineCallFrame(); inlineCallFrame; inlineCallFrame = inlineCallFrame->directCaller.inlineCallFrame()) { | 
|  | loopThreshold.append( | 
|  | jit.branchTest8( | 
|  | AssemblyHelpers::NonZero, | 
|  | AssemblyHelpers::AbsoluteAddress( | 
|  | inlineCallFrame->baselineCodeBlock->ownerExecutable()->addressOfDidTryToEnterInLoop()))); | 
|  | } | 
|  |  | 
|  | jit.move( | 
|  | AssemblyHelpers::TrustedImm32(jit.codeBlock()->exitCountThresholdForReoptimization()), | 
|  | GPRInfo::regT1); | 
|  |  | 
|  | if (!loopThreshold.empty()) { | 
|  | AssemblyHelpers::Jump done = jit.jump(); | 
|  |  | 
|  | loopThreshold.link(&jit); | 
|  | jit.move( | 
|  | AssemblyHelpers::TrustedImm32( | 
|  | jit.codeBlock()->exitCountThresholdForReoptimizationFromLoop()), | 
|  | GPRInfo::regT1); | 
|  |  | 
|  | done.link(&jit); | 
|  | } | 
|  |  | 
|  | tooFewFails = jit.branch32(AssemblyHelpers::BelowOrEqual, GPRInfo::regT2, GPRInfo::regT1); | 
|  |  | 
|  | reoptimizeNow.link(&jit); | 
|  |  | 
|  | jit.setupArguments<decltype(operationTriggerReoptimizationNow)>(GPRInfo::regT0, GPRInfo::regT3, AssemblyHelpers::TrustedImmPtr(&exit)); | 
|  | jit.prepareCallOperation(vm); | 
|  | jit.move(AssemblyHelpers::TrustedImmPtr(tagCFunction<OperationPtrTag>(operationTriggerReoptimizationNow)), GPRInfo::nonArgGPR0); | 
|  | jit.call(GPRInfo::nonArgGPR0, OperationPtrTag); | 
|  | AssemblyHelpers::Jump doneAdjusting = jit.jump(); | 
|  |  | 
|  | tooFewFails.link(&jit); | 
|  |  | 
|  | // Adjust the execution counter such that the target is to only optimize after a while. | 
|  | int32_t activeThreshold = | 
|  | jit.baselineCodeBlock()->adjustedCounterValue( | 
|  | Options::thresholdForOptimizeAfterLongWarmUp()); | 
|  | int32_t targetValue = applyMemoryUsageHeuristicsAndConvertToInt( | 
|  | activeThreshold, jit.baselineCodeBlock()); | 
|  | int32_t clippedValue; | 
|  | switch (jit.codeBlock()->jitType()) { | 
|  | case JITType::DFGJIT: | 
|  | clippedValue = BaselineExecutionCounter::clippedThreshold(jit.codeBlock()->globalObject(), targetValue); | 
|  | break; | 
|  | case JITType::FTLJIT: | 
|  | clippedValue = UpperTierExecutionCounter::clippedThreshold(jit.codeBlock()->globalObject(), targetValue); | 
|  | break; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | #if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE) | 
|  | clippedValue = 0; // Make some compilers, and mhahnenberg, happy. | 
|  | #endif | 
|  | break; | 
|  | } | 
|  | jit.store32(AssemblyHelpers::TrustedImm32(-clippedValue), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter())); | 
|  | jit.store32(AssemblyHelpers::TrustedImm32(activeThreshold), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecutionActiveThreshold())); | 
|  | jit.store32(AssemblyHelpers::TrustedImm32(formattedTotalExecutionCount(clippedValue)), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecutionTotalCount())); | 
|  |  | 
|  | doneAdjusting.link(&jit); | 
|  | } | 
|  |  | 
|  | MacroAssemblerCodePtr<JSEntryPtrTag> callerReturnPC(CodeBlock* baselineCodeBlockForCaller, BytecodeIndex callBytecodeIndex, InlineCallFrame::Kind trueCallerCallKind, bool& callerIsLLInt) | 
|  | { | 
|  | callerIsLLInt = Options::forceOSRExitToLLInt() || baselineCodeBlockForCaller->jitType() == JITType::InterpreterThunk; | 
|  |  | 
|  | if (callBytecodeIndex.checkpoint()) { | 
|  | if (!callerIsLLInt) | 
|  | baselineCodeBlockForCaller->m_hasLinkedOSRExit = true; | 
|  | return LLInt::getCodePtr<JSEntryPtrTag>(checkpoint_osr_exit_from_inlined_call_trampoline); | 
|  | } | 
|  |  | 
|  | MacroAssemblerCodePtr<JSEntryPtrTag> jumpTarget; | 
|  |  | 
|  | const Instruction& callInstruction = *baselineCodeBlockForCaller->instructions().at(callBytecodeIndex).ptr(); | 
|  | if (callerIsLLInt) { | 
|  | #define LLINT_RETURN_LOCATION(name) (callInstruction.isWide16() ? LLInt::getWide16CodePtr<JSEntryPtrTag>(name##_return_location) : (callInstruction.isWide32() ? LLInt::getWide32CodePtr<JSEntryPtrTag>(name##_return_location) : LLInt::getCodePtr<JSEntryPtrTag>(name##_return_location))) | 
|  |  | 
|  | switch (trueCallerCallKind) { | 
|  | case InlineCallFrame::Call: { | 
|  | if (callInstruction.opcodeID() == op_call) | 
|  | jumpTarget = LLINT_RETURN_LOCATION(op_call); | 
|  | else if (callInstruction.opcodeID() == op_iterator_open) | 
|  | jumpTarget = LLINT_RETURN_LOCATION(op_iterator_open); | 
|  | else if (callInstruction.opcodeID() == op_iterator_next) | 
|  | jumpTarget = LLINT_RETURN_LOCATION(op_iterator_next); | 
|  | break; | 
|  | } | 
|  | case InlineCallFrame::Construct: | 
|  | jumpTarget = LLINT_RETURN_LOCATION(op_construct); | 
|  | break; | 
|  | case InlineCallFrame::CallVarargs: | 
|  | jumpTarget = LLINT_RETURN_LOCATION(op_call_varargs_slow); | 
|  | break; | 
|  | case InlineCallFrame::ConstructVarargs: | 
|  | jumpTarget = LLINT_RETURN_LOCATION(op_construct_varargs_slow); | 
|  | break; | 
|  | case InlineCallFrame::GetterCall: { | 
|  | if (callInstruction.opcodeID() == op_get_by_id) | 
|  | jumpTarget = LLINT_RETURN_LOCATION(op_get_by_id); | 
|  | else if (callInstruction.opcodeID() == op_get_by_val) | 
|  | jumpTarget = LLINT_RETURN_LOCATION(op_get_by_val); | 
|  | else | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  | case InlineCallFrame::SetterCall: { | 
|  | if (callInstruction.opcodeID() == op_put_by_id) | 
|  | jumpTarget = LLINT_RETURN_LOCATION(op_put_by_id); | 
|  | else if (callInstruction.opcodeID() == op_put_by_val) | 
|  | jumpTarget = LLINT_RETURN_LOCATION(op_put_by_val); | 
|  | else | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  |  | 
|  | #undef LLINT_RETURN_LOCATION | 
|  |  | 
|  | } else { | 
|  | baselineCodeBlockForCaller->m_hasLinkedOSRExit = true; | 
|  |  | 
|  | switch (trueCallerCallKind) { | 
|  | case InlineCallFrame::Call: | 
|  | case InlineCallFrame::Construct: | 
|  | case InlineCallFrame::CallVarargs: | 
|  | case InlineCallFrame::ConstructVarargs: { | 
|  | CallLinkInfo* callLinkInfo = | 
|  | baselineCodeBlockForCaller->getCallLinkInfoForBytecodeIndex(callBytecodeIndex); | 
|  | RELEASE_ASSERT(callLinkInfo); | 
|  |  | 
|  | jumpTarget = callLinkInfo->callReturnLocation().retagged<JSEntryPtrTag>(); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case InlineCallFrame::GetterCall: | 
|  | case InlineCallFrame::SetterCall: { | 
|  | if (callInstruction.opcodeID() == op_put_by_val) { | 
|  | // We compile op_put_by_val as PutById and inlines SetterCall only when we found StructureStubInfo for this op_put_by_val. | 
|  | // But still it is possible that we cannot find StructureStubInfo here. Let's consider the following scenario. | 
|  | // 1. Baseline CodeBlock (A) is compiled. | 
|  | // 2. (A) gets DFG (B). | 
|  | // 3. Since (A) collects enough information for put_by_val, (B) can get StructureStubInfo from (A) and copmile it as inlined Setter call. | 
|  | // 4. (A)'s JITData is destroyed since it is not executed. Then, (A) becomes LLInt. | 
|  | // 5. The CodeBlock inlining (A) gets OSR exit. So (A) is executed and (A) eventually gets Baseline CodeBlock again. | 
|  | // 6. (B) gets OSR exit. (B) attempts to search for StructureStubInfo in (A) for PutById (originally, put_by_val). But it does not exist since (A)'s JITData is cleared once. | 
|  | ByValInfo* byValInfo = baselineCodeBlockForCaller->findByValInfo(CodeOrigin(callBytecodeIndex)); | 
|  | RELEASE_ASSERT(byValInfo); | 
|  | jumpTarget = byValInfo->doneTarget.retagged<JSEntryPtrTag>(); | 
|  | break; | 
|  | } | 
|  |  | 
|  | StructureStubInfo* stubInfo = baselineCodeBlockForCaller->findStubInfo(CodeOrigin(callBytecodeIndex)); | 
|  | RELEASE_ASSERT(stubInfo); | 
|  | jumpTarget = stubInfo->doneLocation.retagged<JSEntryPtrTag>(); | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  | } | 
|  |  | 
|  | ASSERT(jumpTarget); | 
|  | return jumpTarget; | 
|  | } | 
|  |  | 
|  | CCallHelpers::Address calleeSaveSlot(InlineCallFrame* inlineCallFrame, CodeBlock* baselineCodeBlock, GPRReg calleeSave) | 
|  | { | 
|  | const RegisterAtOffsetList* calleeSaves = baselineCodeBlock->calleeSaveRegisters(); | 
|  | for (unsigned i = 0; i < calleeSaves->size(); i++) { | 
|  | RegisterAtOffset entry = calleeSaves->at(i); | 
|  | if (entry.reg() != calleeSave) | 
|  | continue; | 
|  | return CCallHelpers::Address(CCallHelpers::framePointerRegister, static_cast<VirtualRegister>(inlineCallFrame->stackOffset).offsetInBytes() + entry.offset()); | 
|  | } | 
|  |  | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return CCallHelpers::Address(CCallHelpers::framePointerRegister); | 
|  | } | 
|  |  | 
|  | void reifyInlinedCallFrames(CCallHelpers& jit, const OSRExitBase& exit) | 
|  | { | 
|  | // FIXME: We shouldn't leave holes on the stack when performing an OSR exit | 
|  | // in presence of inlined tail calls. | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=147511 | 
|  | ASSERT(JITCode::isBaselineCode(jit.baselineCodeBlock()->jitType())); | 
|  | jit.storePtr(AssemblyHelpers::TrustedImmPtr(jit.baselineCodeBlock()), AssemblyHelpers::addressFor(CallFrameSlot::codeBlock)); | 
|  |  | 
|  | const CodeOrigin* codeOrigin; | 
|  | for (codeOrigin = &exit.m_codeOrigin; codeOrigin && codeOrigin->inlineCallFrame(); codeOrigin = codeOrigin->inlineCallFrame()->getCallerSkippingTailCalls()) { | 
|  | InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame(); | 
|  | CodeBlock* baselineCodeBlock = jit.baselineCodeBlockFor(*codeOrigin); | 
|  | InlineCallFrame::Kind trueCallerCallKind; | 
|  | CodeOrigin* trueCaller = inlineCallFrame->getCallerSkippingTailCalls(&trueCallerCallKind); | 
|  | GPRReg callerFrameGPR = GPRInfo::callFrameRegister; | 
|  |  | 
|  | bool callerIsLLInt = false; | 
|  |  | 
|  | if (!trueCaller) { | 
|  | ASSERT(inlineCallFrame->isTail()); | 
|  | jit.loadPtr(AssemblyHelpers::Address(GPRInfo::callFrameRegister, CallFrame::returnPCOffset()), GPRInfo::regT3); | 
|  | #if CPU(ARM64E) | 
|  | jit.addPtr(AssemblyHelpers::TrustedImm32(sizeof(CallerFrameAndPC)), GPRInfo::callFrameRegister, GPRInfo::regT2); | 
|  | jit.untagPtr(GPRInfo::regT2, GPRInfo::regT3); | 
|  | jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->returnPCOffset() + sizeof(void*)), GPRInfo::callFrameRegister, GPRInfo::regT2); | 
|  | jit.tagPtr(GPRInfo::regT2, GPRInfo::regT3); | 
|  | #endif | 
|  | jit.storePtr(GPRInfo::regT3, AssemblyHelpers::addressForByteOffset(inlineCallFrame->returnPCOffset())); | 
|  | jit.loadPtr(AssemblyHelpers::Address(GPRInfo::callFrameRegister, CallFrame::callerFrameOffset()), GPRInfo::regT3); | 
|  | callerFrameGPR = GPRInfo::regT3; | 
|  | } else { | 
|  | CodeBlock* baselineCodeBlockForCaller = jit.baselineCodeBlockFor(*trueCaller); | 
|  | auto callBytecodeIndex = trueCaller->bytecodeIndex(); | 
|  | MacroAssemblerCodePtr<JSEntryPtrTag> jumpTarget = callerReturnPC(baselineCodeBlockForCaller, callBytecodeIndex, trueCallerCallKind, callerIsLLInt); | 
|  |  | 
|  | if (trueCaller->inlineCallFrame()) { | 
|  | jit.addPtr( | 
|  | AssemblyHelpers::TrustedImm32(trueCaller->inlineCallFrame()->stackOffset * sizeof(EncodedJSValue)), | 
|  | GPRInfo::callFrameRegister, | 
|  | GPRInfo::regT3); | 
|  | callerFrameGPR = GPRInfo::regT3; | 
|  | } | 
|  |  | 
|  | #if CPU(ARM64E) | 
|  | jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->returnPCOffset() + sizeof(void*)), GPRInfo::callFrameRegister, GPRInfo::regT2); | 
|  | jit.move(AssemblyHelpers::TrustedImmPtr(jumpTarget.untaggedExecutableAddress()), GPRInfo::nonArgGPR0); | 
|  | jit.tagPtr(GPRInfo::regT2, GPRInfo::nonArgGPR0); | 
|  | jit.storePtr(GPRInfo::nonArgGPR0, AssemblyHelpers::addressForByteOffset(inlineCallFrame->returnPCOffset())); | 
|  | #else | 
|  | jit.storePtr(AssemblyHelpers::TrustedImmPtr(jumpTarget.untaggedExecutableAddress()), AssemblyHelpers::addressForByteOffset(inlineCallFrame->returnPCOffset())); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | jit.storePtr(AssemblyHelpers::TrustedImmPtr(baselineCodeBlock), AssemblyHelpers::addressFor((VirtualRegister)(inlineCallFrame->stackOffset + CallFrameSlot::codeBlock))); | 
|  |  | 
|  | // Restore the inline call frame's callee save registers. | 
|  | // If this inlined frame is a tail call that will return back to the original caller, we need to | 
|  | // copy the prior contents of the tag registers already saved for the outer frame to this frame. | 
|  | jit.emitSaveOrCopyCalleeSavesFor( | 
|  | baselineCodeBlock, | 
|  | static_cast<VirtualRegister>(inlineCallFrame->stackOffset), | 
|  | trueCaller ? AssemblyHelpers::UseExistingTagRegisterContents : AssemblyHelpers::CopyBaselineCalleeSavedRegistersFromBaseFrame, | 
|  | GPRInfo::regT2); | 
|  |  | 
|  | if (callerIsLLInt) { | 
|  | CodeBlock* baselineCodeBlockForCaller = jit.baselineCodeBlockFor(*trueCaller); | 
|  | jit.storePtr(CCallHelpers::TrustedImmPtr(baselineCodeBlockForCaller->metadataTable()), calleeSaveSlot(inlineCallFrame, baselineCodeBlock, LLInt::Registers::metadataTableGPR)); | 
|  | jit.storePtr(CCallHelpers::TrustedImmPtr(baselineCodeBlockForCaller->instructionsRawPointer()), calleeSaveSlot(inlineCallFrame, baselineCodeBlock, LLInt::Registers::pbGPR)); | 
|  | } | 
|  |  | 
|  | if (!inlineCallFrame->isVarargs()) | 
|  | jit.store32(AssemblyHelpers::TrustedImm32(inlineCallFrame->argumentCountIncludingThis), AssemblyHelpers::payloadFor(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis))); | 
|  | jit.storePtr(callerFrameGPR, AssemblyHelpers::addressForByteOffset(inlineCallFrame->callerFrameOffset())); | 
|  | uint32_t locationBits = CallSiteIndex(baselineCodeBlock->bytecodeIndexForExit(codeOrigin->bytecodeIndex())).bits(); | 
|  | jit.store32(AssemblyHelpers::TrustedImm32(locationBits), AssemblyHelpers::tagFor(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis))); | 
|  | #if USE(JSVALUE64) | 
|  | if (!inlineCallFrame->isClosureCall) | 
|  | jit.store64(AssemblyHelpers::TrustedImm64(JSValue::encode(JSValue(inlineCallFrame->calleeConstant()))), AssemblyHelpers::addressFor(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::callee))); | 
|  | #else // USE(JSVALUE64) // so this is the 32-bit part | 
|  | jit.store32(AssemblyHelpers::TrustedImm32(JSValue::CellTag), AssemblyHelpers::tagFor(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::callee))); | 
|  | if (!inlineCallFrame->isClosureCall) | 
|  | jit.storePtr(AssemblyHelpers::TrustedImmPtr(inlineCallFrame->calleeConstant()), AssemblyHelpers::payloadFor(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::callee))); | 
|  | #endif // USE(JSVALUE64) // ending the #else part, so directly above is the 32-bit part | 
|  | } | 
|  |  | 
|  | // Don't need to set the toplevel code origin if we only did inline tail calls | 
|  | if (codeOrigin) { | 
|  | uint32_t locationBits = CallSiteIndex(BytecodeIndex(codeOrigin->bytecodeIndex().offset())).bits(); | 
|  | jit.store32(AssemblyHelpers::TrustedImm32(locationBits), AssemblyHelpers::tagFor(CallFrameSlot::argumentCountIncludingThis)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void osrWriteBarrier(VM& vm, CCallHelpers& jit, GPRReg owner, GPRReg scratch) | 
|  | { | 
|  | AssemblyHelpers::Jump ownerIsRememberedOrInEden = jit.barrierBranchWithoutFence(owner); | 
|  |  | 
|  | jit.setupArguments<decltype(operationOSRWriteBarrier)>(&vm, owner); | 
|  | jit.prepareCallOperation(vm); | 
|  | jit.move(MacroAssembler::TrustedImmPtr(tagCFunction<OperationPtrTag>(operationOSRWriteBarrier)), scratch); | 
|  | jit.call(scratch, OperationPtrTag); | 
|  |  | 
|  | ownerIsRememberedOrInEden.link(&jit); | 
|  | } | 
|  |  | 
|  | void adjustAndJumpToTarget(VM& vm, CCallHelpers& jit, const OSRExitBase& exit) | 
|  | { | 
|  | jit.memoryFence(); | 
|  |  | 
|  | jit.move( | 
|  | AssemblyHelpers::TrustedImmPtr( | 
|  | jit.codeBlock()->baselineAlternative()), GPRInfo::argumentGPR1); | 
|  | osrWriteBarrier(vm, jit, GPRInfo::argumentGPR1, GPRInfo::nonArgGPR0); | 
|  |  | 
|  | // We barrier all inlined frames -- and not just the current inline stack -- | 
|  | // because we don't know which inlined function owns the value profile that | 
|  | // we'll update when we exit. In the case of "f() { a(); b(); }", if both | 
|  | // a and b are inlined, we might exit inside b due to a bad value loaded | 
|  | // from a. | 
|  | // FIXME: MethodOfGettingAValueProfile should remember which CodeBlock owns | 
|  | // the value profile. | 
|  | InlineCallFrameSet* inlineCallFrames = jit.codeBlock()->jitCode()->dfgCommon()->inlineCallFrames.get(); | 
|  | if (inlineCallFrames) { | 
|  | for (InlineCallFrame* inlineCallFrame : *inlineCallFrames) { | 
|  | jit.move( | 
|  | AssemblyHelpers::TrustedImmPtr( | 
|  | inlineCallFrame->baselineCodeBlock.get()), GPRInfo::argumentGPR1); | 
|  | osrWriteBarrier(vm, jit, GPRInfo::argumentGPR1, GPRInfo::nonArgGPR0); | 
|  | } | 
|  | } | 
|  |  | 
|  | auto* exitInlineCallFrame = exit.m_codeOrigin.inlineCallFrame(); | 
|  | if (exitInlineCallFrame) | 
|  | jit.addPtr(AssemblyHelpers::TrustedImm32(exitInlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister); | 
|  |  | 
|  | CodeBlock* codeBlockForExit = jit.baselineCodeBlockFor(exit.m_codeOrigin); | 
|  | ASSERT(codeBlockForExit == codeBlockForExit->baselineVersion()); | 
|  | ASSERT(JITCode::isBaselineCode(codeBlockForExit->jitType())); | 
|  |  | 
|  | void* jumpTarget; | 
|  | bool exitToLLInt = Options::forceOSRExitToLLInt() || codeBlockForExit->jitType() == JITType::InterpreterThunk; | 
|  | if (exitToLLInt) { | 
|  | auto bytecodeIndex = exit.m_codeOrigin.bytecodeIndex(); | 
|  | const Instruction& currentInstruction = *codeBlockForExit->instructions().at(bytecodeIndex).ptr(); | 
|  | MacroAssemblerCodePtr<JSEntryPtrTag> destination; | 
|  | if (bytecodeIndex.checkpoint()) | 
|  | destination = LLInt::getCodePtr<JSEntryPtrTag>(checkpoint_osr_exit_trampoline); | 
|  | else | 
|  | destination = LLInt::getCodePtr<JSEntryPtrTag>(currentInstruction); | 
|  |  | 
|  | if (exit.isExceptionHandler()) { | 
|  | jit.move(CCallHelpers::TrustedImmPtr(¤tInstruction), GPRInfo::regT2); | 
|  | jit.storePtr(GPRInfo::regT2, &vm.targetInterpreterPCForThrow); | 
|  | } | 
|  |  | 
|  | jit.move(CCallHelpers::TrustedImmPtr(codeBlockForExit->metadataTable()), LLInt::Registers::metadataTableGPR); | 
|  | jit.move(CCallHelpers::TrustedImmPtr(codeBlockForExit->instructionsRawPointer()), LLInt::Registers::pbGPR); | 
|  | jit.move(CCallHelpers::TrustedImm32(bytecodeIndex.offset()), LLInt::Registers::pcGPR); | 
|  | jumpTarget = destination.retagged<OSRExitPtrTag>().executableAddress(); | 
|  | } else { | 
|  | codeBlockForExit->m_hasLinkedOSRExit = true; | 
|  |  | 
|  | BytecodeIndex exitIndex = exit.m_codeOrigin.bytecodeIndex(); | 
|  | MacroAssemblerCodePtr<JSEntryPtrTag> destination; | 
|  | if (exitIndex.checkpoint()) | 
|  | destination = LLInt::getCodePtr<JSEntryPtrTag>(checkpoint_osr_exit_trampoline); | 
|  | else { | 
|  | ASSERT(codeBlockForExit->bytecodeIndexForExit(exitIndex) == exitIndex); | 
|  | destination = codeBlockForExit->jitCodeMap().find(exitIndex); | 
|  | } | 
|  |  | 
|  | ASSERT(destination); | 
|  |  | 
|  | jumpTarget = destination.retagged<OSRExitPtrTag>().executableAddress(); | 
|  | } | 
|  |  | 
|  | jit.addPtr(AssemblyHelpers::TrustedImm32(JIT::stackPointerOffsetFor(codeBlockForExit) * sizeof(Register)), GPRInfo::callFrameRegister, AssemblyHelpers::stackPointerRegister); | 
|  | if (exit.isExceptionHandler()) { | 
|  | // Since we're jumping to op_catch, we need to set callFrameForCatch. | 
|  | jit.storePtr(GPRInfo::callFrameRegister, vm.addressOfCallFrameForCatch()); | 
|  | } | 
|  |  | 
|  | jit.move(AssemblyHelpers::TrustedImmPtr(jumpTarget), GPRInfo::regT2); | 
|  | jit.farJump(GPRInfo::regT2, OSRExitPtrTag); | 
|  | } | 
|  |  | 
|  | } } // namespace JSC::DFG | 
|  |  | 
|  | #endif // ENABLE(DFG_JIT) | 
|  |  |