|  | /* | 
|  | * Copyright (C) 2011-2013, 2015 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 "DFGOSRExitCompiler.h" | 
|  |  | 
|  | #if ENABLE(DFG_JIT) | 
|  |  | 
|  | #include "CallFrame.h" | 
|  | #include "DFGCommon.h" | 
|  | #include "DFGJITCode.h" | 
|  | #include "DFGOSRExitPreparation.h" | 
|  | #include "LinkBuffer.h" | 
|  | #include "OperandsInlines.h" | 
|  | #include "JSCInlines.h" | 
|  | #include "RepatchBuffer.h" | 
|  | #include <wtf/StringPrintStream.h> | 
|  |  | 
|  | namespace JSC { namespace DFG { | 
|  |  | 
|  | void OSRExitCompiler::emitRestoreArguments(const Operands<ValueRecovery>& operands) | 
|  | { | 
|  | HashMap<MinifiedID, int> alreadyAllocatedArguments; // Maps phantom arguments node ID to operand. | 
|  | for (size_t index = 0; index < operands.size(); ++index) { | 
|  | const ValueRecovery& recovery = operands[index]; | 
|  | int operand = operands.operandForIndex(index); | 
|  |  | 
|  | if (recovery.technique() != DirectArgumentsThatWereNotCreated | 
|  | && recovery.technique() != ClonedArgumentsThatWereNotCreated) | 
|  | continue; | 
|  |  | 
|  | MinifiedID id = recovery.nodeID(); | 
|  | auto iter = alreadyAllocatedArguments.find(id); | 
|  | if (iter != alreadyAllocatedArguments.end()) { | 
|  | JSValueRegs regs = JSValueRegs::withTwoAvailableRegs(GPRInfo::regT0, GPRInfo::regT1); | 
|  | m_jit.loadValue(CCallHelpers::addressFor(iter->value), regs); | 
|  | m_jit.storeValue(regs, CCallHelpers::addressFor(operand)); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | InlineCallFrame* inlineCallFrame = | 
|  | m_jit.codeBlock()->jitCode()->dfg()->minifiedDFG.at(id)->inlineCallFrame(); | 
|  |  | 
|  | int stackOffset; | 
|  | if (inlineCallFrame) | 
|  | stackOffset = inlineCallFrame->stackOffset; | 
|  | else | 
|  | stackOffset = 0; | 
|  |  | 
|  | if (!inlineCallFrame || inlineCallFrame->isClosureCall) { | 
|  | m_jit.loadPtr( | 
|  | AssemblyHelpers::addressFor(stackOffset + JSStack::Callee), | 
|  | GPRInfo::regT0); | 
|  | } else { | 
|  | m_jit.move( | 
|  | AssemblyHelpers::TrustedImmPtr(inlineCallFrame->calleeRecovery.constant().asCell()), | 
|  | GPRInfo::regT0); | 
|  | } | 
|  |  | 
|  | if (!inlineCallFrame || inlineCallFrame->isVarargs()) { | 
|  | m_jit.load32( | 
|  | AssemblyHelpers::payloadFor(stackOffset + JSStack::ArgumentCount), | 
|  | GPRInfo::regT1); | 
|  | } else { | 
|  | m_jit.move( | 
|  | AssemblyHelpers::TrustedImm32(inlineCallFrame->arguments.size()), | 
|  | GPRInfo::regT1); | 
|  | } | 
|  |  | 
|  | m_jit.setupArgumentsWithExecState( | 
|  | AssemblyHelpers::TrustedImmPtr(inlineCallFrame), GPRInfo::regT0, GPRInfo::regT1); | 
|  | switch (recovery.technique()) { | 
|  | case DirectArgumentsThatWereNotCreated: | 
|  | m_jit.move(AssemblyHelpers::TrustedImmPtr(bitwise_cast<void*>(operationCreateDirectArgumentsDuringExit)), GPRInfo::nonArgGPR0); | 
|  | break; | 
|  | case ClonedArgumentsThatWereNotCreated: | 
|  | m_jit.move(AssemblyHelpers::TrustedImmPtr(bitwise_cast<void*>(operationCreateClonedArgumentsDuringExit)), GPRInfo::nonArgGPR0); | 
|  | break; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  | m_jit.call(GPRInfo::nonArgGPR0); | 
|  | m_jit.storeCell(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(operand)); | 
|  |  | 
|  | alreadyAllocatedArguments.add(id, operand); | 
|  | } | 
|  | } | 
|  |  | 
|  | extern "C" { | 
|  |  | 
|  | void compileOSRExit(ExecState* exec) | 
|  | { | 
|  | SamplingRegion samplingRegion("DFG OSR Exit Compilation"); | 
|  |  | 
|  | CodeBlock* codeBlock = exec->codeBlock(); | 
|  |  | 
|  | ASSERT(codeBlock); | 
|  | ASSERT(codeBlock->jitType() == JITCode::DFGJIT); | 
|  |  | 
|  | VM* vm = &exec->vm(); | 
|  |  | 
|  | // It's sort of preferable that we don't GC while in here. Anyways, doing so wouldn't | 
|  | // really be profitable. | 
|  | DeferGCForAWhile deferGC(vm->heap); | 
|  |  | 
|  | uint32_t exitIndex = vm->osrExitIndex; | 
|  | OSRExit& exit = codeBlock->jitCode()->dfg()->osrExit[exitIndex]; | 
|  |  | 
|  | prepareCodeOriginForOSRExit(exec, exit.m_codeOrigin); | 
|  |  | 
|  | // Compute the value recoveries. | 
|  | Operands<ValueRecovery> operands; | 
|  | codeBlock->jitCode()->dfg()->variableEventStream.reconstruct(codeBlock, exit.m_codeOrigin, codeBlock->jitCode()->dfg()->minifiedDFG, exit.m_streamIndex, operands); | 
|  |  | 
|  | SpeculationRecovery* recovery = 0; | 
|  | if (exit.m_recoveryIndex != UINT_MAX) | 
|  | recovery = &codeBlock->jitCode()->dfg()->speculationRecovery[exit.m_recoveryIndex]; | 
|  |  | 
|  | { | 
|  | CCallHelpers jit(vm, codeBlock); | 
|  | OSRExitCompiler exitCompiler(jit); | 
|  |  | 
|  | jit.jitAssertHasValidCallFrame(); | 
|  |  | 
|  | if (vm->m_perBytecodeProfiler && codeBlock->jitCode()->dfgCommon()->compilation) { | 
|  | Profiler::Database& database = *vm->m_perBytecodeProfiler; | 
|  | Profiler::Compilation* compilation = codeBlock->jitCode()->dfgCommon()->compilation.get(); | 
|  |  | 
|  | Profiler::OSRExit* profilerExit = compilation->addOSRExit( | 
|  | exitIndex, Profiler::OriginStack(database, codeBlock, exit.m_codeOrigin), | 
|  | exit.m_kind, exit.m_kind == UncountableInvalidation); | 
|  | jit.add64(CCallHelpers::TrustedImm32(1), CCallHelpers::AbsoluteAddress(profilerExit->counterAddress())); | 
|  | } | 
|  |  | 
|  | exitCompiler.compileExit(exit, operands, recovery); | 
|  |  | 
|  | LinkBuffer patchBuffer(*vm, jit, codeBlock); | 
|  | exit.m_code = FINALIZE_CODE_IF( | 
|  | shouldShowDisassembly() || Options::verboseOSR(), | 
|  | patchBuffer, | 
|  | ("DFG OSR exit #%u (%s, %s) from %s, with operands = %s", | 
|  | exitIndex, toCString(exit.m_codeOrigin).data(), | 
|  | exitKindToString(exit.m_kind), toCString(*codeBlock).data(), | 
|  | toCString(ignoringContext<DumpContext>(operands)).data())); | 
|  | } | 
|  |  | 
|  | { | 
|  | RepatchBuffer repatchBuffer(codeBlock); | 
|  | repatchBuffer.relink(exit.codeLocationForRepatch(codeBlock), CodeLocationLabel(exit.m_code.code())); | 
|  | } | 
|  |  | 
|  | vm->osrExitJumpDestination = exit.m_code.code().executableAddress(); | 
|  | } | 
|  |  | 
|  | } // extern "C" | 
|  |  | 
|  | } } // namespace JSC::DFG | 
|  |  | 
|  | #endif // ENABLE(DFG_JIT) |