Commit 4b6981f7 authored by antonm@chromium.org's avatar antonm@chromium.org

Do not use possibly stale values for cache size, etc.

Those value can become invalid if cache gets cleared by GC.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6353 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent d2df943b
...@@ -670,16 +670,17 @@ void JSFunctionResultCache::JSFunctionResultCacheVerify() { ...@@ -670,16 +670,17 @@ void JSFunctionResultCache::JSFunctionResultCacheVerify() {
int finger = Smi::cast(get(kFingerIndex))->value(); int finger = Smi::cast(get(kFingerIndex))->value();
ASSERT(kEntriesIndex <= finger); ASSERT(kEntriesIndex <= finger);
ASSERT(finger < size || finger == kEntriesIndex); ASSERT((finger < size) || (finger == kEntriesIndex && finger == size));
ASSERT_EQ(0, finger % kEntrySize); ASSERT_EQ(0, finger % kEntrySize);
if (FLAG_enable_slow_asserts) { if (FLAG_enable_slow_asserts) {
STATIC_ASSERT(2 == kEntrySize); for (int i = kEntriesIndex; i < size; i++) {
for (int i = kEntriesIndex; i < length(); i += kEntrySize) { ASSERT(!get(i)->IsTheHole());
get(i)->Verify();
}
for (int i = size; i < length(); i++) {
ASSERT(get(i)->IsTheHole());
get(i)->Verify(); get(i)->Verify();
get(i + 1)->Verify();
// Key and value must be either both the holes, or not.
ASSERT(get(i)->IsTheHole() == get(i + 1)->IsTheHole());
} }
} }
} }
......
...@@ -1978,13 +1978,13 @@ void ExternalTwoByteString::set_resource( ...@@ -1978,13 +1978,13 @@ void ExternalTwoByteString::set_resource(
void JSFunctionResultCache::MakeZeroSize() { void JSFunctionResultCache::MakeZeroSize() {
set(kFingerIndex, Smi::FromInt(kEntriesIndex)); set_finger_index(kEntriesIndex);
set(kCacheSizeIndex, Smi::FromInt(kEntriesIndex)); set_size(kEntriesIndex);
} }
void JSFunctionResultCache::Clear() { void JSFunctionResultCache::Clear() {
int cache_size = Smi::cast(get(kCacheSizeIndex))->value(); int cache_size = size();
Object** entries_start = RawField(this, OffsetOfElementAt(kEntriesIndex)); Object** entries_start = RawField(this, OffsetOfElementAt(kEntriesIndex));
MemsetPointer(entries_start, MemsetPointer(entries_start,
Heap::the_hole_value(), Heap::the_hole_value(),
...@@ -1993,6 +1993,26 @@ void JSFunctionResultCache::Clear() { ...@@ -1993,6 +1993,26 @@ void JSFunctionResultCache::Clear() {
} }
int JSFunctionResultCache::size() {
return Smi::cast(get(kCacheSizeIndex))->value();
}
void JSFunctionResultCache::set_size(int size) {
set(kCacheSizeIndex, Smi::FromInt(size));
}
int JSFunctionResultCache::finger_index() {
return Smi::cast(get(kFingerIndex))->value();
}
void JSFunctionResultCache::set_finger_index(int finger_index) {
set(kFingerIndex, Smi::FromInt(finger_index));
}
byte ByteArray::get(int index) { byte ByteArray::get(int index) {
ASSERT(index >= 0 && index < this->length()); ASSERT(index >= 0 && index < this->length());
return READ_BYTE_FIELD(this, kHeaderSize + index * kCharSize); return READ_BYTE_FIELD(this, kHeaderSize + index * kCharSize);
......
...@@ -2613,6 +2613,11 @@ class JSFunctionResultCache: public FixedArray { ...@@ -2613,6 +2613,11 @@ class JSFunctionResultCache: public FixedArray {
inline void MakeZeroSize(); inline void MakeZeroSize();
inline void Clear(); inline void Clear();
inline int size();
inline void set_size(int size);
inline int finger_index();
inline void set_finger_index(int finger_index);
// Casting // Casting
static inline JSFunctionResultCache* cast(Object* obj); static inline JSFunctionResultCache* cast(Object* obj);
......
...@@ -10654,51 +10654,12 @@ static MaybeObject* Runtime_Abort(Arguments args) { ...@@ -10654,51 +10654,12 @@ static MaybeObject* Runtime_Abort(Arguments args) {
} }
MUST_USE_RESULT static MaybeObject* CacheMiss(FixedArray* cache_obj,
int index,
Object* key_obj) {
ASSERT(index % 2 == 0); // index of the key
ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
ASSERT(index < cache_obj->length());
HandleScope scope;
Handle<FixedArray> cache(cache_obj);
Handle<Object> key(key_obj);
Handle<JSFunction> factory(JSFunction::cast(
cache->get(JSFunctionResultCache::kFactoryIndex)));
// TODO(antonm): consider passing a receiver when constructing a cache.
Handle<Object> receiver(Top::global_context()->global());
Handle<Object> value;
{
// This handle is nor shared, nor used later, so it's safe.
Object** argv[] = { key.location() };
bool pending_exception = false;
value = Execution::Call(factory,
receiver,
1,
argv,
&pending_exception);
if (pending_exception) return Failure::Exception();
}
cache->set(index, *key);
cache->set(index + 1, *value);
cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
return *value;
}
static MaybeObject* Runtime_GetFromCache(Arguments args) { static MaybeObject* Runtime_GetFromCache(Arguments args) {
// This is only called from codegen, so checks might be more lax. // This is only called from codegen, so checks might be more lax.
CONVERT_CHECKED(FixedArray, cache, args[0]); CONVERT_CHECKED(JSFunctionResultCache, cache, args[0]);
Object* key = args[1]; Object* key = args[1];
const int finger_index = int finger_index = cache->finger_index();
Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
Object* o = cache->get(finger_index); Object* o = cache->get(finger_index);
if (o == key) { if (o == key) {
// The fastest case: hit the same place again. // The fastest case: hit the same place again.
...@@ -10710,35 +10671,78 @@ static MaybeObject* Runtime_GetFromCache(Arguments args) { ...@@ -10710,35 +10671,78 @@ static MaybeObject* Runtime_GetFromCache(Arguments args) {
i -= 2) { i -= 2) {
o = cache->get(i); o = cache->get(i);
if (o == key) { if (o == key) {
cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i)); cache->set_finger_index(i);
return cache->get(i + 1); return cache->get(i + 1);
} }
} }
const int size = int size = cache->size();
Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
ASSERT(size <= cache->length()); ASSERT(size <= cache->length());
for (int i = size - 2; i > finger_index; i -= 2) { for (int i = size - 2; i > finger_index; i -= 2) {
o = cache->get(i); o = cache->get(i);
if (o == key) { if (o == key) {
cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i)); cache->set_finger_index(i);
return cache->get(i + 1); return cache->get(i + 1);
} }
} }
// Cache miss. If we have spare room, put new data into it, otherwise // There is no value in the cache. Invoke the function and cache result.
// evict post finger entry which must be least recently used. HandleScope scope;
if (size < cache->length()) {
cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2)); Handle<JSFunctionResultCache> cache_handle(cache);
return CacheMiss(cache, size, key); Handle<Object> key_handle(key);
Handle<Object> value;
{
Handle<JSFunction> factory(JSFunction::cast(
cache_handle->get(JSFunctionResultCache::kFactoryIndex)));
// TODO(antonm): consider passing a receiver when constructing a cache.
Handle<Object> receiver(Top::global_context()->global());
// This handle is nor shared, nor used later, so it's safe.
Object** argv[] = { key_handle.location() };
bool pending_exception = false;
value = Execution::Call(factory,
receiver,
1,
argv,
&pending_exception);
if (pending_exception) return Failure::Exception();
}
#ifdef DEBUG
cache_handle->JSFunctionResultCacheVerify();
#endif
// Function invocation may have cleared the cache. Reread all the data.
finger_index = cache_handle->finger_index();
size = cache_handle->size();
// If we have spare room, put new data into it, otherwise evict post finger
// entry which is likely to be the least recently used.
int index = -1;
if (size < cache_handle->length()) {
cache_handle->set_size(size + JSFunctionResultCache::kEntrySize);
index = size;
} else { } else {
int target_index = finger_index + JSFunctionResultCache::kEntrySize; index = finger_index + JSFunctionResultCache::kEntrySize;
if (target_index == cache->length()) { if (index == cache_handle->length()) {
target_index = JSFunctionResultCache::kEntriesIndex; index = JSFunctionResultCache::kEntriesIndex;
} }
return CacheMiss(cache, target_index, key);
} }
ASSERT(index % 2 == 0);
ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
ASSERT(index < cache_handle->length());
cache_handle->set(index, *key_handle);
cache_handle->set(index + 1, *value);
cache_handle->set_finger_index(index);
#ifdef DEBUG
cache_handle->JSFunctionResultCacheVerify();
#endif
return *value;
} }
#ifdef DEBUG #ifdef DEBUG
......
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