Implemented a ContextSlotCache for compiled code.
Review URL: http://codereview.chromium.org/141038 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2230 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
4f6a4a8339
commit
c078783c36
@ -149,7 +149,7 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags,
|
||||
// check parameter locals in context
|
||||
int param_index = ScopeInfo<>::ParameterIndex(*code, *name);
|
||||
if (param_index >= 0) {
|
||||
// slot found
|
||||
// slot found.
|
||||
int index =
|
||||
ScopeInfo<>::ContextSlotIndex(*code,
|
||||
Heap::arguments_shadow_symbol(),
|
||||
|
@ -501,6 +501,7 @@ void Heap::MarkCompactPrologue(bool is_compacting) {
|
||||
// At any old GC clear the keyed lookup cache to enable collection of unused
|
||||
// maps.
|
||||
KeyedLookupCache::Clear();
|
||||
ContextSlotCache::Clear();
|
||||
|
||||
CompilationCache::MarkCompactPrologue();
|
||||
|
||||
@ -1388,6 +1389,9 @@ bool Heap::CreateInitialObjects() {
|
||||
// Initialize keyed lookup cache.
|
||||
KeyedLookupCache::Clear();
|
||||
|
||||
// Initialize context slot cache.
|
||||
ContextSlotCache::Clear();
|
||||
|
||||
// Initialize compilation cache.
|
||||
CompilationCache::Clear();
|
||||
|
||||
@ -3548,8 +3552,10 @@ void KeyedLookupCache::Clear() {
|
||||
for (int index = 0; index < kLength; index++) keys_[index].map = NULL;
|
||||
}
|
||||
|
||||
|
||||
KeyedLookupCache::Key KeyedLookupCache::keys_[KeyedLookupCache::kLength];
|
||||
|
||||
|
||||
int KeyedLookupCache::field_offsets_[KeyedLookupCache::kLength];
|
||||
|
||||
|
||||
|
23
src/heap.h
23
src/heap.h
@ -444,17 +444,6 @@ class Heap : public AllStatic {
|
||||
// Allocates a new utility object in the old generation.
|
||||
static Object* AllocateStruct(InstanceType type);
|
||||
|
||||
|
||||
// Initializes a function with a shared part and prototype.
|
||||
// Returns the function.
|
||||
// Note: this code was factored out of AllocateFunction such that
|
||||
// other parts of the VM could use it. Specifically, a function that creates
|
||||
// instances of type JS_FUNCTION_TYPE benefit from the use of this function.
|
||||
// Please note this does not perform a garbage collection.
|
||||
static Object* InitializeFunction(JSFunction* function,
|
||||
SharedFunctionInfo* shared,
|
||||
Object* prototype);
|
||||
|
||||
// Allocates a function initialized with a shared part.
|
||||
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
|
||||
// failed.
|
||||
@ -984,7 +973,17 @@ class Heap : public AllStatic {
|
||||
static void ScavengeObjectSlow(HeapObject** p, HeapObject* object);
|
||||
|
||||
// Copy memory from src to dst.
|
||||
inline static void CopyBlock(Object** dst, Object** src, int byte_size);
|
||||
static inline void CopyBlock(Object** dst, Object** src, int byte_size);
|
||||
|
||||
// Initializes a function with a shared part and prototype.
|
||||
// Returns the function.
|
||||
// Note: this code was factored out of AllocateFunction such that
|
||||
// other parts of the VM could use it. Specifically, a function that creates
|
||||
// instances of type JS_FUNCTION_TYPE benefit from the use of this function.
|
||||
// Please note this does not perform a garbage collection.
|
||||
static inline Object* InitializeFunction(JSFunction* function,
|
||||
SharedFunctionInfo* shared,
|
||||
Object* prototype);
|
||||
|
||||
static const int kInitialSymbolTableSize = 2048;
|
||||
static const int kInitialEvalCacheSize = 64;
|
||||
|
@ -124,7 +124,8 @@ static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
|
||||
}
|
||||
}
|
||||
mode = copy->GetWriteBarrierMode();
|
||||
for (int i = 0; i < copy->map()->inobject_properties(); i++) {
|
||||
int nof = copy->map()->inobject_properties();
|
||||
for (int i = 0; i < nof; i++) {
|
||||
Object* value = copy->InObjectPropertyAt(i);
|
||||
if (value->IsJSObject()) {
|
||||
JSObject* jsObject = JSObject::cast(value);
|
||||
|
@ -432,10 +432,13 @@ int ScopeInfo<Allocator>::ContextSlotIndex(Code* code,
|
||||
String* name,
|
||||
Variable::Mode* mode) {
|
||||
ASSERT(name->IsSymbol());
|
||||
int result = ContextSlotCache::Lookup(code, name, mode);
|
||||
if (result != ContextSlotCache::kNotFound) return result;
|
||||
if (code->sinfo_size() > 0) {
|
||||
// Loop below depends on the NULL sentinel after the context slot names.
|
||||
ASSERT(NumberOfContextSlots(code) >= Context::MIN_CONTEXT_SLOTS ||
|
||||
*(ContextEntriesAddr(code) + 1) == NULL);
|
||||
|
||||
// slots start after length entry
|
||||
Object** p0 = ContextEntriesAddr(code) + 1;
|
||||
Object** p = p0;
|
||||
@ -443,14 +446,18 @@ int ScopeInfo<Allocator>::ContextSlotIndex(Code* code,
|
||||
while (*p != NULL) {
|
||||
if (*p == name) {
|
||||
ASSERT(((p - p0) & 1) == 0);
|
||||
if (mode != NULL) {
|
||||
ReadInt(p + 1, reinterpret_cast<int*>(mode));
|
||||
}
|
||||
return ((p - p0) >> 1) + Context::MIN_CONTEXT_SLOTS;
|
||||
int v;
|
||||
ReadInt(p + 1, &v);
|
||||
Variable::Mode mode_value = static_cast<Variable::Mode>(v);
|
||||
if (mode != NULL) *mode = mode_value;
|
||||
result = ((p - p0) >> 1) + Context::MIN_CONTEXT_SLOTS;
|
||||
ContextSlotCache::Update(code, name, mode_value, result);
|
||||
return result;
|
||||
}
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
ContextSlotCache::Update(code, name, Variable::INTERNAL, -1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -526,7 +533,78 @@ int ScopeInfo<Allocator>::NumberOfLocals() const {
|
||||
}
|
||||
|
||||
|
||||
int ContextSlotCache::Hash(Code* code, String* name) {
|
||||
// Uses only lower 32 bits if pointers are larger.
|
||||
uintptr_t addr_hash =
|
||||
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(code)) >> 2;
|
||||
return (addr_hash ^ name->Hash()) % kLength;
|
||||
}
|
||||
|
||||
|
||||
int ContextSlotCache::Lookup(Code* code,
|
||||
String* name,
|
||||
Variable::Mode* mode) {
|
||||
int index = Hash(code, name);
|
||||
Key& key = keys_[index];
|
||||
if ((key.code == code) && key.name->Equals(name)) {
|
||||
Value result(values_[index]);
|
||||
if (mode != NULL) *mode = result.mode();
|
||||
return result.index() + kNotFound;
|
||||
}
|
||||
return kNotFound;
|
||||
}
|
||||
|
||||
|
||||
void ContextSlotCache::Update(Code* code,
|
||||
String* name,
|
||||
Variable::Mode mode,
|
||||
int slot_index) {
|
||||
String* symbol;
|
||||
ASSERT(slot_index > kNotFound);
|
||||
if (Heap::LookupSymbolIfExists(name, &symbol)) {
|
||||
int index = Hash(code, symbol);
|
||||
Key& key = keys_[index];
|
||||
key.code = code;
|
||||
key.name = symbol;
|
||||
// Please note value only takes a uint as index.
|
||||
values_[index] = Value(mode, slot_index - kNotFound).raw();
|
||||
#ifdef DEBUG
|
||||
ValidateEntry(code, name, mode, slot_index);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ContextSlotCache::Clear() {
|
||||
for (int index = 0; index < kLength; index++) keys_[index].code = NULL;
|
||||
}
|
||||
|
||||
|
||||
ContextSlotCache::Key ContextSlotCache::keys_[ContextSlotCache::kLength];
|
||||
|
||||
|
||||
uint32_t ContextSlotCache::values_[ContextSlotCache::kLength];
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
void ContextSlotCache::ValidateEntry(Code* code,
|
||||
String* name,
|
||||
Variable::Mode mode,
|
||||
int slot_index) {
|
||||
String* symbol;
|
||||
if (Heap::LookupSymbolIfExists(name, &symbol)) {
|
||||
int index = Hash(code, name);
|
||||
Key& key = keys_[index];
|
||||
ASSERT(key.code == code);
|
||||
ASSERT(key.name->Equals(name));
|
||||
Value result(values_[index]);
|
||||
ASSERT(result.mode() == mode);
|
||||
ASSERT(result.index() + kNotFound == slot_index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <class Allocator>
|
||||
static void PrintList(const char* list_name,
|
||||
int nof_internal_slots,
|
||||
|
@ -163,6 +163,74 @@ class ZoneScopeInfo: public ScopeInfo<ZoneListAllocationPolicy> {
|
||||
};
|
||||
|
||||
|
||||
// Cache for mapping (code, property name) into context slot index.
|
||||
// The cache contains both positive and negative results.
|
||||
// Slot index equals -1 means the property is absent.
|
||||
// Cleared at startup and prior to mark sweep collection.
|
||||
class ContextSlotCache {
|
||||
public:
|
||||
// Lookup context slot index for (code, name).
|
||||
// If absent, kNotFound is returned.
|
||||
static int Lookup(Code* code,
|
||||
String* name,
|
||||
Variable::Mode* mode);
|
||||
|
||||
// Update an element in the cache.
|
||||
static void Update(Code* code,
|
||||
String* name,
|
||||
Variable::Mode mode,
|
||||
int slot_index);
|
||||
|
||||
// Clear the cache.
|
||||
static void Clear();
|
||||
|
||||
static const int kNotFound = -2;
|
||||
private:
|
||||
inline static int Hash(Code* code, String* name);
|
||||
|
||||
#ifdef DEBUG
|
||||
static void ValidateEntry(Code* code,
|
||||
String* name,
|
||||
Variable::Mode mode,
|
||||
int slot_index);
|
||||
#endif
|
||||
|
||||
static const int kLength = 256;
|
||||
struct Key {
|
||||
Code* code;
|
||||
String* name;
|
||||
};
|
||||
|
||||
struct Value {
|
||||
Value(Variable::Mode mode, int index) {
|
||||
ASSERT(ModeField::is_valid(mode));
|
||||
ASSERT(IndexField::is_valid(index));
|
||||
value_ = ModeField::encode(mode) | IndexField::encode(index);
|
||||
ASSERT(mode == this->mode());
|
||||
ASSERT(index == this->index());
|
||||
}
|
||||
|
||||
inline Value(uint32_t value) : value_(value) {}
|
||||
|
||||
uint32_t raw() { return value_; }
|
||||
|
||||
Variable::Mode mode() { return ModeField::decode(value_); }
|
||||
|
||||
int index() { return IndexField::decode(value_); }
|
||||
|
||||
// Bit fields in value_ (type, shift, size). Must be public so the
|
||||
// constants can be embedded in generated code.
|
||||
class ModeField: public BitField<Variable::Mode, 0, 3> {};
|
||||
class IndexField: public BitField<int, 3, 32-3> {};
|
||||
private:
|
||||
uint32_t value_;
|
||||
};
|
||||
|
||||
static Key keys_[kLength];
|
||||
static uint32_t values_[kLength];
|
||||
};
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_SCOPEINFO_H_
|
||||
|
Loading…
Reference in New Issue
Block a user