Commit c56b92d6 authored by serya@chromium.org's avatar serya@chromium.org

This change allows generating call-stubs for objects with normal (non-fast)...

This change allows generating call-stubs for objects with normal (non-fast) objects in the prototype chain. StubCompiler::CheckPrototypes does ne

If the top level object is a normal object the stub is stored in its prototype map.

Lookup result of type NORMAL is not covered (since the normal stub currently doesn't check the prototype chain).
Review URL: http://codereview.chromium.org/2801018

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5010 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 04da7b90
......@@ -741,7 +741,8 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
Register scratch,
String* name,
int save_at_depth,
Label* miss) {
Label* miss,
Register extra) {
// Check that the maps haven't changed.
Register result =
masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch,
......
......@@ -463,6 +463,12 @@ enum CallFunctionFlags {
};
enum InlineCacheHolderFlag {
OWN_MAP, // For fast properties objects.
PROTOTYPE_MAP // For slow properties objects (except GlobalObjects).
};
// Type of properties.
// Order of properties is significant.
// Must fit in the BitField PropertyDetails::TypeField.
......
......@@ -542,97 +542,6 @@ void MacroAssembler::PopTryHandler() {
}
Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
JSObject* holder, Register holder_reg,
Register scratch,
int save_at_depth,
Label* miss) {
// Make sure there's no overlap between scratch and the other
// registers.
ASSERT(!scratch.is(object_reg) && !scratch.is(holder_reg));
// Keep track of the current object in register reg.
Register reg = object_reg;
int depth = 0;
if (save_at_depth == depth) {
mov(Operand(esp, kPointerSize), object_reg);
}
// Check the maps in the prototype chain.
// Traverse the prototype chain from the object and do map checks.
while (object != holder) {
depth++;
// Only global objects and objects that do not require access
// checks are allowed in stubs.
ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
JSObject* prototype = JSObject::cast(object->GetPrototype());
if (Heap::InNewSpace(prototype)) {
// Get the map of the current object.
mov(scratch, FieldOperand(reg, HeapObject::kMapOffset));
cmp(Operand(scratch), Immediate(Handle<Map>(object->map())));
// Branch on the result of the map check.
j(not_equal, miss, not_taken);
// Check access rights to the global object. This has to happen
// after the map check so that we know that the object is
// actually a global object.
if (object->IsJSGlobalProxy()) {
CheckAccessGlobalProxy(reg, scratch, miss);
// Restore scratch register to be the map of the object.
// We load the prototype from the map in the scratch register.
mov(scratch, FieldOperand(reg, HeapObject::kMapOffset));
}
// The prototype is in new space; we cannot store a reference
// to it in the code. Load it from the map.
reg = holder_reg; // from now the object is in holder_reg
mov(reg, FieldOperand(scratch, Map::kPrototypeOffset));
} else {
// Check the map of the current object.
cmp(FieldOperand(reg, HeapObject::kMapOffset),
Immediate(Handle<Map>(object->map())));
// Branch on the result of the map check.
j(not_equal, miss, not_taken);
// Check access rights to the global object. This has to happen
// after the map check so that we know that the object is
// actually a global object.
if (object->IsJSGlobalProxy()) {
CheckAccessGlobalProxy(reg, scratch, miss);
}
// The prototype is in old space; load it directly.
reg = holder_reg; // from now the object is in holder_reg
mov(reg, Handle<JSObject>(prototype));
}
if (save_at_depth == depth) {
mov(Operand(esp, kPointerSize), reg);
}
// Go to the next object in the prototype chain.
object = prototype;
}
// Check the holder map.
cmp(FieldOperand(reg, HeapObject::kMapOffset),
Immediate(Handle<Map>(holder->map())));
j(not_equal, miss, not_taken);
// Log the check depth.
LOG(IntEvent("check-maps-depth", depth + 1));
// Perform security check for access to the global object and return
// the holder register.
ASSERT(object == holder);
ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
if (object->IsJSGlobalProxy()) {
CheckAccessGlobalProxy(reg, scratch, miss);
}
return reg;
}
void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
Register scratch,
Label* miss) {
......
......@@ -244,24 +244,6 @@ class MacroAssembler: public Assembler {
// ---------------------------------------------------------------------------
// Inline caching support
// Generates code that verifies that the maps of objects in the
// prototype chain of object hasn't changed since the code was
// generated and branches to the miss label if any map has. If
// necessary the function also generates code for security check
// in case of global object holders. The scratch and holder
// registers are always clobbered, but the object register is only
// clobbered if it the same as the holder register. The function
// returns a register containing the holder - either object_reg or
// holder_reg.
// The function can optionally (when save_at_depth !=
// kInvalidProtoDepth) save the object at the given depth by moving
// it to [esp + kPointerSize].
Register CheckMaps(JSObject* object, Register object_reg,
JSObject* holder, Register holder_reg,
Register scratch,
int save_at_depth,
Label* miss);
// Generate code for checking access rights - used for security checks
// on access to global objects across environments. The holder register
// is left untouched, but the scratch register is clobbered.
......
This diff is collapsed.
......@@ -80,11 +80,38 @@ void IC::SetTargetAtAddress(Address address, Code* target) {
}
Map* IC::GetCodeCacheMapForObject(Object* object) {
if (object->IsJSObject()) return JSObject::cast(object)->map();
InlineCacheHolderFlag IC::GetCodeCacheForObject(Object* object,
JSObject* holder) {
if (object->IsJSObject()) {
return GetCodeCacheForObject(JSObject::cast(object), holder);
}
// If the object is a value, we use the prototype map for the cache.
ASSERT(object->IsString() || object->IsNumber() || object->IsBoolean());
return JSObject::cast(object->GetPrototype())->map();
return PROTOTYPE_MAP;
}
InlineCacheHolderFlag IC::GetCodeCacheForObject(JSObject* object,
JSObject* holder) {
// Fast-properties and global objects store stubs in their own maps.
// Slow properties objects use prototype's map (unless the property is its own
// when holder == object). It works because slow properties objects having
// the same prototype (or a prototype with the same map) and not having
// the property are interchangeable for such a stub.
if (holder != object &&
!object->HasFastProperties() &&
!object->IsJSGlobalProxy() &&
!object->IsJSGlobalObject()) {
return PROTOTYPE_MAP;
}
return OWN_MAP;
}
Map* IC::GetCodeCacheMap(Object* object, InlineCacheHolderFlag holder) {
Object* map_owner = (holder == OWN_MAP ? object : object->GetPrototype());
ASSERT(map_owner->IsJSObject());
return JSObject::cast(map_owner)->map();
}
......
......@@ -134,13 +134,45 @@ Address IC::OriginalCodeAddress() {
}
#endif
static bool HasNormalObjectsInPrototypeChain(LookupResult* lookup,
Object* receiver) {
Object* end = lookup->IsProperty() ? lookup->holder() : Heap::null_value();
for (Object* current = receiver;
current != end;
current = current->GetPrototype()) {
if (current->IsJSObject() &&
!JSObject::cast(current)->HasFastProperties() &&
!current->IsJSGlobalProxy() &&
!current->IsJSGlobalObject()) {
return true;
}
}
return false;
}
IC::State IC::StateFrom(Code* target, Object* receiver, Object* name) {
IC::State state = target->ic_state();
if (state != MONOMORPHIC) return state;
if (receiver->IsUndefined() || receiver->IsNull()) return state;
Map* map = GetCodeCacheMapForObject(receiver);
InlineCacheHolderFlag cache_holder =
Code::ExtractCacheHolderFromFlags(target->flags());
if (cache_holder == OWN_MAP && !receiver->IsJSObject()) {
// The stub was generated for JSObject but called for non-JSObject.
// IC::GetCodeCacheMap is not applicable.
return MONOMORPHIC;
} else if (cache_holder == PROTOTYPE_MAP &&
receiver->GetPrototype()->IsNull()) {
// IC::GetCodeCacheMap is not applicable.
return MONOMORPHIC;
}
Map* map = IC::GetCodeCacheMap(receiver, cache_holder);
// Decide whether the inline cache failed because of changes to the
// receiver itself or changes to one of its prototypes.
......@@ -487,12 +519,24 @@ Object* CallICBase::LoadFunction(State state,
void CallICBase::UpdateCaches(LookupResult* lookup,
State state,
Handle<Object> object,
Handle<String> name) {
State state,
Handle<Object> object,
Handle<String> name) {
// Bail out if we didn't find a result.
if (!lookup->IsProperty() || !lookup->IsCacheable()) return;
#ifndef V8_TARGET_ARCH_IA32
// Normal objects only implemented for IA32 by now.
if (HasNormalObjectsInPrototypeChain(lookup, *object)) return;
#else
if (lookup->holder() != *object &&
HasNormalObjectsInPrototypeChain(lookup, object->GetPrototype())) {
// Suppress optimization for prototype chains with slow properties objects
// in the middle.
return;
}
#endif
// Compute the number of arguments.
int argc = target()->arguments_count();
InLoopFlag in_loop = target()->ic_in_loop();
......@@ -590,8 +634,13 @@ void CallICBase::UpdateCaches(LookupResult* lookup,
state == MONOMORPHIC_PROTOTYPE_FAILURE) {
set_target(Code::cast(code));
} else if (state == MEGAMORPHIC) {
// Cache code holding map should be consistent with
// GenerateMonomorphicCacheProbe. It is not the map which holds the stub.
Map* map = JSObject::cast(object->IsJSObject() ? *object :
object->GetPrototype())->map();
// Update the stub cache.
StubCache::Set(*name, GetCodeCacheMapForObject(*object), Code::cast(code));
StubCache::Set(*name, map, Code::cast(code));
}
#ifdef DEBUG
......@@ -795,6 +844,8 @@ void LoadIC::UpdateCaches(LookupResult* lookup,
if (!object->IsJSObject()) return;
Handle<JSObject> receiver = Handle<JSObject>::cast(object);
if (HasNormalObjectsInPrototypeChain(lookup, *object)) return;
// Compute the code stub for this load.
Object* code = NULL;
if (state == UNINITIALIZED) {
......@@ -871,8 +922,12 @@ void LoadIC::UpdateCaches(LookupResult* lookup,
} else if (state == MONOMORPHIC) {
set_target(megamorphic_stub());
} else if (state == MEGAMORPHIC) {
// Update the stub cache.
StubCache::Set(*name, GetCodeCacheMapForObject(*object), Code::cast(code));
// Cache code holding map should be consistent with
// GenerateMonomorphicCacheProbe.
Map* map = JSObject::cast(object->IsJSObject() ? *object :
object->GetPrototype())->map();
StubCache::Set(*name, map, Code::cast(code));
}
#ifdef DEBUG
......@@ -1018,6 +1073,8 @@ void KeyedLoadIC::UpdateCaches(LookupResult* lookup, State state,
if (!object->IsJSObject()) return;
Handle<JSObject> receiver = Handle<JSObject>::cast(object);
if (HasNormalObjectsInPrototypeChain(lookup, *object)) return;
// Compute the code stub for this load.
Object* code = NULL;
......
......@@ -117,9 +117,14 @@ class IC {
return ComputeMode() == RelocInfo::CODE_TARGET_CONTEXT;
}
// Returns the map to use for caching stubs for a given object.
// This method should not be called with undefined or null.
static inline Map* GetCodeCacheMapForObject(Object* object);
// Determines which map must be used for keeping the code stub.
// These methods should not be called with undefined or null.
static inline InlineCacheHolderFlag GetCodeCacheForObject(Object* object,
JSObject* holder);
static inline InlineCacheHolderFlag GetCodeCacheForObject(JSObject* object,
JSObject* holder);
static inline Map* GetCodeCacheMap(Object* object,
InlineCacheHolderFlag holder);
protected:
Address fp() const { return fp_; }
......
......@@ -2273,13 +2273,15 @@ Code::Flags Code::ComputeFlags(Kind kind,
InLoopFlag in_loop,
InlineCacheState ic_state,
PropertyType type,
int argc) {
int argc,
InlineCacheHolderFlag holder) {
// Compute the bit mask.
int bits = kind << kFlagsKindShift;
if (in_loop) bits |= kFlagsICInLoopMask;
bits |= ic_state << kFlagsICStateShift;
bits |= type << kFlagsTypeShift;
bits |= argc << kFlagsArgumentsCountShift;
if (holder == PROTOTYPE_MAP) bits |= kFlagsCacheInPrototypeMapMask;
// Cast to flags and validate result before returning it.
Flags result = static_cast<Flags>(bits);
ASSERT(ExtractKindFromFlags(result) == kind);
......@@ -2293,9 +2295,10 @@ Code::Flags Code::ComputeFlags(Kind kind,
Code::Flags Code::ComputeMonomorphicFlags(Kind kind,
PropertyType type,
InlineCacheHolderFlag holder,
InLoopFlag in_loop,
int argc) {
return ComputeFlags(kind, in_loop, MONOMORPHIC, type, argc);
return ComputeFlags(kind, in_loop, MONOMORPHIC, type, argc, holder);
}
......@@ -2328,6 +2331,12 @@ int Code::ExtractArgumentsCountFromFlags(Flags flags) {
}
InlineCacheHolderFlag Code::ExtractCacheHolderFromFlags(Flags flags) {
int bits = (flags & kFlagsCacheInPrototypeMapMask);
return bits != 0 ? PROTOTYPE_MAP : OWN_MAP;
}
Code::Flags Code::RemoveTypeFromFlags(Flags flags) {
int bits = flags & ~kFlagsTypeMask;
return static_cast<Flags>(bits);
......
......@@ -1746,8 +1746,6 @@ void JSObject::LocalLookupRealNamedProperty(String* name,
result->DictionaryResult(this, entry);
return;
}
// Slow case object skipped during lookup. Do not use inline caching.
if (!IsGlobalObject()) result->DisallowCaching();
}
result->NotFound();
}
......
......@@ -2769,11 +2769,13 @@ class Code: public HeapObject {
InLoopFlag in_loop = NOT_IN_LOOP,
InlineCacheState ic_state = UNINITIALIZED,
PropertyType type = NORMAL,
int argc = -1);
int argc = -1,
InlineCacheHolderFlag holder = OWN_MAP);
static inline Flags ComputeMonomorphicFlags(
Kind kind,
PropertyType type,
InlineCacheHolderFlag holder = OWN_MAP,
InLoopFlag in_loop = NOT_IN_LOOP,
int argc = -1);
......@@ -2782,6 +2784,7 @@ class Code: public HeapObject {
static inline InLoopFlag ExtractICInLoopFromFlags(Flags flags);
static inline PropertyType ExtractTypeFromFlags(Flags flags);
static inline int ExtractArgumentsCountFromFlags(Flags flags);
static inline InlineCacheHolderFlag ExtractCacheHolderFromFlags(Flags flags);
static inline Flags RemoveTypeFromFlags(Flags flags);
// Convert a target address into a code object.
......@@ -2868,16 +2871,18 @@ class Code: public HeapObject {
static const int kFlagsICInLoopShift = 3;
static const int kFlagsTypeShift = 4;
static const int kFlagsKindShift = 7;
static const int kFlagsArgumentsCountShift = 11;
static const int kFlagsICHolderShift = 11;
static const int kFlagsArgumentsCountShift = 12;
static const int kFlagsICStateMask = 0x00000007; // 00000000111
static const int kFlagsICInLoopMask = 0x00000008; // 00000001000
static const int kFlagsTypeMask = 0x00000070; // 00001110000
static const int kFlagsKindMask = 0x00000780; // 11110000000
static const int kFlagsArgumentsCountMask = 0xFFFFF800;
static const int kFlagsCacheInPrototypeMapMask = 0x00000800;
static const int kFlagsArgumentsCountMask = 0xFFFFF000;
static const int kFlagsNotUsedInLookup =
(kFlagsICInLoopMask | kFlagsTypeMask);
(kFlagsICInLoopMask | kFlagsTypeMask | kFlagsCacheInPrototypeMapMask);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Code);
......
This diff is collapsed.
......@@ -409,8 +409,21 @@ class StubCompiler BASE_EMBEDDED {
static void GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind);
// Check the integrity of the prototype chain to make sure that the
// current IC is still valid.
// Generates code that verifies that the property holder has not changed
// (checking maps of objects in the prototype chain for fast and global
// objects or doing negative lookup for slow objects, ensures that the
// property cells for global objects are still empty) and checks that the map
// of the holder has not changed. If necessary the function also generates
// code for security check in case of global object holders. Helps to make
// sure that the current IC is still valid.
//
// The scratch and holder registers are always clobbered, but the object
// register is only clobbered if it the same as the holder register. The
// function returns a register containing the holder - either object_reg or
// holder_reg.
// The function can optionally (when save_at_depth !=
// kInvalidProtoDepth) save the object at the given depth by moving
// it to [esp + kPointerSize].
Register CheckPrototypes(JSObject* object,
Register object_reg,
......@@ -418,9 +431,10 @@ class StubCompiler BASE_EMBEDDED {
Register holder_reg,
Register scratch,
String* name,
Label* miss) {
Label* miss,
Register extra = no_reg) {
return CheckPrototypes(object, object_reg, holder, holder_reg, scratch,
name, kInvalidProtoDepth, miss);
name, kInvalidProtoDepth, miss, extra);
}
Register CheckPrototypes(JSObject* object,
......@@ -430,7 +444,8 @@ class StubCompiler BASE_EMBEDDED {
Register scratch,
String* name,
int save_at_depth,
Label* miss);
Label* miss,
Register extra = no_reg);
protected:
Object* GetCodeWithFlags(Code::Flags flags, const char* name);
......@@ -613,8 +628,10 @@ class CallStubCompiler: public StubCompiler {
kNumCallGenerators
};
CallStubCompiler(int argc, InLoopFlag in_loop, Code::Kind kind)
: arguments_(argc), in_loop_(in_loop), kind_(kind) { }
CallStubCompiler(int argc,
InLoopFlag in_loop,
Code::Kind kind,
InlineCacheHolderFlag cache_holder);
Object* CompileCallField(JSObject* object,
JSObject* holder,
......@@ -655,6 +672,7 @@ class CallStubCompiler: public StubCompiler {
const ParameterCount arguments_;
const InLoopFlag in_loop_;
const Code::Kind kind_;
const InlineCacheHolderFlag cache_holder_;
const ParameterCount& arguments() { return arguments_; }
......
......@@ -168,6 +168,8 @@ namespace internal {
SC(constructed_objects, V8.ConstructedObjects) \
SC(constructed_objects_runtime, V8.ConstructedObjectsRuntime) \
SC(constructed_objects_stub, V8.ConstructedObjectsStub) \
SC(negative_lookups, V8.NegativeLookups) \
SC(negative_lookups_miss, V8.NegativeLookupsMiss) \
SC(array_function_runtime, V8.ArrayFunctionRuntime) \
SC(array_function_native, V8.ArrayFunctionNative) \
SC(for_in, V8.ForIn) \
......
......@@ -2125,7 +2125,8 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
Register scratch,
String* name,
int save_at_depth,
Label* miss) {
Label* miss,
Register extra) {
// Check that the maps haven't changed.
Register result =
masm()->CheckMaps(object,
......
// Copyright 2010 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.
function Hash() {
for (var i = 0; i < 100; i++) {
this['a' + i] = i;
}
delete this.a50; // Ensure it's a normal object.
}
Hash.prototype.m = function() {
return 1;
};
var h = new Hash();
for (var i = 1; i < 100; i++) {
if (i == 50) {
h.m = function() {
return 2;
};
} else if (i == 70) {
delete h.m;
}
assertEquals(i < 50 || i >= 70 ? 1 : 2, h.m());
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment