📄 jitstubs.cpp
字号:
/* * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca> * * 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. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "JITStubs.h"#if ENABLE(JIT)#include "Arguments.h"#include "CallFrame.h"#include "CodeBlock.h"#include "Collector.h"#include "Debugger.h"#include "ExceptionHelpers.h"#include "GlobalEvalFunction.h"#include "JIT.h"#include "JSActivation.h"#include "JSArray.h"#include "JSByteArray.h"#include "JSFunction.h"#include "JSNotAnObject.h"#include "JSPropertyNameIterator.h"#include "JSStaticScopeObject.h"#include "JSString.h"#include "ObjectPrototype.h"#include "Operations.h"#include "Parser.h"#include "Profiler.h"#include "RegExpObject.h"#include "RegExpPrototype.h"#include "Register.h"#include "SamplingTool.h"#include <stdio.h>using namespace std;namespace JSC {#if ENABLE(OPCODE_SAMPLING) #define CTI_SAMPLER ARG_globalData->interpreter->sampler()#else #define CTI_SAMPLER 0#endifJITStubs::JITStubs(JSGlobalData* globalData) : m_ctiArrayLengthTrampoline(0) , m_ctiStringLengthTrampoline(0) , m_ctiVirtualCallPreLink(0) , m_ctiVirtualCallLink(0) , m_ctiVirtualCall(0){ JIT::compileCTIMachineTrampolines(globalData, &m_executablePool, &m_ctiArrayLengthTrampoline, &m_ctiStringLengthTrampoline, &m_ctiVirtualCallPreLink, &m_ctiVirtualCallLink, &m_ctiVirtualCall);}#if ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS)NEVER_INLINE void JITStubs::tryCachePutByID(CallFrame* callFrame, CodeBlock* codeBlock, void* returnAddress, JSValuePtr baseValue, const PutPropertySlot& slot){ // The interpreter checks for recursion here; I do not believe this can occur in CTI. if (!baseValue.isCell()) return; // Uncacheable: give up. if (!slot.isCacheable()) { ctiPatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(JITStubs::cti_op_put_by_id_generic)); return; } JSCell* baseCell = asCell(baseValue); Structure* structure = baseCell->structure(); if (structure->isDictionary()) { ctiPatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(JITStubs::cti_op_put_by_id_generic)); return; } // If baseCell != base, then baseCell must be a proxy for another object. if (baseCell != slot.base()) { ctiPatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(JITStubs::cti_op_put_by_id_generic)); return; } StructureStubInfo* stubInfo = &codeBlock->getStubInfo(returnAddress); // Cache hit: Specialize instruction and ref Structures. // Structure transition, cache transition info if (slot.type() == PutPropertySlot::NewProperty) { StructureChain* prototypeChain = structure->prototypeChain(callFrame); stubInfo->initPutByIdTransition(structure->previousID(), structure, prototypeChain); JIT::compilePutByIdTransition(callFrame->scopeChain()->globalData, codeBlock, stubInfo, structure->previousID(), structure, slot.cachedOffset(), prototypeChain, returnAddress); return; } stubInfo->initPutByIdReplace(structure);#if USE(CTI_REPATCH_PIC) JIT::patchPutByIdReplace(stubInfo, structure, slot.cachedOffset(), returnAddress);#else JIT::compilePutByIdReplace(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, structure, slot.cachedOffset(), returnAddress);#endif}NEVER_INLINE void JITStubs::tryCacheGetByID(CallFrame* callFrame, CodeBlock* codeBlock, void* returnAddress, JSValuePtr baseValue, const Identifier& propertyName, const PropertySlot& slot){ // FIXME: Write a test that proves we need to check for recursion here just // like the interpreter does, then add a check for recursion. // FIXME: Cache property access for immediates. if (!baseValue.isCell()) { ctiPatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(JITStubs::cti_op_get_by_id_generic)); return; } JSGlobalData* globalData = &callFrame->globalData(); if (isJSArray(globalData, baseValue) && propertyName == callFrame->propertyNames().length) {#if USE(CTI_REPATCH_PIC) JIT::compilePatchGetArrayLength(callFrame->scopeChain()->globalData, codeBlock, returnAddress);#else ctiPatchCallByReturnAddress(returnAddress, globalData->jitStubs.ctiArrayLengthTrampoline());#endif return; } if (isJSString(globalData, baseValue) && propertyName == callFrame->propertyNames().length) { // The tradeoff of compiling an patched inline string length access routine does not seem // to pay off, so we currently only do this for arrays. ctiPatchCallByReturnAddress(returnAddress, globalData->jitStubs.ctiStringLengthTrampoline()); return; } // Uncacheable: give up. if (!slot.isCacheable()) { ctiPatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(JITStubs::cti_op_get_by_id_generic)); return; } JSCell* baseCell = asCell(baseValue); Structure* structure = baseCell->structure(); if (structure->isDictionary()) { ctiPatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(JITStubs::cti_op_get_by_id_generic)); return; } // In the interpreter the last structure is trapped here; in CTI we use the // *_second method to achieve a similar (but not quite the same) effect. StructureStubInfo* stubInfo = &codeBlock->getStubInfo(returnAddress); // Cache hit: Specialize instruction and ref Structures. if (slot.slotBase() == baseValue) { // set this up, so derefStructures can do it's job. stubInfo->initGetByIdSelf(structure); #if USE(CTI_REPATCH_PIC) JIT::patchGetByIdSelf(stubInfo, structure, slot.cachedOffset(), returnAddress);#else JIT::compileGetByIdSelf(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, structure, slot.cachedOffset(), returnAddress);#endif return; } if (slot.slotBase() == structure->prototypeForLookup(callFrame)) { ASSERT(slot.slotBase().isObject()); JSObject* slotBaseObject = asObject(slot.slotBase()); // Since we're accessing a prototype in a loop, it's a good bet that it // should not be treated as a dictionary. if (slotBaseObject->structure()->isDictionary()) slotBaseObject->setStructure(Structure::fromDictionaryTransition(slotBaseObject->structure())); stubInfo->initGetByIdProto(structure, slotBaseObject->structure()); JIT::compileGetByIdProto(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, structure, slotBaseObject->structure(), slot.cachedOffset(), returnAddress); return; } size_t count = countPrototypeChainEntriesAndCheckForProxies(callFrame, baseValue, slot); if (!count) { stubInfo->opcodeID = op_get_by_id_generic; return; } StructureChain* prototypeChain = structure->prototypeChain(callFrame); stubInfo->initGetByIdChain(structure, prototypeChain); JIT::compileGetByIdChain(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, structure, prototypeChain, count, slot.cachedOffset(), returnAddress);}#endif#if USE(JIT_STUB_ARGUMENT_VA_LIST)#define SETUP_VA_LISTL_ARGS va_list vl_args; va_start(vl_args, args)#else // JIT_STUB_ARGUMENT_REGISTER or JIT_STUB_ARGUMENT_STACK#define SETUP_VA_LISTL_ARGS#endif#ifndef NDEBUGextern "C" {static void jscGeneratedNativeCode() { // When executing a CTI function (which might do an allocation), we hack the return address // to pretend to be executing this function, to keep stack logging tools from blowing out // memory.}}struct StackHack { ALWAYS_INLINE StackHack(void** location) { returnAddressLocation = location; savedReturnAddress = *returnAddressLocation; ctiSetReturnAddress(returnAddressLocation, reinterpret_cast<void*>(jscGeneratedNativeCode)); } ALWAYS_INLINE ~StackHack() { ctiSetReturnAddress(returnAddressLocation, savedReturnAddress); } void** returnAddressLocation; void* savedReturnAddress;};#define BEGIN_STUB_FUNCTION() SETUP_VA_LISTL_ARGS; StackHack stackHack(&STUB_RETURN_ADDRESS_SLOT)#define STUB_SET_RETURN_ADDRESS(address) stackHack.savedReturnAddress = address#define STUB_RETURN_ADDRESS stackHack.savedReturnAddress#else#define BEGIN_STUB_FUNCTION() SETUP_VA_LISTL_ARGS#define STUB_SET_RETURN_ADDRESS(address) ctiSetReturnAddress(&STUB_RETURN_ADDRESS_SLOT, address);#define STUB_RETURN_ADDRESS STUB_RETURN_ADDRESS_SLOT#endif// The reason this is not inlined is to avoid having to do a PIC branch// to get the address of the ctiVMThrowTrampoline function. It's also// good to keep the code size down by leaving as much of the exception// handling code out of line as possible.static NEVER_INLINE void returnToThrowTrampoline(JSGlobalData* globalData, void* exceptionLocation, void*& returnAddressSlot){ ASSERT(globalData->exception); globalData->exceptionLocation = exceptionLocation; ctiSetReturnAddress(&returnAddressSlot, reinterpret_cast<void*>(ctiVMThrowTrampoline));}static NEVER_INLINE void throwStackOverflowError(CallFrame* callFrame, JSGlobalData* globalData, void* exceptionLocation, void*& returnAddressSlot){ globalData->exception = createStackOverflowError(callFrame); returnToThrowTrampoline(globalData, exceptionLocation, returnAddressSlot);}#define VM_THROW_EXCEPTION() \ do { \ VM_THROW_EXCEPTION_AT_END(); \ return 0; \ } while (0)#define VM_THROW_EXCEPTION_2() \ do { \ VM_THROW_EXCEPTION_AT_END(); \ RETURN_PAIR(0, 0); \ } while (0)#define VM_THROW_EXCEPTION_AT_END() \ returnToThrowTrampoline(ARG_globalData, STUB_RETURN_ADDRESS, STUB_RETURN_ADDRESS)#define CHECK_FOR_EXCEPTION() \ do { \ if (UNLIKELY(ARG_globalData->exception != noValue())) \ VM_THROW_EXCEPTION(); \ } while (0)#define CHECK_FOR_EXCEPTION_AT_END() \ do { \ if (UNLIKELY(ARG_globalData->exception != noValue())) \ VM_THROW_EXCEPTION_AT_END(); \ } while (0)#define CHECK_FOR_EXCEPTION_VOID() \ do { \ if (UNLIKELY(ARG_globalData->exception != noValue())) { \ VM_THROW_EXCEPTION_AT_END(); \ return; \ } \ } while (0)JSObject* JITStubs::cti_op_convert_this(STUB_ARGS){ BEGIN_STUB_FUNCTION(); JSValuePtr v1 = ARG_src1; CallFrame* callFrame = ARG_callFrame; JSObject* result = v1.toThisObject(callFrame); CHECK_FOR_EXCEPTION_AT_END(); return result;}void JITStubs::cti_op_end(STUB_ARGS){ BEGIN_STUB_FUNCTION(); ScopeChainNode* scopeChain = ARG_callFrame->scopeChain(); ASSERT(scopeChain->refCount > 1); scopeChain->deref();}JSValueEncodedAsPointer* JITStubs::cti_op_add(STUB_ARGS){ BEGIN_STUB_FUNCTION(); JSValuePtr v1 = ARG_src1; JSValuePtr v2 = ARG_src2; double left; double right = 0.0; bool rightIsNumber = v2.getNumber(right); if (rightIsNumber && v1.getNumber(left)) return JSValuePtr::encode(jsNumber(ARG_globalData, left + right)); CallFrame* callFrame = ARG_callFrame; bool leftIsString = v1.isString(); if (leftIsString && v2.isString()) { RefPtr<UString::Rep> value = concatenate(asString(v1)->value().rep(), asString(v2)->value().rep()); if (UNLIKELY(!value)) { throwOutOfMemoryError(callFrame); VM_THROW_EXCEPTION(); } return JSValuePtr::encode(jsString(ARG_globalData, value.release())); } if (rightIsNumber & leftIsString) { RefPtr<UString::Rep> value = v2.isInt32Fast() ? concatenate(asString(v1)->value().rep(), v2.getInt32Fast()) : concatenate(asString(v1)->value().rep(), right); if (UNLIKELY(!value)) { throwOutOfMemoryError(callFrame); VM_THROW_EXCEPTION(); } return JSValuePtr::encode(jsString(ARG_globalData, value.release())); } // All other cases are pretty uncommon JSValuePtr result = jsAddSlowCase(callFrame, v1, v2); CHECK_FOR_EXCEPTION_AT_END(); return JSValuePtr::encode(result);}JSValueEncodedAsPointer* JITStubs::cti_op_pre_inc(STUB_ARGS){ BEGIN_STUB_FUNCTION(); JSValuePtr v = ARG_src1; CallFrame* callFrame = ARG_callFrame; JSValuePtr result = jsNumber(ARG_globalData, v.toNumber(callFrame) + 1); CHECK_FOR_EXCEPTION_AT_END(); return JSValuePtr::encode(result);}int JITStubs::cti_timeout_check(STUB_ARGS){ BEGIN_STUB_FUNCTION();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -