📄 ic.cc.svn-base
字号:
// Copyright 2006-2008 the V8 project authors. All rights reserved.// Redistribution and use in source and binary forms, with or without// modification, are permitted provided that the following conditions are// met://// * Redistributions of source code must retain the above copyright// notice, this list of conditions and the following disclaimer.// * 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.// * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT// OWNER 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 "v8.h"#include "accessors.h"#include "api.h"#include "arguments.h"#include "execution.h"#include "ic-inl.h"#include "runtime.h"#include "stub-cache.h"namespace v8 { namespace internal {#ifdef DEBUGstatic char TransitionMarkFromState(IC::State state) { switch (state) { case UNINITIALIZED: return '0'; case PREMONOMORPHIC: return '0'; case MONOMORPHIC: return '1'; case MONOMORPHIC_PROTOTYPE_FAILURE: return '^'; case MEGAMORPHIC: return 'N'; // We never see the debugger states here, because the state is // computed from the original code - not the patched code. Let // these cases fall through to the unreachable code below. case DEBUG_BREAK: break; case DEBUG_PREPARE_STEP_IN: break; } UNREACHABLE(); return 0;}void IC::TraceIC(const char* type, Handle<String> name, State old_state, Code* new_target) { if (FLAG_trace_ic) { State new_state = StateFrom(new_target, Heap::undefined_value()); PrintF("[%s (%c->%c) ", type, TransitionMarkFromState(old_state), TransitionMarkFromState(new_state)); name->Print(); PrintF("]\n"); }}#endifIC::IC(FrameDepth depth) { // To improve the performance of the (much used) IC code, we unfold // a few levels of the stack frame iteration code. This yields a // ~35% speedup when running DeltaBlue with the '--nouse-ic' flag. const Address entry = Top::c_entry_fp(Top::GetCurrentThread()); Address* pc_address = reinterpret_cast<Address*>(entry + ExitFrameConstants::kCallerPCOffset); Address fp = Memory::Address_at(entry + ExitFrameConstants::kCallerFPOffset); // If there's another JavaScript frame on the stack, we need to look // one frame further down the stack to find the frame pointer and // the return address stack slot. if (depth == EXTRA_CALL_FRAME) { const int kCallerPCOffset = StandardFrameConstants::kCallerPCOffset; pc_address = reinterpret_cast<Address*>(fp + kCallerPCOffset); fp = Memory::Address_at(fp + StandardFrameConstants::kCallerFPOffset); }#ifdef DEBUG StackFrameIterator it; for (int i = 0; i < depth + 1; i++) it.Advance(); StackFrame* frame = it.frame(); ASSERT(fp == frame->fp() && pc_address == frame->pc_address());#endif fp_ = fp; pc_address_ = pc_address;}Address IC::OriginalCodeAddress() { HandleScope scope; // Compute the JavaScript frame for the frame pointer of this IC // structure. We need this to be able to find the function // corresponding to the frame. StackFrameIterator it; while (it.frame()->fp() != this->fp()) it.Advance(); JavaScriptFrame* frame = JavaScriptFrame::cast(it.frame()); // Find the function on the stack and both the active code for the // function and the original code. JSFunction* function = JSFunction::cast(frame->function()); Handle<SharedFunctionInfo> shared(function->shared()); Code* code = shared->code(); ASSERT(Debug::HasDebugInfo(shared)); Code* original_code = Debug::GetDebugInfo(shared)->original_code(); ASSERT(original_code->IsCode()); // Get the address of the call site in the active code. This is the // place where the call to DebugBreakXXX is and where the IC // normally would be. Address addr = pc() - Assembler::kTargetAddrToReturnAddrDist; // Return the address in the original code. This is the place where // the call which has been overwriten by the DebugBreakXXX resides // and the place where the inline cache system should look. int delta = original_code->instruction_start() - code->instruction_start(); return addr + delta;}IC::State IC::StateFrom(Code* target, Object* receiver) { IC::State state = target->ic_state(); if (state != MONOMORPHIC) return state; if (receiver->IsUndefined() || receiver->IsNull()) return state; Map* map = GetCodeCacheMapForObject(receiver); // Decide whether the inline cache failed because of changes to the // receiver itself or changes to one of its prototypes. // // If there are changes to the receiver itself, the map of the // receiver will have changed and the current target will not be in // the receiver map's code cache. Therefore, if the current target // is in the receiver map's code cache, the inline cache failed due // to prototype check failure. int index = map->IndexInCodeCache(target); if (index >= 0) { // For keyed load/store, the most likely cause of cache failure is // that the key has changed. We do not distinguish between // prototype and non-prototype failures for keyed access. Code::Kind kind = target->kind(); if (kind == Code::KEYED_LOAD_IC || kind == Code::KEYED_STORE_IC) { return MONOMORPHIC; } // Remove the target from the code cache to avoid hitting the same // invalid stub again. map->RemoveFromCodeCache(index); return MONOMORPHIC_PROTOTYPE_FAILURE; } // The builtins object is special. It only changes when JavaScript // builtins are loaded lazily. It is important to keep inline // caches for the builtins object monomorphic. Therefore, if we get // an inline cache miss for the builtins object after lazily loading // JavaScript builtins, we return uninitialized as the state to // force the inline cache back to monomorphic state. if (receiver->IsJSBuiltinsObject()) { return UNINITIALIZED; } return MONOMORPHIC;}RelocInfo::Mode IC::ComputeMode() { Address addr = address(); Code* code = Code::cast(Heap::FindCodeObject(addr)); for (RelocIterator it(code, RelocInfo::kCodeTargetMask); !it.done(); it.next()) { RelocInfo* info = it.rinfo(); if (info->pc() == addr) return info->rmode(); } UNREACHABLE(); return RelocInfo::NONE;}Failure* IC::TypeError(const char* type, Handle<Object> object, Handle<String> name) { HandleScope scope; Handle<Object> args[2] = { name, object }; Handle<Object> error = Factory::NewTypeError(type, HandleVector(args, 2)); return Top::Throw(*error);}Failure* IC::ReferenceError(const char* type, Handle<String> name) { HandleScope scope; Handle<Object> error = Factory::NewReferenceError(type, HandleVector(&name, 1)); return Top::Throw(*error);}void IC::Clear(Address address) { Code* target = GetTargetAtAddress(address); // Don't clear debug break inline cache as it will remove the break point. if (target->ic_state() == DEBUG_BREAK) return; switch (target->kind()) { case Code::LOAD_IC: return LoadIC::Clear(address, target); case Code::KEYED_LOAD_IC: return KeyedLoadIC::Clear(address, target); case Code::STORE_IC: return StoreIC::Clear(address, target); case Code::KEYED_STORE_IC: return KeyedStoreIC::Clear(address, target); case Code::CALL_IC: return CallIC::Clear(address, target); default: UNREACHABLE(); }}void CallIC::Clear(Address address, Code* target) { if (target->ic_state() == UNINITIALIZED) return; Code* code = StubCache::FindCallInitialize(target->arguments_count()); SetTargetAtAddress(address, code);}void KeyedLoadIC::Clear(Address address, Code* target) { if (target->ic_state() == UNINITIALIZED) return; SetTargetAtAddress(address, initialize_stub());}void LoadIC::Clear(Address address, Code* target) { if (target->ic_state() == UNINITIALIZED) return; SetTargetAtAddress(address, initialize_stub());}void StoreIC::Clear(Address address, Code* target) { if (target->ic_state() == UNINITIALIZED) return; SetTargetAtAddress(address, initialize_stub());}void KeyedStoreIC::Clear(Address address, Code* target) { if (target->ic_state() == UNINITIALIZED) return; SetTargetAtAddress(address, initialize_stub());}Object* CallIC::TryCallAsFunction(Object* object) { HandleScope scope; Handle<Object> target(object); Handle<Object> delegate = Execution::GetFunctionDelegate(target); if (delegate->IsJSFunction()) { // Patch the receiver and use the delegate as the function to // invoke. This is used for invoking objects as if they were // functions. const int argc = this->target()->arguments_count(); StackFrameLocator locator; JavaScriptFrame* frame = locator.FindJavaScriptFrame(0); int index = frame->ComputeExpressionsCount() - (argc + 1); frame->SetExpression(index, *target); } return *delegate;}Object* CallIC::LoadFunction(State state, Handle<Object> object, Handle<String> name) { // If the object is undefined or null it's illegal to try to get any // of its properties; throw a TypeError in that case. if (object->IsUndefined() || object->IsNull()) { return TypeError("non_object_property_call", object, name); } Object* result = Heap::the_hole_value(); // Check if the name is trivially convertible to an index and get // the element if so. uint32_t index; if (name->AsArrayIndex(&index)) { result = object->GetElement(index); if (result->IsJSFunction()) return result; // Try to find a suitable function delegate for the object at hand. result = TryCallAsFunction(result); if (result->IsJSFunction()) return result; // Otherwise, it will fail in the lookup step. } // Lookup the property in the object. LookupResult lookup; object->Lookup(*name, &lookup); if (!lookup.IsValid()) { // If the object does not have the requested property, check which // exception we need to throw. if (is_contextual()) { return ReferenceError("not_defined", name); } return TypeError("undefined_method", object, name); } // Lookup is valid: Update inline cache and stub cache. if (FLAG_use_ic && lookup.IsLoaded()) { UpdateCaches(&lookup, state, object, name); } if (lookup.type() == INTERCEPTOR) { // Get the property. PropertyAttributes attr; result = object->GetProperty(*name, &attr); if (result->IsFailure()) return result; // If the object does not have the requested property, check which // exception we need to throw. if (attr == ABSENT) { if (is_contextual()) { return ReferenceError("not_defined", name); } return TypeError("undefined_method", object, name); } } else { // Lookup is valid and no interceptors are involved. Get the // property. result = object->GetProperty(*name); if (result->IsFailure()) return result; } ASSERT(result != Heap::the_hole_value()); if (result->IsJSFunction()) { // Check if there is an optimized (builtin) version of the function. // Ignored this will degrade performance for Array.prototype.{push,pop}. // Please note we only return the optimized function iff // the JSObject has FastElements. if (object->IsJSObject() && JSObject::cast(*object)->HasFastElements()) { Object* opt = Top::LookupSpecialFunction(JSObject::cast(*object), lookup.holder(), JSFunction::cast(result)); if (opt->IsJSFunction()) return opt; } // If performing debug step into then flood this function with one-shot // break points if it is called from where step into was requested. if (Debug::StepInActive() && fp() == Debug::step_in_fp()) { // Don't allow step into functions in the native context. if (JSFunction::cast(result)->context()->global() != Top::context()->builtins()) { HandleScope scope; Handle<SharedFunctionInfo> shared(JSFunction::cast(result)->shared()); Debug::FloodWithOneShot(shared); } } return result; } // Try to find a suitable function delegate for the object at hand. result = TryCallAsFunction(result); return result->IsJSFunction() ? result : TypeError("property_not_function", object, name);}void CallIC::UpdateCaches(LookupResult* lookup, State state, Handle<Object> object, Handle<String> name) { ASSERT(lookup->IsLoaded()); // Bail out if we didn't find a result. if (!lookup->IsValid() || !lookup->IsCacheable()) return; // Compute the number of arguments. int argc = target()->arguments_count(); Object* code = NULL; if (state == UNINITIALIZED) { // This is the first time we execute this inline cache. // Set the target to the pre monomorphic stub to delay
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -