Revision: 11051
Author: [email protected]
Date: Thu Mar 15 00:13:46 2012
Log: Ensure that generated code for object literals will call
Runtime_DefineOrRedefineAccessorProperty only once per accessor property.
To do this, we collect all accessor properties in a first pass and emit
code for
defining those properties afterwards in a second pass.
As a finger exercise, the table used for collecting accessors has a (subset
of
an) STL-like iterator interface, including STL-like names and operators.
Although C++ is quite verbose here (as usual, but partly this is caused by
our
current slightly clumsy classes/templates), things work out quite nicely
and it
cleans up some confusion, e.g. a table entry is not an iterator etc.
Everything compiles into very efficient code, e.g. the loop condition 'it !=
accessor_table.end()' compiles into a single 'testl' instruction on ia32.
+1 for using standard APIs!
Review URL: https://chromiumcodereview.appspot.com/9691040
http://code.google.com/p/v8/source/detail?r=11051
Modified:
/branches/bleeding_edge/src/arm/full-codegen-arm.cc
/branches/bleeding_edge/src/ast.h
/branches/bleeding_edge/src/full-codegen.h
/branches/bleeding_edge/src/hashmap.h
/branches/bleeding_edge/src/ia32/full-codegen-ia32.cc
/branches/bleeding_edge/src/mips/full-codegen-mips.cc
/branches/bleeding_edge/src/x64/full-codegen-x64.cc
/branches/bleeding_edge/src/zone.h
=======================================
--- /branches/bleeding_edge/src/arm/full-codegen-arm.cc Fri Mar 9 04:07:29
2012
+++ /branches/bleeding_edge/src/arm/full-codegen-arm.cc Thu Mar 15 00:13:46
2012
@@ -1408,6 +1408,16 @@
__ CopyFields(r0, r5, r2.bit(), size / kPointerSize);
context()->Plug(r0);
}
+
+
+void FullCodeGenerator::EmitAccessor(Expression* expression) {
+ if (expression == NULL) {
+ __ LoadRoot(r1, Heap::kNullValueRootIndex);
+ __ push(r1);
+ } else {
+ VisitForStackValue(expression);
+ }
+}
void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
@@ -1445,6 +1455,7 @@
// marked expressions, no store code is emitted.
expr->CalculateEmitStore();
+ AccessorTable accessor_table(isolate()->zone());
for (int i = 0; i < expr->properties()->length(); i++) {
ObjectLiteral::Property* property = expr->properties()->at(i);
if (property->IsCompileTimeValue()) continue;
@@ -1493,26 +1504,28 @@
}
break;
case ObjectLiteral::Property::GETTER:
+ accessor_table.find(key)->second->getter = value;
+ break;
case ObjectLiteral::Property::SETTER:
- // Duplicate receiver on stack.
- __ ldr(r0, MemOperand(sp));
- __ push(r0);
- VisitForStackValue(key);
- if (property->kind() == ObjectLiteral::Property::GETTER) {
- VisitForStackValue(value);
- __ LoadRoot(r1, Heap::kNullValueRootIndex);
- __ push(r1);
- } else {
- __ LoadRoot(r1, Heap::kNullValueRootIndex);
- __ push(r1);
- VisitForStackValue(value);
- }
- __ mov(r0, Operand(Smi::FromInt(NONE)));
- __ push(r0);
- __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5);
+ accessor_table.find(key)->second->setter = value;
break;
}
}
+
+ // Emit code to define accessors, using only a single call to the
runtime for
+ // each pair of corresponding getters and setters.
+ for (AccessorTable::Iterator it = accessor_table.begin();
+ it != accessor_table.end();
+ ++it) {
+ __ ldr(r0, MemOperand(sp)); // Duplicate receiver.
+ __ push(r0);
+ VisitForStackValue(it->first);
+ EmitAccessor(it->second->getter);
+ EmitAccessor(it->second->setter);
+ __ mov(r0, Operand(Smi::FromInt(NONE)));
+ __ push(r0);
+ __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5);
+ }
if (expr->has_function()) {
ASSERT(result_saved);
=======================================
--- /branches/bleeding_edge/src/ast.h Tue Mar 13 05:11:46 2012
+++ /branches/bleeding_edge/src/ast.h Thu Mar 15 00:13:46 2012
@@ -1366,6 +1366,12 @@
kHasFunction = 1 << 1
};
+ struct Accessors: public ZoneObject {
+ Accessors() : getter(NULL), setter(NULL) { }
+ Expression* getter;
+ Expression* setter;
+ };
+
protected:
template<class> friend class AstNodeFactory;
=======================================
--- /branches/bleeding_edge/src/full-codegen.h Fri Mar 9 08:23:06 2012
+++ /branches/bleeding_edge/src/full-codegen.h Thu Mar 15 00:13:46 2012
@@ -470,6 +470,8 @@
Label* done);
void EmitVariableLoad(VariableProxy* proxy);
+ void EmitAccessor(Expression* expression);
+
// Expects the arguments and the function already pushed.
void EmitResolvePossiblyDirectEval(int arg_count);
@@ -804,6 +806,28 @@
};
+// A map from property names to getter/setter pairs allocated in the zone.
+class AccessorTable: public TemplateHashMap<Literal,
+ ObjectLiteral::Accessors,
+ ZoneListAllocationPolicy> {
+ public:
+ explicit AccessorTable(Zone* zone) :
+ TemplateHashMap<Literal,
+ ObjectLiteral::Accessors,
+ ZoneListAllocationPolicy>(Literal::Match),
+ zone_(zone) { }
+
+ Iterator lookup(Literal* literal) {
+ Iterator it = find(literal, true);
+ if (it->second == NULL) it->second = new(zone_)
ObjectLiteral::Accessors();
+ return it;
+ }
+
+ private:
+ Zone* zone_;
+};
+
+
} } // namespace v8::internal
#endif // V8_FULL_CODEGEN_H_
=======================================
--- /branches/bleeding_edge/src/hashmap.h Thu Feb 23 02:12:50 2012
+++ /branches/bleeding_edge/src/hashmap.h Thu Mar 15 00:13:46 2012
@@ -36,15 +36,15 @@
namespace internal {
template<class AllocationPolicy>
-class TemplateHashMap {
+class TemplateHashMapImpl {
public:
typedef bool (*MatchFun) (void* key1, void* key2);
// initial_capacity is the size of the initial hash map;
// it must be a power of 2 (and thus must not be 0).
- TemplateHashMap(MatchFun match, uint32_t initial_capacity = 8);
-
- ~TemplateHashMap();
+ TemplateHashMapImpl(MatchFun match, uint32_t initial_capacity = 8);
+
+ ~TemplateHashMapImpl();
// HashMap entries are (key, value, hash) triplets.
// Some clients may not need to use the value slot
@@ -99,10 +99,10 @@
void Resize();
};
-typedef TemplateHashMap<FreeStoreAllocationPolicy> HashMap;
+typedef TemplateHashMapImpl<FreeStoreAllocationPolicy> HashMap;
template<class P>
-TemplateHashMap<P>::TemplateHashMap(MatchFun match,
+TemplateHashMapImpl<P>::TemplateHashMapImpl(MatchFun match,
uint32_t initial_capacity) {
match_ = match;
Initialize(initial_capacity);
@@ -110,13 +110,13 @@
template<class P>
-TemplateHashMap<P>::~TemplateHashMap() {
+TemplateHashMapImpl<P>::~TemplateHashMapImpl() {
P::Delete(map_);
}
template<class P>
-typename TemplateHashMap<P>::Entry* TemplateHashMap<P>::Lookup(
+typename TemplateHashMapImpl<P>::Entry* TemplateHashMapImpl<P>::Lookup(
void* key, uint32_t hash, bool insert) {
// Find a matching entry.
Entry* p = Probe(key, hash);
@@ -146,7 +146,7 @@
template<class P>
-void TemplateHashMap<P>::Remove(void* key, uint32_t hash) {
+void TemplateHashMapImpl<P>::Remove(void* key, uint32_t hash) {
// Lookup the entry for the key to remove.
Entry* p = Probe(key, hash);
if (p->key == NULL) {
@@ -206,7 +206,7 @@
template<class P>
-void TemplateHashMap<P>::Clear() {
+void TemplateHashMapImpl<P>::Clear() {
// Mark all entries as empty.
const Entry* end = map_end();
for (Entry* p = map_; p < end; p++) {
@@ -217,13 +217,14 @@
template<class P>
-typename TemplateHashMap<P>::Entry* TemplateHashMap<P>::Start() const {
+typename TemplateHashMapImpl<P>::Entry* TemplateHashMapImpl<P>::Start()
const {
return Next(map_ - 1);
}
template<class P>
-typename TemplateHashMap<P>::Entry* TemplateHashMap<P>::Next(Entry* p)
const {
+typename TemplateHashMapImpl<P>::Entry*
TemplateHashMapImpl<P>::Next(Entry* p)
+ const {
const Entry* end = map_end();
ASSERT(map_ - 1 <= p && p < end);
for (p++; p < end; p++) {
@@ -236,7 +237,7 @@
template<class P>
-typename TemplateHashMap<P>::Entry* TemplateHashMap<P>::Probe(void* key,
+typename TemplateHashMapImpl<P>::Entry*
TemplateHashMapImpl<P>::Probe(void* key,
uint32_t hash)
{
ASSERT(key != NULL);
@@ -258,7 +259,7 @@
template<class P>
-void TemplateHashMap<P>::Initialize(uint32_t capacity) {
+void TemplateHashMapImpl<P>::Initialize(uint32_t capacity) {
ASSERT(IsPowerOf2(capacity));
map_ = reinterpret_cast<Entry*>(P::New(capacity * sizeof(Entry)));
if (map_ == NULL) {
@@ -271,7 +272,7 @@
template<class P>
-void TemplateHashMap<P>::Resize() {
+void TemplateHashMapImpl<P>::Resize() {
Entry* map = map_;
uint32_t n = occupancy_;
@@ -289,6 +290,50 @@
// Delete old map.
P::Delete(map);
}
+
+
+// A hash map for pointer keys and values with an STL-like interface.
+template<class Key, class Value, class AllocationPolicy>
+class TemplateHashMap: private TemplateHashMapImpl<AllocationPolicy> {
+ public:
+ STATIC_ASSERT(sizeof(Key*) == sizeof(void*)); // NOLINT
+ STATIC_ASSERT(sizeof(Value*) == sizeof(void*)); // NOLINT
+ struct value_type {
+ Key* first;
+ Value* second;
+ };
+
+ class Iterator {
+ public:
+ Iterator& operator++() {
+ entry_ = map_->Next(entry_);
+ return *this;
+ }
+
+ value_type* operator->() { return
reinterpret_cast<value_type*>(entry_); }
+ bool operator!=(const Iterator& other) { return entry_ !=
other.entry_; }
+
+ private:
+ Iterator(const TemplateHashMapImpl<AllocationPolicy>* map,
+ typename TemplateHashMapImpl<AllocationPolicy>::Entry*
entry) :
+ map_(map), entry_(entry) { }
+
+ const TemplateHashMapImpl<AllocationPolicy>* map_;
+ typename TemplateHashMapImpl<AllocationPolicy>::Entry* entry_;
+
+ friend class TemplateHashMap;
+ };
+
+ TemplateHashMap(
+ typename TemplateHashMapImpl<AllocationPolicy>::MatchFun match)
+ : TemplateHashMapImpl<AllocationPolicy>(match) { }
+
+ Iterator begin() const { return Iterator(this, this->Start()); }
+ Iterator end() const { return Iterator(this, NULL); }
+ Iterator find(Key* key, bool insert = false) {
+ return Iterator(this, Lookup(key, key->Hash(), insert));
+ }
+};
} } // namespace v8::internal
=======================================
--- /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Fri Mar 9
08:23:06 2012
+++ /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Thu Mar 15
00:13:46 2012
@@ -1409,6 +1409,15 @@
}
context()->Plug(eax);
}
+
+
+void FullCodeGenerator::EmitAccessor(Expression* expression) {
+ if (expression == NULL) {
+ __ push(Immediate(isolate()->factory()->null_value()));
+ } else {
+ VisitForStackValue(expression);
+ }
+}
void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
@@ -1445,6 +1454,7 @@
// marked expressions, no store code is emitted.
expr->CalculateEmitStore();
+ AccessorTable accessor_table(isolate()->zone());
for (int i = 0; i < expr->properties()->length(); i++) {
ObjectLiteral::Property* property = expr->properties()->at(i);
if (property->IsCompileTimeValue()) continue;
@@ -1456,6 +1466,8 @@
result_saved = true;
}
switch (property->kind()) {
+ case ObjectLiteral::Property::CONSTANT:
+ UNREACHABLE();
case ObjectLiteral::Property::MATERIALIZED_LITERAL:
ASSERT(!CompileTimeValue::IsCompileTimeValue(value));
// Fall through.
@@ -1487,23 +1499,27 @@
__ Drop(3);
}
break;
- case ObjectLiteral::Property::SETTER:
case ObjectLiteral::Property::GETTER:
- __ push(Operand(esp, 0)); // Duplicate receiver.
- VisitForStackValue(key);
- if (property->kind() == ObjectLiteral::Property::GETTER) {
- VisitForStackValue(value);
- __ push(Immediate(isolate()->factory()->null_value()));
- } else {
- __ push(Immediate(isolate()->factory()->null_value()));
- VisitForStackValue(value);
- }
- __ push(Immediate(Smi::FromInt(NONE)));
- __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5);
+ accessor_table.lookup(key)->second->getter = value;
break;
- default: UNREACHABLE();
+ case ObjectLiteral::Property::SETTER:
+ accessor_table.lookup(key)->second->setter = value;
+ break;
}
}
+
+ // Emit code to define accessors, using only a single call to the
runtime for
+ // each pair of corresponding getters and setters.
+ for (AccessorTable::Iterator it = accessor_table.begin();
+ it != accessor_table.end();
+ ++it) {
+ __ push(Operand(esp, 0)); // Duplicate receiver.
+ VisitForStackValue(it->first);
+ EmitAccessor(it->second->getter);
+ EmitAccessor(it->second->setter);
+ __ push(Immediate(Smi::FromInt(NONE)));
+ __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5);
+ }
if (expr->has_function()) {
ASSERT(result_saved);
=======================================
--- /branches/bleeding_edge/src/mips/full-codegen-mips.cc Tue Mar 13
09:18:30 2012
+++ /branches/bleeding_edge/src/mips/full-codegen-mips.cc Thu Mar 15
00:13:46 2012
@@ -1419,6 +1419,16 @@
__ CopyFields(v0, t1, a2.bit(), size / kPointerSize);
context()->Plug(v0);
}
+
+
+void FullCodeGenerator::EmitAccessor(Expression* expression) {
+ if (expression == NULL) {
+ __ LoadRoot(a1, Heap::kNullValueRootIndex);
+ __ push(a1);
+ } else {
+ VisitForStackValue(expression);
+ }
+}
void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
@@ -1456,6 +1466,7 @@
// marked expressions, no store code is emitted.
expr->CalculateEmitStore();
+ AccessorTable accessor_table(isolate()->zone());
for (int i = 0; i < expr->properties()->length(); i++) {
ObjectLiteral::Property* property = expr->properties()->at(i);
if (property->IsCompileTimeValue()) continue;
@@ -1505,26 +1516,28 @@
}
break;
case ObjectLiteral::Property::GETTER:
+ accessor_table.find(key)->second->getter = value;
+ break;
case ObjectLiteral::Property::SETTER:
- // Duplicate receiver on stack.
- __ lw(a0, MemOperand(sp));
- __ push(a0);
- VisitForStackValue(key);
- if (property->kind() == ObjectLiteral::Property::GETTER) {
- VisitForStackValue(value);
- __ LoadRoot(a1, Heap::kNullValueRootIndex);
- __ push(a1);
- } else {
- __ LoadRoot(a1, Heap::kNullValueRootIndex);
- __ push(a1);
- VisitForStackValue(value);
- }
- __ li(a0, Operand(Smi::FromInt(NONE)));
- __ push(a0);
- __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5);
+ accessor_table.find(key)->second->setter = value;
break;
}
}
+
+ // Emit code to define accessors, using only a single call to the
runtime for
+ // each pair of corresponding getters and setters.
+ for (AccessorTable::Iterator it = accessor_table.begin();
+ it != accessor_table.end();
+ ++it) {
+ __ lw(a0, MemOperand(sp)); // Duplicate receiver.
+ __ push(a0);
+ VisitForStackValue(it->first);
+ EmitAccessor(it->second->getter);
+ EmitAccessor(it->second->setter);
+ __ li(a0, Operand(Smi::FromInt(NONE)));
+ __ push(a0);
+ __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5);
+ }
if (expr->has_function()) {
ASSERT(result_saved);
=======================================
--- /branches/bleeding_edge/src/x64/full-codegen-x64.cc Fri Mar 9 04:07:29
2012
+++ /branches/bleeding_edge/src/x64/full-codegen-x64.cc Thu Mar 15 00:13:46
2012
@@ -1375,6 +1375,15 @@
}
context()->Plug(rax);
}
+
+
+void FullCodeGenerator::EmitAccessor(Expression* expression) {
+ if (expression == NULL) {
+ __ PushRoot(Heap::kNullValueRootIndex);
+ } else {
+ VisitForStackValue(expression);
+ }
+}
void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
@@ -1411,6 +1420,7 @@
// marked expressions, no store code is emitted.
expr->CalculateEmitStore();
+ AccessorTable accessor_table(isolate()->zone());
for (int i = 0; i < expr->properties()->length(); i++) {
ObjectLiteral::Property* property = expr->properties()->at(i);
if (property->IsCompileTimeValue()) continue;
@@ -1455,22 +1465,27 @@
__ Drop(3);
}
break;
- case ObjectLiteral::Property::SETTER:
case ObjectLiteral::Property::GETTER:
- __ push(Operand(rsp, 0)); // Duplicate receiver.
- VisitForStackValue(key);
- if (property->kind() == ObjectLiteral::Property::GETTER) {
- VisitForStackValue(value);
- __ PushRoot(Heap::kNullValueRootIndex);
- } else {
- __ PushRoot(Heap::kNullValueRootIndex);
- VisitForStackValue(value);
- }
- __ Push(Smi::FromInt(NONE));
- __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5);
+ accessor_table.find(key)->second->getter = value;
break;
+ case ObjectLiteral::Property::SETTER:
+ accessor_table.find(key)->second->setter = value;
+ break;
}
}
+
+ // Emit code to define accessors, using only a single call to the
runtime for
+ // each pair of corresponding getters and setters.
+ for (AccessorTable::Iterator it = accessor_table.begin();
+ it != accessor_table.end();
+ ++it) {
+ __ push(Operand(rsp, 0)); // Duplicate receiver.
+ VisitForStackValue(it->first);
+ EmitAccessor(it->second->getter);
+ EmitAccessor(it->second->setter);
+ __ Push(Smi::FromInt(NONE));
+ __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5);
+ }
if (expr->has_function()) {
ASSERT(result_saved);
=======================================
--- /branches/bleeding_edge/src/zone.h Thu Feb 23 01:12:57 2012
+++ /branches/bleeding_edge/src/zone.h Thu Mar 15 00:13:46 2012
@@ -240,7 +240,7 @@
};
-typedef TemplateHashMap<ZoneListAllocationPolicy> ZoneHashMap;
+typedef TemplateHashMapImpl<ZoneListAllocationPolicy> ZoneHashMap;
} } // namespace v8::internal
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev