X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/4be4e30906bcb8ee30b4d189205cb70bad6707ce..81345200c95645a1b0d2635520f96ad55dfde63f:/dfg/DFGOSRExitCompilerCommon.cpp diff --git a/dfg/DFGOSRExitCompilerCommon.cpp b/dfg/DFGOSRExitCompilerCommon.cpp new file mode 100644 index 0000000..5b78cb2 --- /dev/null +++ b/dfg/DFGOSRExitCompilerCommon.cpp @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2013, 2014 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 "Arguments.h" +#include "DFGJITCode.h" +#include "DFGOperations.h" +#include "JIT.h" +#include "JSCJSValueInlines.h" +#include "JSCInlines.h" + +namespace JSC { namespace DFG { + +void handleExitCounts(CCallHelpers& jit, const OSRExitBase& exit) +{ + jit.add32(AssemblyHelpers::TrustedImm32(1), AssemblyHelpers::AbsoluteAddress(&exit.m_count)); + + jit.move(AssemblyHelpers::TrustedImmPtr(jit.codeBlock()), GPRInfo::regT0); + + AssemblyHelpers::Jump tooFewFails; + + jit.load32(AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfOSRExitCounter()), GPRInfo::regT2); + jit.add32(AssemblyHelpers::TrustedImm32(1), GPRInfo::regT2); + jit.store32(GPRInfo::regT2, AssemblyHelpers::Address(GPRInfo::regT0, 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)); + + tooFewFails = jit.branch32(AssemblyHelpers::BelowOrEqual, GPRInfo::regT2, AssemblyHelpers::TrustedImm32(jit.codeBlock()->exitCountThresholdForReoptimization())); + + reoptimizeNow.link(&jit); + + // Reoptimize as soon as possible. +#if !NUMBER_OF_ARGUMENT_REGISTERS + jit.poke(GPRInfo::regT0); +#else + jit.move(GPRInfo::regT0, GPRInfo::argumentGPR0); + ASSERT(GPRInfo::argumentGPR0 != GPRInfo::regT1); +#endif + jit.move(AssemblyHelpers::TrustedImmPtr(bitwise_cast(triggerReoptimizationNow)), GPRInfo::regT1); + jit.call(GPRInfo::regT1); + 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 JITCode::DFGJIT: + clippedValue = BaselineExecutionCounter::clippedThreshold(jit.codeBlock()->globalObject(), targetValue); + break; + case JITCode::FTLJIT: + clippedValue = UpperTierExecutionCounter::clippedThreshold(jit.codeBlock()->globalObject(), targetValue); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + clippedValue = 0; // Make some compilers, and mhahnenberg, happy. + 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); +} + +void reifyInlinedCallFrames(CCallHelpers& jit, const OSRExitBase& exit) +{ + ASSERT(jit.baselineCodeBlock()->jitType() == JITCode::BaselineJIT); + jit.storePtr(AssemblyHelpers::TrustedImmPtr(jit.baselineCodeBlock()), AssemblyHelpers::addressFor((VirtualRegister)JSStack::CodeBlock)); + + CodeOrigin codeOrigin; + for (codeOrigin = exit.m_codeOrigin; codeOrigin.inlineCallFrame; codeOrigin = codeOrigin.inlineCallFrame->caller) { + InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame; + CodeBlock* baselineCodeBlock = jit.baselineCodeBlockFor(codeOrigin); + CodeBlock* baselineCodeBlockForCaller = jit.baselineCodeBlockFor(inlineCallFrame->caller); + unsigned callBytecodeIndex = inlineCallFrame->caller.bytecodeIndex; + CallLinkInfo* callLinkInfo = + baselineCodeBlockForCaller->getCallLinkInfoForBytecodeIndex(callBytecodeIndex); + RELEASE_ASSERT(callLinkInfo); + + void* jumpTarget = callLinkInfo->callReturnLocation.executableAddress(); + + GPRReg callerFrameGPR; + if (inlineCallFrame->caller.inlineCallFrame) { + jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->caller.inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT3); + callerFrameGPR = GPRInfo::regT3; + } else + callerFrameGPR = GPRInfo::callFrameRegister; + +#if USE(JSVALUE64) + jit.storePtr(AssemblyHelpers::TrustedImmPtr(baselineCodeBlock), AssemblyHelpers::addressFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::CodeBlock))); + if (!inlineCallFrame->isClosureCall) + jit.store64(AssemblyHelpers::TrustedImm64(JSValue::encode(JSValue(inlineCallFrame->calleeConstant()->scope()))), AssemblyHelpers::addressFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::ScopeChain))); + jit.store64(callerFrameGPR, AssemblyHelpers::addressForByteOffset(inlineCallFrame->callerFrameOffset())); + jit.storePtr(AssemblyHelpers::TrustedImmPtr(jumpTarget), AssemblyHelpers::addressForByteOffset(inlineCallFrame->returnPCOffset())); + uint32_t locationBits = CallFrame::Location::encodeAsBytecodeOffset(codeOrigin.bytecodeIndex); + jit.store32(AssemblyHelpers::TrustedImm32(locationBits), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::ArgumentCount))); + jit.store32(AssemblyHelpers::TrustedImm32(inlineCallFrame->arguments.size()), AssemblyHelpers::payloadFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::ArgumentCount))); + if (!inlineCallFrame->isClosureCall) + jit.store64(AssemblyHelpers::TrustedImm64(JSValue::encode(JSValue(inlineCallFrame->calleeConstant()))), AssemblyHelpers::addressFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::Callee))); + + // Leave the captured arguments in regT3. + if (baselineCodeBlock->usesArguments()) + jit.loadPtr(AssemblyHelpers::addressFor(VirtualRegister(inlineCallFrame->stackOffset + unmodifiedArgumentsRegister(baselineCodeBlock->argumentsRegister()).offset())), GPRInfo::regT3); +#else // USE(JSVALUE64) // so this is the 32-bit part + jit.storePtr(AssemblyHelpers::TrustedImmPtr(baselineCodeBlock), AssemblyHelpers::addressFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::CodeBlock))); + jit.store32(AssemblyHelpers::TrustedImm32(JSValue::CellTag), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::ScopeChain))); + if (!inlineCallFrame->isClosureCall) + jit.storePtr(AssemblyHelpers::TrustedImmPtr(inlineCallFrame->calleeConstant()->scope()), AssemblyHelpers::payloadFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::ScopeChain))); + jit.storePtr(callerFrameGPR, AssemblyHelpers::addressForByteOffset(inlineCallFrame->callerFrameOffset())); + jit.storePtr(AssemblyHelpers::TrustedImmPtr(jumpTarget), AssemblyHelpers::addressForByteOffset(inlineCallFrame->returnPCOffset())); + Instruction* instruction = baselineCodeBlock->instructions().begin() + codeOrigin.bytecodeIndex; + uint32_t locationBits = CallFrame::Location::encodeAsBytecodeInstruction(instruction); + jit.store32(AssemblyHelpers::TrustedImm32(locationBits), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::ArgumentCount))); + jit.store32(AssemblyHelpers::TrustedImm32(inlineCallFrame->arguments.size()), AssemblyHelpers::payloadFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::ArgumentCount))); + jit.store32(AssemblyHelpers::TrustedImm32(JSValue::CellTag), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::Callee))); + if (!inlineCallFrame->isClosureCall) + jit.storePtr(AssemblyHelpers::TrustedImmPtr(inlineCallFrame->calleeConstant()), AssemblyHelpers::payloadFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::Callee))); + + // Leave the captured arguments in regT3. + if (baselineCodeBlock->usesArguments()) + jit.loadPtr(AssemblyHelpers::payloadFor(VirtualRegister(inlineCallFrame->stackOffset + unmodifiedArgumentsRegister(baselineCodeBlock->argumentsRegister()).offset())), GPRInfo::regT3); +#endif // USE(JSVALUE64) // ending the #else part, so directly above is the 32-bit part + + if (baselineCodeBlock->usesArguments()) { + AssemblyHelpers::Jump noArguments = jit.branchTestPtr(AssemblyHelpers::Zero, GPRInfo::regT3); + jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0); + jit.storePtr(GPRInfo::regT0, AssemblyHelpers::Address(GPRInfo::regT3, Arguments::offsetOfRegisters())); + noArguments.link(&jit); + } + } + +#if USE(JSVALUE64) + uint32_t locationBits = CallFrame::Location::encodeAsBytecodeOffset(codeOrigin.bytecodeIndex); +#else + Instruction* instruction = jit.baselineCodeBlock()->instructions().begin() + codeOrigin.bytecodeIndex; + uint32_t locationBits = CallFrame::Location::encodeAsBytecodeInstruction(instruction); +#endif + jit.store32(AssemblyHelpers::TrustedImm32(locationBits), AssemblyHelpers::tagFor((VirtualRegister)(JSStack::ArgumentCount))); +} + +#if ENABLE(GGC) +static void osrWriteBarrier(CCallHelpers& jit, GPRReg owner, GPRReg scratch) +{ + AssemblyHelpers::Jump ownerNotMarkedOrAlreadyRemembered = jit.checkMarkByte(owner); + + // We need these extra slots because setupArgumentsWithExecState will use poke on x86. +#if CPU(X86) + jit.subPtr(MacroAssembler::TrustedImm32(sizeof(void*) * 3), MacroAssembler::stackPointerRegister); +#endif + + jit.setupArgumentsWithExecState(owner); + jit.move(MacroAssembler::TrustedImmPtr(reinterpret_cast(operationOSRWriteBarrier)), scratch); + jit.call(scratch); + +#if CPU(X86) + jit.addPtr(MacroAssembler::TrustedImm32(sizeof(void*) * 3), MacroAssembler::stackPointerRegister); +#endif + + ownerNotMarkedOrAlreadyRemembered.link(&jit); +} +#endif // ENABLE(GGC) + +void adjustAndJumpToTarget(CCallHelpers& jit, const OSRExitBase& exit) +{ +#if ENABLE(GGC) + // 11) Write barrier the owner executables because we're jumping into a different block. + jit.move(AssemblyHelpers::TrustedImmPtr(jit.codeBlock()->ownerExecutable()), GPRInfo::nonArgGPR0); + osrWriteBarrier(jit, GPRInfo::nonArgGPR0, GPRInfo::nonArgGPR1); + InlineCallFrameSet* inlineCallFrames = jit.codeBlock()->jitCode()->dfgCommon()->inlineCallFrames.get(); + if (inlineCallFrames) { + for (InlineCallFrame* inlineCallFrame : *inlineCallFrames) { + ScriptExecutable* ownerExecutable = inlineCallFrame->executable.get(); + jit.move(AssemblyHelpers::TrustedImmPtr(ownerExecutable), GPRInfo::nonArgGPR0); + osrWriteBarrier(jit, GPRInfo::nonArgGPR0, GPRInfo::nonArgGPR1); + } + } +#endif + + if (exit.m_codeOrigin.inlineCallFrame) + jit.addPtr(AssemblyHelpers::TrustedImm32(exit.m_codeOrigin.inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister); + + CodeBlock* baselineCodeBlock = jit.baselineCodeBlockFor(exit.m_codeOrigin); + Vector& decodedCodeMap = jit.decodedCodeMapFor(baselineCodeBlock); + + BytecodeAndMachineOffset* mapping = binarySearch(decodedCodeMap, decodedCodeMap.size(), exit.m_codeOrigin.bytecodeIndex, BytecodeAndMachineOffset::getBytecodeIndex); + + ASSERT(mapping); + ASSERT(mapping->m_bytecodeIndex == exit.m_codeOrigin.bytecodeIndex); + + void* jumpTarget = baselineCodeBlock->jitCode()->executableAddressAtOffset(mapping->m_machineCodeOffset); + + jit.addPtr(AssemblyHelpers::TrustedImm32(JIT::stackPointerOffsetFor(baselineCodeBlock) * sizeof(Register)), GPRInfo::callFrameRegister, AssemblyHelpers::stackPointerRegister); + + jit.jitAssertTagsInPlace(); + + jit.move(AssemblyHelpers::TrustedImmPtr(jumpTarget), GPRInfo::regT2); + jit.jump(GPRInfo::regT2); +} + +ArgumentsRecoveryGenerator::ArgumentsRecoveryGenerator() { } +ArgumentsRecoveryGenerator::~ArgumentsRecoveryGenerator() { } + +void ArgumentsRecoveryGenerator::generateFor( + int operand, CodeOrigin codeOrigin, CCallHelpers& jit) +{ + // Find the right inline call frame. + InlineCallFrame* inlineCallFrame = 0; + for (InlineCallFrame* current = codeOrigin.inlineCallFrame; + current; + current = current->caller.inlineCallFrame) { + if (current->stackOffset >= operand) { + inlineCallFrame = current; + break; + } + } + + if (!jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments()) + return; + VirtualRegister argumentsRegister = jit.baselineArgumentsRegisterFor(inlineCallFrame); + if (m_didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) { + // We know this call frame optimized out an arguments object that + // the baseline JIT would have created. Do that creation now. +#if USE(JSVALUE64) + if (inlineCallFrame) { + jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0); + jit.setupArguments(GPRInfo::regT0); + } else + jit.setupArgumentsExecState(); + jit.move( + AssemblyHelpers::TrustedImmPtr( + bitwise_cast(operationCreateArgumentsDuringOSRExit)), + GPRInfo::nonArgGPR0); + jit.call(GPRInfo::nonArgGPR0); + jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister)); + jit.store64( + GPRInfo::returnValueGPR, + AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister))); + jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms. +#else // USE(JSVALUE64) -> so the 32_64 part + if (inlineCallFrame) { + jit.setupArgumentsWithExecState( + AssemblyHelpers::TrustedImmPtr(inlineCallFrame)); + jit.move( + AssemblyHelpers::TrustedImmPtr( + bitwise_cast(operationCreateInlinedArgumentsDuringOSRExit)), + GPRInfo::nonArgGPR0); + } else { + jit.setupArgumentsExecState(); + jit.move( + AssemblyHelpers::TrustedImmPtr( + bitwise_cast(operationCreateArgumentsDuringOSRExit)), + GPRInfo::nonArgGPR0); + } + jit.call(GPRInfo::nonArgGPR0); + jit.store32( + AssemblyHelpers::TrustedImm32(JSValue::CellTag), + AssemblyHelpers::tagFor(argumentsRegister)); + jit.store32( + GPRInfo::returnValueGPR, + AssemblyHelpers::payloadFor(argumentsRegister)); + jit.store32( + AssemblyHelpers::TrustedImm32(JSValue::CellTag), + AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister))); + jit.store32( + GPRInfo::returnValueGPR, + AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister))); + jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms. +#endif // USE(JSVALUE64) + } + +#if USE(JSVALUE64) + jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0); + jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand)); +#else // USE(JSVALUE64) -> so the 32_64 part + jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0); + jit.store32( + AssemblyHelpers::TrustedImm32(JSValue::CellTag), + AssemblyHelpers::tagFor(operand)); + jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor(operand)); +#endif // USE(JSVALUE64) +} + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) +