Author: [EMAIL PROTECTED]
Date: Thu Sep 25 00:46:07 2008
New Revision: 371
Modified:
branches/bleeding_edge/src/ast.cc
branches/bleeding_edge/src/codegen-ia32.cc
branches/bleeding_edge/src/contexts.h
branches/bleeding_edge/src/factory.cc
branches/bleeding_edge/src/factory.h
branches/bleeding_edge/src/flag-definitions.h
branches/bleeding_edge/src/objects-inl.h
branches/bleeding_edge/src/objects.cc
branches/bleeding_edge/src/objects.h
branches/bleeding_edge/src/parser.cc
branches/bleeding_edge/src/runtime.cc
Log:
- Added a map cache for literal objects. This will
canonicalize maps for object literals. JSON objects
with the same set of properties names will then
share the same map.
This reduces the amount of generated code associated
with object literals.
- Added a flag canonicalize_object_literal_maps.
(default true)
- Changed the format of a function's literal array.
Only the global context is now stored in the literal prefix.
Review URL: http://codereview.chromium.org/4078
Modified: branches/bleeding_edge/src/ast.cc
==============================================================================
--- branches/bleeding_edge/src/ast.cc (original)
+++ branches/bleeding_edge/src/ast.cc Thu Sep 25 00:46:07 2008
@@ -146,7 +146,6 @@
}
-
void LabelCollector::AddLabel(Label* label) {
// Add the label to the collector, but discard duplicates.
int length = labels_->length();
Modified: branches/bleeding_edge/src/codegen-ia32.cc
==============================================================================
--- branches/bleeding_edge/src/codegen-ia32.cc (original)
+++ branches/bleeding_edge/src/codegen-ia32.cc Thu Sep 25 00:46:07 2008
@@ -3811,7 +3811,8 @@
// therefore context dependent.
class ObjectLiteralDeferred: public DeferredCode {
public:
- ObjectLiteralDeferred(CodeGenerator* generator, ObjectLiteral* node)
+ ObjectLiteralDeferred(CodeGenerator* generator,
+ ObjectLiteral* node)
: DeferredCode(generator), node_(node) {
set_comment("[ ObjectLiteralDeferred");
}
@@ -3838,7 +3839,6 @@
void Ia32CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
Comment cmnt(masm_, "[ ObjectLiteral");
-
ObjectLiteralDeferred* deferred = new ObjectLiteralDeferred(this, node);
// Retrieve the literal array and check the allocated entry.
@@ -3866,6 +3866,7 @@
__ CallRuntime(Runtime::kCloneObjectLiteralBoilerplate, 1);
// Push the new cloned literal object as the result.
__ push(eax);
+
for (int i = 0; i < node->properties()->length(); i++) {
ObjectLiteral::Property* property = node->properties()->at(i);
Modified: branches/bleeding_edge/src/contexts.h
==============================================================================
--- branches/bleeding_edge/src/contexts.h (original)
+++ branches/bleeding_edge/src/contexts.h Thu Sep 25 00:46:07 2008
@@ -91,7 +91,8 @@
V(EMPTY_SCRIPT_INDEX, Script, empty_script) \
V(SCRIPT_FUNCTION_INDEX, JSFunction, script_function) \
V(CONTEXT_EXTENSION_FUNCTION_INDEX, JSFunction,
context_extension_function) \
- V(OUT_OF_MEMORY_INDEX, Object, out_of_memory)
+ V(OUT_OF_MEMORY_INDEX, Object, out_of_memory) \
+ V(MAP_CACHE_INDEX, Object, map_cache)
// JSFunctions are pairs (context, function code), sometimes also called
// closures. A Context object is used to represent function contexts and
@@ -207,6 +208,7 @@
SCRIPT_FUNCTION_INDEX,
CONTEXT_EXTENSION_FUNCTION_INDEX,
OUT_OF_MEMORY_INDEX,
+ MAP_CACHE_INDEX,
GLOBAL_CONTEXT_SLOTS
};
Modified: branches/bleeding_edge/src/factory.cc
==============================================================================
--- branches/bleeding_edge/src/factory.cc (original)
+++ branches/bleeding_edge/src/factory.cc Thu Sep 25 00:46:07 2008
@@ -211,12 +211,8 @@
// Store the object, regexp and array functions in the literals
// array prefix. These functions will be used when creating
// object, regexp and array literals in this function.
- literals->set(JSFunction::kLiteralObjectFunctionIndex,
- context->global_context()->object_function());
- literals->set(JSFunction::kLiteralRegExpFunctionIndex,
- context->global_context()->regexp_function());
- literals->set(JSFunction::kLiteralArrayFunctionIndex,
- context->global_context()->array_function());
+ literals->set(JSFunction::kLiteralGlobalContextIndex,
+ context->global_context());
}
result->set_literals(*literals);
ASSERT(!result->IsBoilerplate());
@@ -558,6 +554,12 @@
}
+Handle<JSObject> Factory::NewJSObjectFromMap(Handle<Map> map) {
+ CALL_HEAP_FUNCTION(Heap::AllocateJSObjectFromMap(*map, NOT_TENURED),
+ JSObject);
+}
+
+
Handle<JSObject> Factory::NewObjectLiteral(int
expected_number_of_properties) {
Handle<Map> map = Handle<Map>(Top::object_function()->initial_map());
map = Factory::CopyMap(map);
@@ -746,6 +748,47 @@
}
return result;
+}
+
+
+Handle<MapCache> Factory::NewMapCache(int at_least_space_for) {
+ CALL_HEAP_FUNCTION(MapCache::Allocate(at_least_space_for), MapCache);
+}
+
+
+static Object* UpdateMapCacheWith(Context* context,
+ FixedArray* keys,
+ Map* map) {
+ Object* result = MapCache::cast(context->map_cache())->Put(keys, map);
+ if (!result->IsFailure()) context->set_map_cache(MapCache::cast(result));
+ return result;
+}
+
+
+Handle<MapCache> Factory::AddToMapCache(Handle<Context> context,
+ Handle<FixedArray> keys,
+ Handle<Map> map) {
+ CALL_HEAP_FUNCTION(UpdateMapCacheWith(*context, *keys, *map), MapCache);
+}
+
+
+Handle<Map> Factory::ObjectLiteralMapFromCache(Handle<Context> context,
+ Handle<FixedArray> keys) {
+ if (context->map_cache()->IsUndefined()) {
+ // Allocate the new map cache for the global context.
+ Handle<MapCache> new_cache = NewMapCache(24);
+ context->set_map_cache(*new_cache);
+ }
+ // Check to see whether there is a maching element in the cache.
+ Handle<MapCache> cache =
+ Handle<MapCache>(MapCache::cast(context->map_cache()));
+ Handle<Object> result = Handle<Object>(cache->Lookup(*keys));
+ if (result->IsMap()) return Handle<Map>::cast(result);
+ // Create a new map and add it to the cache.
+ Handle<Map> map =
+ CopyMap(Handle<Map>(context->object_function()->initial_map()));
+ AddToMapCache(context, keys, map);
+ return Handle<Map>(map);
}
Modified: branches/bleeding_edge/src/factory.h
==============================================================================
--- branches/bleeding_edge/src/factory.h (original)
+++ branches/bleeding_edge/src/factory.h Thu Sep 25 00:46:07 2008
@@ -167,6 +167,10 @@
static Handle<JSObject> NewJSObject(Handle<JSFunction> constructor,
PretenureFlag pretenure =
NOT_TENURED);
+ // JS objects are pretenured when allocated by the bootstrapper and
+ // runtime.
+ static Handle<JSObject> NewJSObjectFromMap(Handle<Map> map);
+
// Allocate a JS object representing an object literal. The object is
// pretenured (allocated directly in the old generation).
static Handle<JSObject> NewObjectLiteral(int
expected_number_of_properties);
@@ -291,6 +295,12 @@
static Handle<DebugInfo> NewDebugInfo(Handle<SharedFunctionInfo> shared);
+
+ // Return a map using the map cache in the global context.
+ // The key the an ordered set of property names.
+ static Handle<Map> ObjectLiteralMapFromCache(Handle<Context> context,
+ Handle<FixedArray> keys);
+
private:
static Handle<JSFunction> NewFunctionHelper(Handle<String> name,
Handle<Object> prototype);
@@ -302,6 +312,14 @@
static Handle<JSFunction> BaseNewFunctionFromBoilerplate(
Handle<JSFunction> boilerplate,
Handle<Map> function_map);
+
+ // Create a new map cache.
+ static Handle<MapCache> NewMapCache(int at_least_space_for);
+
+ // Update the map cache in the global context with (keys, map)
+ static Handle<MapCache> AddToMapCache(Handle<Context> context,
+ Handle<FixedArray> keys,
+ Handle<Map> map);
};
Modified: branches/bleeding_edge/src/flag-definitions.h
==============================================================================
--- branches/bleeding_edge/src/flag-definitions.h (original)
+++ branches/bleeding_edge/src/flag-definitions.h Thu Sep 25 00:46:07 2008
@@ -154,6 +154,9 @@
DEFINE_bool(cleanup_caches_in_maps_at_gc, true,
"Flush code caches in maps during mark compact cycle.")
+DEFINE_bool(canonicalize_object_literal_maps, true,
+ "Canonicalize maps for object literals.")
+
// mksnapshot.cc
DEFINE_bool(h, false, "print this message")
Modified: branches/bleeding_edge/src/objects-inl.h
==============================================================================
--- branches/bleeding_edge/src/objects-inl.h (original)
+++ branches/bleeding_edge/src/objects-inl.h Thu Sep 25 00:46:07 2008
@@ -325,6 +325,11 @@
}
+bool Object::IsMapCache() {
+ return IsHashTable();
+}
+
+
bool Object::IsPrimitive() {
return IsOddball() || IsNumber() || IsString();
}
@@ -1112,6 +1117,7 @@
CAST_ACCESSOR(Dictionary)
CAST_ACCESSOR(SymbolTable)
CAST_ACCESSOR(CompilationCacheTable)
+CAST_ACCESSOR(MapCache)
CAST_ACCESSOR(String)
CAST_ACCESSOR(SeqString)
CAST_ACCESSOR(AsciiString)
Modified: branches/bleeding_edge/src/objects.cc
==============================================================================
--- branches/bleeding_edge/src/objects.cc (original)
+++ branches/bleeding_edge/src/objects.cc Thu Sep 25 00:46:07 2008
@@ -3923,6 +3923,11 @@
}
+Context* JSFunction::GlobalContextFromLiterals(FixedArray* literals) {
+ return
Context::cast(literals->get(JSFunction::kLiteralGlobalContextIndex));
+}
+
+
void Oddball::OddballIterateBody(ObjectVisitor* v) {
// Assumes all Object* members are contiguously allocated!
IteratePointers(v, kToStringOffset, kToNumberOffset + kPointerSize);
@@ -5533,12 +5538,12 @@
Object* obj = Allocate(nof * 2);
if (obj->IsFailure()) return obj;
- HashTable* dict = HashTable::cast(obj);
- WriteBarrierMode mode = dict->GetWriteBarrierMode();
+ HashTable* table = HashTable::cast(obj);
+ WriteBarrierMode mode = table->GetWriteBarrierMode();
// Copy prefix to new array.
for (int i = kPrefixStartIndex; i < kPrefixStartIndex + prefix_size;
i++) {
- dict->set(i, get(i), mode);
+ table->set(i, get(i), mode);
}
// Rehash the elements.
uint32_t (*Hash)(Object* key) = key->GetHashFunction();
@@ -5547,14 +5552,14 @@
Object* key = get(from_index);
if (IsKey(key)) {
uint32_t insertion_index =
- EntryToIndex(dict->FindInsertionEntry(key, Hash(key)));
+ EntryToIndex(table->FindInsertionEntry(key, Hash(key)));
for (int j = 0; j < element_size; j++) {
- dict->set(insertion_index + j, get(from_index + j), mode);
+ table->set(insertion_index + j, get(from_index + j), mode);
}
}
}
- dict->SetNumberOfElements(NumberOfElements());
- return dict;
+ table->SetNumberOfElements(NumberOfElements());
+ return table;
}
@@ -5650,6 +5655,70 @@
reinterpret_cast<CompilationCacheTable*>(obj);
int entry = cache->FindInsertionEntry(src, key.Hash());
cache->set(EntryToIndex(entry), src);
+ cache->set(EntryToIndex(entry) + 1, value);
+ cache->ElementAdded();
+ return cache;
+}
+
+
+// SymbolsKey used for HashTable where key is array of symbols.
+class SymbolsKey : public HashTableKey {
+ public:
+ explicit SymbolsKey(FixedArray* symbols) {
+ symbols_ = symbols;
+ }
+
+ bool IsMatch(Object* other) {
+ if (!other->IsFixedArray()) return false;
+ FixedArray* o = FixedArray::cast(other);
+ int len = symbols_->length();
+ if (o->length() != len) return false;
+ for (int i = 0; i < len; i++) {
+ if (o->get(i) != symbols_->get(i)) return false;
+ }
+ return true;
+ }
+
+ uint32_t Hash() { return SymbolsHash(symbols_); }
+
+ HashFunction GetHashFunction() { return SymbolsHash; }
+
+ Object* GetObject() { return symbols_; }
+
+ static uint32_t SymbolsHash(Object* obj) {
+ FixedArray* symbols_ = FixedArray::cast(obj);
+ int len = symbols_->length();
+ uint32_t hash = 0;
+ for (int i = 0; i < len; i++) {
+ hash ^= String::cast(symbols_->get(i))->Hash();
+ }
+ return hash;
+ }
+
+ bool IsStringKey() { return false; }
+
+ FixedArray* symbols_;
+};
+
+Object* MapCache::Lookup(FixedArray* array) {
+ SymbolsKey key(array);
+ int entry = FindEntry(&key);
+ if (entry != -1) {
+ return get(EntryToIndex(entry) + 1);
+ } else {
+ return Heap::undefined_value();
+ }
+}
+
+
+Object* MapCache::Put(FixedArray* array, Map* value) {
+ SymbolsKey key(array);
+ Object* obj = EnsureCapacity(1, &key);
+ if (obj->IsFailure()) return obj;
+
+ MapCache* cache = reinterpret_cast<MapCache*>(obj);
+ int entry = cache->FindInsertionEntry(array, key.Hash());
+ cache->set(EntryToIndex(entry), array);
cache->set(EntryToIndex(entry) + 1, value);
cache->ElementAdded();
return cache;
Modified: branches/bleeding_edge/src/objects.h
==============================================================================
--- branches/bleeding_edge/src/objects.h (original)
+++ branches/bleeding_edge/src/objects.h Thu Sep 25 00:46:07 2008
@@ -619,6 +619,7 @@
inline bool IsDictionary();
inline bool IsSymbolTable();
inline bool IsCompilationCacheTable();
+ inline bool IsMapCache();
inline bool IsPrimitive();
inline bool IsGlobalObject();
inline bool IsJSGlobalObject();
@@ -1836,6 +1837,22 @@
};
+// MapCache.
+//
+// Maps keys that are a fixed array of symbols to a map.
+// Used for canonicalize maps for object literals.
+class MapCache: public HashTable<0, 2> {
+ public:
+ // Find cached value for a string key, otherwise return null.
+ Object* Lookup(FixedArray* key);
+ Object* Put(FixedArray* key, Map* value);
+ static inline MapCache* cast(Object* obj);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(MapCache);
+};
+
+
// Dictionary for keeping properties and elements in slow case.
//
// One element in the prefix is used for storing non-element
@@ -2671,6 +2688,9 @@
// Returns the number of allocated literals.
int NumberOfLiterals();
+ // Retrieve the global context from a function's literal array.
+ static Context* GlobalContextFromLiterals(FixedArray* literals);
+
// Layout descriptors.
static const int kPrototypeOrInitialMapOffset = JSObject::kHeaderSize;
static const int kSharedFunctionInfoOffset =
@@ -2680,11 +2700,8 @@
static const int kSize = kLiteralsOffset + kPointerSize;
// Layout of the literals array.
- static const int kLiteralsPrefixSize = 3;
- static const int kLiteralObjectFunctionIndex = 0;
- static const int kLiteralRegExpFunctionIndex = 1;
- static const int kLiteralArrayFunctionIndex = 2;
-
+ static const int kLiteralsPrefixSize = 1;
+ static const int kLiteralGlobalContextIndex = 0;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSFunction);
};
Modified: branches/bleeding_edge/src/parser.cc
==============================================================================
--- branches/bleeding_edge/src/parser.cc (original)
+++ branches/bleeding_edge/src/parser.cc Thu Sep 25 00:46:07 2008
@@ -2738,8 +2738,7 @@
NEW(ObjectLiteral::Property(key, value));
// Count CONSTANT or COMPUTED properties to maintain the enumeration
order.
- if (IsBoilerplateProperty(property))
- number_of_boilerplate_properties++;
+ if (IsBoilerplateProperty(property))
number_of_boilerplate_properties++;
properties.Add(property);
// TODO(1240767): Consider allowing trailing comma.
Modified: branches/bleeding_edge/src/runtime.cc
==============================================================================
--- branches/bleeding_edge/src/runtime.cc (original)
+++ branches/bleeding_edge/src/runtime.cc Thu Sep 25 00:46:07 2008
@@ -97,6 +97,37 @@
}
+static Handle<Map> ComputeObjectLiteralMap(
+ Handle<Context> context,
+ Handle<FixedArray> constant_properties,
+ bool &is_result_from_cache) {
+ if (FLAG_canonicalize_object_literal_maps) {
+ // First find prefix of consecutive symbol keys.
+ int number_of_properties = constant_properties->length()/2;
+ int number_of_symbol_keys = 0;
+ while ((number_of_symbol_keys < number_of_properties) &&
+
(constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
+ number_of_symbol_keys++;
+ }
+ // Based on the number of prefix symbols key we decide whether
+ // to use the map cache in the global context.
+ const int kMaxKeys = 10;
+ if ((number_of_symbol_keys == number_of_properties)
+ && (number_of_symbol_keys < kMaxKeys)) {
+ // Create the fixed array with the key.
+ Handle<FixedArray> keys =
Factory::NewFixedArray(number_of_symbol_keys);
+ for (int i = 0; i < number_of_symbol_keys; i++) {
+ keys->set(i, constant_properties->get(i*2));
+ }
+ is_result_from_cache = true;
+ return Factory::ObjectLiteralMapFromCache(context, keys);
+ }
+ }
+ is_result_from_cache = false;
+ return Handle<Map>(context->object_function()->initial_map());
+}
+
+
static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 3);
@@ -104,21 +135,24 @@
Handle<FixedArray> literals = args.at<FixedArray>(0);
int literals_index = Smi::cast(args[1])->value();
Handle<FixedArray> constant_properties = args.at<FixedArray>(2);
+ Handle<Context> context =
+ Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
+
+ bool is_result_from_cache;
+ Handle<Map> map = ComputeObjectLiteralMap(context,
+ constant_properties,
+ is_result_from_cache);
// Get the object function from the literals array. This is the
// object function from the context in which the function was
// created. We do not use the object function from the current
// global context because this might be the object function from
// another context which we should not have access to.
- const int kObjectFunIndex = JSFunction::kLiteralObjectFunctionIndex;
- Handle<JSFunction> constructor =
- Handle<JSFunction>(JSFunction::cast(literals->get(kObjectFunIndex)));
-
- Handle<JSObject> boilerplate = Factory::NewJSObject(constructor,
TENURED);
-
+ Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
{ // Add the constant propeties to the boilerplate.
int length = constant_properties->length();
- OptimizedObjectForAddingMultipleProperties opt(boilerplate, true);
+ OptimizedObjectForAddingMultipleProperties opt(boilerplate,
+ !is_result_from_cache);
for (int index = 0; index < length; index +=2) {
Handle<Object> key(constant_properties->get(index+0));
Handle<Object> value(constant_properties->get(index+1));
@@ -160,9 +194,8 @@
ASSERT(args.length() == 2);
CONVERT_CHECKED(FixedArray, elements, args[0]);
CONVERT_CHECKED(FixedArray, literals, args[1]);
- const int kArrayFunIndex = JSFunction::kLiteralArrayFunctionIndex;
- JSFunction* constructor =
JSFunction::cast(literals->get(kArrayFunIndex));
-
+ JSFunction* constructor =
+ JSFunction::GlobalContextFromLiterals(literals)->array_function();
// Create the JSArray.
Object* object = Heap::AllocateJSObject(constructor);
if (object->IsFailure()) return object;
@@ -699,10 +732,9 @@
// created. We do not use the RegExp function from the current
// global context because this might be the RegExp function from
// another context which we should not have access to.
- const int kRegexpFunIndex = JSFunction::kLiteralRegExpFunctionIndex;
Handle<JSFunction> constructor =
- Handle<JSFunction>(JSFunction::cast(literals->get(kRegexpFunIndex)));
-
+ Handle<JSFunction>(
+
JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
// Compute the regular expression literal.
bool has_pending_exception;
Handle<Object> regexp =
@@ -839,12 +871,8 @@
// Insert the object, regexp and array functions in the literals
// array prefix. These are the functions that will be used when
// creating object, regexp and array literals.
- literals->set(JSFunction::kLiteralObjectFunctionIndex,
- context->global_context()->object_function());
- literals->set(JSFunction::kLiteralRegExpFunctionIndex,
- context->global_context()->regexp_function());
- literals->set(JSFunction::kLiteralArrayFunctionIndex,
- context->global_context()->array_function());
+ literals->set(JSFunction::kLiteralGlobalContextIndex,
+ context->global_context());
}
target->set_literals(*literals);
}
--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---