Per-Isolate cache for polymorphic stubs

BUG=1385
TEST=Existing tests still pass; running d8 with --dump-counters shows fewer polymorphic stubs being compiled

Review URL: http://codereview.chromium.org/7094003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8183 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 6a81642f
......@@ -3703,7 +3703,7 @@ class Internals {
static const int kFullStringRepresentationMask = 0x07;
static const int kExternalTwoByteRepresentationTag = 0x02;
static const int kJSObjectType = 0xa1;
static const int kJSObjectType = 0xa2;
static const int kFirstNonstringType = 0x80;
static const int kForeignType = 0x85;
......
......@@ -877,6 +877,9 @@ void Heap::MarkCompactPrologue(bool is_compacting) {
CompletelyClearInstanceofCache();
if (is_compacting) FlushNumberStringCache();
if (FLAG_cleanup_code_caches_at_gc) {
polymorphic_code_cache()->set_cache(undefined_value());
}
ClearNormalizedMapCaches();
}
......@@ -1661,6 +1664,11 @@ MaybeObject* Heap::AllocateCodeCache() {
}
MaybeObject* Heap::AllocatePolymorphicCodeCache() {
return AllocateStruct(POLYMORPHIC_CODE_CACHE_TYPE);
}
const Heap::StringTypeTable Heap::string_type_table[] = {
#define STRING_TYPE_ELEMENT(type, size, name, camel_name) \
{type, size, k##camel_name##MapRootIndex},
......@@ -2168,6 +2176,11 @@ bool Heap::CreateInitialObjects() {
}
set_non_monomorphic_cache(NumberDictionary::cast(obj));
{ MaybeObject* maybe_obj = AllocatePolymorphicCodeCache();
if (!maybe_obj->ToObject(&obj)) return false;
}
set_polymorphic_code_cache(PolymorphicCodeCache::cast(obj));
set_instanceof_cache_function(Smi::FromInt(0));
set_instanceof_cache_map(Smi::FromInt(0));
set_instanceof_cache_answer(Smi::FromInt(0));
......
......@@ -120,6 +120,7 @@ inline Heap* _inline_get_heap_();
V(Foreign, prototype_accessors, PrototypeAccessors) \
V(NumberDictionary, code_stubs, CodeStubs) \
V(NumberDictionary, non_monomorphic_cache, NonMonomorphicCache) \
V(PolymorphicCodeCache, polymorphic_code_cache, PolymorphicCodeCache) \
V(Code, js_entry_code, JsEntryCode) \
V(Code, js_construct_entry_code, JsConstructEntryCode) \
V(FixedArray, natives_source_cache, NativesSourceCache) \
......@@ -477,6 +478,9 @@ class Heap {
// Allocates an empty code cache.
MUST_USE_RESULT MaybeObject* AllocateCodeCache();
// Allocates an empty PolymorphicCodeCache.
MUST_USE_RESULT MaybeObject* AllocatePolymorphicCodeCache();
// Clear the Instanceof cache (used when a prototype changes).
inline void ClearInstanceofCache();
......
......@@ -1636,79 +1636,40 @@ MaybeObject* KeyedIC::ComputeStub(JSObject* receiver,
return generic_stub;
}
// TODO(1385): Currently MEGAMORPHIC stubs are cached in the receiver map stub
// cache, but that can put receiver types together from unrelated call sites
// into the same stub--they always handle the union of all receiver maps seen
// at all call sites involving the receiver map. This is only an
// approximation: ideally, there would be a global cache that mapped sets of
// receiver maps to MEGAMORPHIC stubs. The complexity of the MEGAMORPHIC stub
// computation also leads to direct manipulation of the stub cache from the IC
// code, which the global cache solution would avoid.
Code::Kind kind = this->kind();
Code::Flags flags = Code::ComputeFlags(kind,
NOT_IN_LOOP,
MEGAMORPHIC,
strict_mode);
String* megamorphic_name = GetStubNameForCache(MEGAMORPHIC);
Object* maybe_cached_stub = receiver->map()->FindInCodeCache(megamorphic_name,
flags);
// Create a set of all receiver maps that have been seen at the IC call site
// and those seen by the MEGAMORPHIC cached stub, if that's the stub that's
// been selected.
MapList receiver_maps;
if (!maybe_cached_stub->IsUndefined()) {
GetReceiverMapsForStub(Code::cast(maybe_cached_stub), &receiver_maps);
}
bool added_map = false;
for (int i = 0; i < target_receiver_maps.length(); ++i) {
if (AddOneReceiverMapIfMissing(&receiver_maps,
target_receiver_maps.at(i))) {
added_map = true;
}
}
ASSERT(receiver_maps.length() > 0);
// If the maximum number of receiver maps has been exceeded, use the Generic
// If the maximum number of receiver maps has been exceeded, use the generic
// version of the IC.
if (receiver_maps.length() > KeyedIC::kMaxKeyedPolymorphism) {
if (target_receiver_maps.length() > KeyedIC::kMaxKeyedPolymorphism) {
return generic_stub;
}
// If no maps have been seen at the call site that aren't in the cached
// stub, then use it.
if (!added_map) {
ASSERT(!maybe_cached_stub->IsUndefined());
PolymorphicCodeCache* cache = isolate()->heap()->polymorphic_code_cache();
Code::Flags flags = Code::ComputeFlags(this->kind(),
NOT_IN_LOOP,
MEGAMORPHIC,
strict_mode);
Object* maybe_cached_stub = cache->Lookup(&target_receiver_maps, flags);
// If there is a cached stub, use it.
if (!maybe_cached_stub->IsUndefined()) {
ASSERT(maybe_cached_stub->IsCode());
return Code::cast(maybe_cached_stub);
}
// Lookup all of the receiver maps in the cache, they should all already
// have MONOMORPHIC stubs.
CodeList handler_ics(KeyedIC::kMaxKeyedPolymorphism);
for (int current = 0; current < receiver_maps.length(); ++current) {
Map* receiver_map(receiver_maps.at(current));
// Collect MONOMORPHIC stubs for all target_receiver_maps.
CodeList handler_ics(target_receiver_maps.length());
for (int i = 0; i < target_receiver_maps.length(); ++i) {
Map* receiver_map(target_receiver_maps.at(i));
MaybeObject* maybe_cached_stub = ComputeMonomorphicStubWithoutMapCheck(
receiver_map,
strict_mode,
generic_stub);
receiver_map, strict_mode, generic_stub);
Code* cached_stub;
if (!maybe_cached_stub->To(&cached_stub)) {
return maybe_cached_stub;
}
if (!maybe_cached_stub->To(&cached_stub)) return maybe_cached_stub;
handler_ics.Add(cached_stub);
}
Code* stub;
// Build the MEGAMORPHIC stub.
maybe_stub = ConstructMegamorphicStub(&receiver_maps,
Code* stub;
maybe_stub = ConstructMegamorphicStub(&target_receiver_maps,
&handler_ics,
strict_mode);
if (!maybe_stub->To(&stub)) return maybe_stub;
MaybeObject* maybe_update = receiver->UpdateMapCodeCache(
megamorphic_name,
stub);
MaybeObject* maybe_update = cache->Update(&target_receiver_maps, flags, stub);
if (maybe_update->IsFailure()) return maybe_update;
return stub;
}
......
......@@ -289,6 +289,12 @@ void CodeCache::CodeCacheVerify() {
}
void PolymorphicCodeCache::PolymorphicCodeCacheVerify() {
VerifyHeapPointer(cache());
ASSERT(cache()->IsUndefined() || cache()->IsPolymorphicCodeCacheHashTable());
}
void FixedArray::FixedArrayVerify() {
for (int i = 0; i < length(); i++) {
Object* e = get(i);
......
......@@ -688,6 +688,11 @@ bool Object::IsCodeCacheHashTable() {
}
bool Object::IsPolymorphicCodeCacheHashTable() {
return IsHashTable();
}
bool Object::IsMapCache() {
return IsHashTable();
}
......@@ -1903,6 +1908,7 @@ CAST_ACCESSOR(JSFunctionResultCache)
CAST_ACCESSOR(NormalizedMapCache)
CAST_ACCESSOR(CompilationCacheTable)
CAST_ACCESSOR(CodeCacheHashTable)
CAST_ACCESSOR(PolymorphicCodeCacheHashTable)
CAST_ACCESSOR(MapCache)
CAST_ACCESSOR(String)
CAST_ACCESSOR(SeqString)
......@@ -3338,6 +3344,8 @@ void SharedFunctionInfo::set_native(bool value) {
ACCESSORS(CodeCache, default_cache, FixedArray, kDefaultCacheOffset)
ACCESSORS(CodeCache, normal_type_cache, Object, kNormalTypeCacheOffset)
ACCESSORS(PolymorphicCodeCache, cache, Object, kCacheOffset)
bool Script::HasValidSource() {
Object* src = this->source();
if (!src->IsString()) return true;
......
......@@ -472,6 +472,13 @@ void CodeCache::CodeCachePrint(FILE* out) {
}
void PolymorphicCodeCache::PolymorphicCodeCachePrint(FILE* out) {
HeapObject::PrintHeader(out, "PolymorphicCodeCache");
PrintF(out, "\n - cache: ");
cache()->ShortPrint(out);
}
void FixedArray::FixedArrayPrint(FILE* out) {
HeapObject::PrintHeader(out, "FixedArray");
PrintF(out, " - length: %d", length());
......
This diff is collapsed.
......@@ -316,6 +316,7 @@ static const int kVariableSizeSentinel = 0;
V(TYPE_SWITCH_INFO_TYPE) \
V(SCRIPT_TYPE) \
V(CODE_CACHE_TYPE) \
V(POLYMORPHIC_CODE_CACHE_TYPE) \
\
V(FIXED_ARRAY_TYPE) \
V(SHARED_FUNCTION_INFO_TYPE) \
......@@ -427,7 +428,8 @@ static const int kVariableSizeSentinel = 0;
V(SIGNATURE_INFO, SignatureInfo, signature_info) \
V(TYPE_SWITCH_INFO, TypeSwitchInfo, type_switch_info) \
V(SCRIPT, Script, script) \
V(CODE_CACHE, CodeCache, code_cache)
V(CODE_CACHE, CodeCache, code_cache) \
V(POLYMORPHIC_CODE_CACHE, PolymorphicCodeCache, polymorphic_code_cache)
#ifdef ENABLE_DEBUGGER_SUPPORT
#define STRUCT_LIST_DEBUGGER(V) \
......@@ -544,6 +546,7 @@ enum InstanceType {
TYPE_SWITCH_INFO_TYPE,
SCRIPT_TYPE,
CODE_CACHE_TYPE,
POLYMORPHIC_CODE_CACHE_TYPE,
// The following two instance types are only used when ENABLE_DEBUGGER_SUPPORT
// is defined. However as include/v8.h contain some of the instance type
// constants always having them avoids them getting different numbers
......@@ -752,6 +755,7 @@ class MaybeObject BASE_EMBEDDED {
V(NormalizedMapCache) \
V(CompilationCacheTable) \
V(CodeCacheHashTable) \
V(PolymorphicCodeCacheHashTable) \
V(MapCache) \
V(Primitive) \
V(GlobalObject) \
......@@ -2840,11 +2844,6 @@ class NormalizedMapCache: public FixedArray {
#ifdef DEBUG
void NormalizedMapCacheVerify();
#endif
private:
static int Hash(Map* fast);
static bool CheckHit(Map* slow, Map* fast, PropertyNormalizationMode mode);
};
......@@ -3964,6 +3963,21 @@ class Map: public HeapObject {
// following back pointers.
void ClearNonLiveTransitions(Heap* heap, Object* real_prototype);
// Computes a hash value for this map, to be used in HashTables and such.
int Hash();
// Compares this map to another to see if they describe equivalent objects.
// If |mode| is set to CLEAR_INOBJECT_PROPERTIES, |other| is treated as if
// it had exactly zero inobject properties.
// The "shared" flags of both this map and |other| are ignored.
bool EquivalentToForNormalization(Map* other, PropertyNormalizationMode mode);
// Returns true if this map and |other| describe equivalent objects.
// The "shared" flags of both this map and |other| are ignored.
bool EquivalentTo(Map* other) {
return EquivalentToForNormalization(other, KEEP_INOBJECT_PROPERTIES);
}
// Dispatched behavior.
#ifdef OBJECT_PRINT
inline void MapPrint() {
......@@ -5373,6 +5387,49 @@ class CodeCacheHashTable: public HashTable<CodeCacheHashTableShape,
};
class PolymorphicCodeCache: public Struct {
public:
DECL_ACCESSORS(cache, Object)
MUST_USE_RESULT MaybeObject* Update(MapList* maps,
Code::Flags flags,
Code* code);
Object* Lookup(MapList* maps, Code::Flags flags);
static inline PolymorphicCodeCache* cast(Object* obj);
#ifdef OBJECT_PRINT
inline void PolymorphicCodeCachePrint() {
PolymorphicCodeCachePrint(stdout);
}
void PolymorphicCodeCachePrint(FILE* out);
#endif
#ifdef DEBUG
void PolymorphicCodeCacheVerify();
#endif
static const int kCacheOffset = HeapObject::kHeaderSize;
static const int kSize = kCacheOffset + kPointerSize;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(PolymorphicCodeCache);
};
class PolymorphicCodeCacheHashTable
: public HashTable<CodeCacheHashTableShape, HashTableKey*> {
public:
Object* Lookup(MapList* maps, int code_kind);
MUST_USE_RESULT MaybeObject* Put(MapList* maps, int code_kind, Code* code);
static inline PolymorphicCodeCacheHashTable* cast(Object* obj);
static const int kInitialSize = 64;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(PolymorphicCodeCacheHashTable);
};
enum AllowNullsFlag {ALLOW_NULLS, DISALLOW_NULLS};
enum RobustnessFlag {ROBUST_STRING_TRAVERSAL, FAST_STRING_TRAVERSAL};
......
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