Commit 0179ec57 authored by jkummerow's avatar jkummerow Committed by Commit bot

Use Cells to check prototype chain validity (disabled by default).

The cells are stored on prototypes (in their map's PrototypeInfo). When a prototype object changes its map, then both its own validity cell and those of all "downstream" prototypes are invalidated; handlers for a given receiver embed the currently valid cell for that receiver's prototype during their compilation and check it on execution.

Review URL: https://codereview.chromium.org/908213002

Cr-Commit-Position: refs/heads/master@{#27845}
parent a2481f8d
...@@ -675,6 +675,9 @@ DEFINE_INT(random_seed, 0, ...@@ -675,6 +675,9 @@ DEFINE_INT(random_seed, 0,
DEFINE_BOOL(trace_weak_arrays, false, "trace WeakFixedArray usage") DEFINE_BOOL(trace_weak_arrays, false, "trace WeakFixedArray usage")
DEFINE_BOOL(track_prototype_users, false, DEFINE_BOOL(track_prototype_users, false,
"keep track of which maps refer to a given prototype object") "keep track of which maps refer to a given prototype object")
DEFINE_BOOL(eliminate_prototype_chain_checks, false,
"collapse prototype chain checks into single-cell checks")
DEFINE_IMPLICATION(eliminate_prototype_chain_checks, track_prototype_users)
DEFINE_BOOL(use_verbose_printer, true, "allows verbose printing") DEFINE_BOOL(use_verbose_printer, true, "allows verbose printing")
#if TRACE_MAPS #if TRACE_MAPS
DEFINE_BOOL(trace_maps, false, "trace map creation") DEFINE_BOOL(trace_maps, false, "trace map creation")
......
...@@ -411,8 +411,8 @@ void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type, ...@@ -411,8 +411,8 @@ void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type,
Register PropertyHandlerCompiler::CheckPrototypes( Register PropertyHandlerCompiler::CheckPrototypes(
Register object_reg, Register holder_reg, Register scratch1, Register object_reg, Register holder_reg, Register scratch1,
Register scratch2, Handle<Name> name, Label* miss, Register scratch2, Handle<Name> name, Label* miss, PrototypeCheckType check,
PrototypeCheckType check) { ReturnHolder return_what) {
Handle<Map> receiver_map = map(); Handle<Map> receiver_map = map();
// Make sure there's no overlap between holder and object registers. // Make sure there's no overlap between holder and object registers.
...@@ -420,6 +420,30 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -420,6 +420,30 @@ Register PropertyHandlerCompiler::CheckPrototypes(
DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) && DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) &&
!scratch2.is(scratch1)); !scratch2.is(scratch1));
if (FLAG_eliminate_prototype_chain_checks) {
Handle<Cell> validity_cell =
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
if (!validity_cell.is_null()) {
DCHECK_EQ(Smi::FromInt(Map::kPrototypeChainValid),
validity_cell->value());
__ mov(scratch1, Operand(validity_cell));
__ ldr(scratch1, FieldMemOperand(scratch1, Cell::kValueOffset));
__ cmp(scratch1, Operand(Smi::FromInt(Map::kPrototypeChainValid)));
__ b(ne, miss);
}
// The prototype chain of primitives (and their JSValue wrappers) depends
// on the native context, which can't be guarded by validity cells.
// |object_reg| holds the native context specific prototype in this case;
// we need to check its map.
if (check == CHECK_ALL_MAPS) {
__ ldr(scratch1, FieldMemOperand(object_reg, HeapObject::kMapOffset));
Handle<WeakCell> cell = Map::WeakCellForMap(receiver_map);
__ CmpWeakValue(scratch1, cell, scratch2);
__ b(ne, miss);
}
}
// Keep track of the current object in register reg. // Keep track of the current object in register reg.
Register reg = object_reg; Register reg = object_reg;
int depth = 0; int depth = 0;
...@@ -464,30 +488,36 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -464,30 +488,36 @@ Register PropertyHandlerCompiler::CheckPrototypes(
current->property_dictionary()->FindEntry(name) == current->property_dictionary()->FindEntry(name) ==
NameDictionary::kNotFound); NameDictionary::kNotFound);
if (FLAG_eliminate_prototype_chain_checks && depth > 1) {
// TODO(jkummerow): Cache and re-use weak cell.
__ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss);
}
GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1, GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1,
scratch2); scratch2);
if (!FLAG_eliminate_prototype_chain_checks) {
__ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); __ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
reg = holder_reg; // From now on the object will be in holder_reg. __ ldr(holder_reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
__ ldr(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset)); }
} else { } else {
Register map_reg = scratch1; Register map_reg = scratch1;
__ ldr(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset)); if (!FLAG_eliminate_prototype_chain_checks) {
__ ldr(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset));
}
if (current_map->IsJSGlobalObjectMap()) { if (current_map->IsJSGlobalObjectMap()) {
GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current), GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
name, scratch2, miss); name, scratch2, miss);
} else if (depth != 1 || check == CHECK_ALL_MAPS) { } else if (!FLAG_eliminate_prototype_chain_checks &&
(depth != 1 || check == CHECK_ALL_MAPS)) {
Handle<WeakCell> cell = Map::WeakCellForMap(current_map); Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
__ CmpWeakValue(map_reg, cell, scratch2); __ CmpWeakValue(map_reg, cell, scratch2);
__ b(ne, miss); __ b(ne, miss);
} }
if (!FLAG_eliminate_prototype_chain_checks) {
reg = holder_reg; // From now on the object will be in holder_reg. __ ldr(holder_reg, FieldMemOperand(map_reg, Map::kPrototypeOffset));
}
__ ldr(reg, FieldMemOperand(map_reg, Map::kPrototypeOffset));
} }
reg = holder_reg; // From now on the object will be in holder_reg.
// Go to the next object in the prototype chain. // Go to the next object in the prototype chain.
current = prototype; current = prototype;
current_map = handle(current->map()); current_map = handle(current->map());
...@@ -498,7 +528,8 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -498,7 +528,8 @@ Register PropertyHandlerCompiler::CheckPrototypes(
// Log the check depth. // Log the check depth.
LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
if (depth != 0 || check == CHECK_ALL_MAPS) { if (!FLAG_eliminate_prototype_chain_checks &&
(depth != 0 || check == CHECK_ALL_MAPS)) {
// Check the holder map. // Check the holder map.
__ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); __ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
Handle<WeakCell> cell = Map::WeakCellForMap(current_map); Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
...@@ -506,8 +537,13 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -506,8 +537,13 @@ Register PropertyHandlerCompiler::CheckPrototypes(
__ b(ne, miss); __ b(ne, miss);
} }
bool return_holder = return_what == RETURN_HOLDER;
if (FLAG_eliminate_prototype_chain_checks && return_holder && depth != 0) {
__ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss);
}
// Return the register containing the holder. // Return the register containing the holder.
return reg; return return_holder ? reg : no_reg;
} }
...@@ -722,7 +758,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal( ...@@ -722,7 +758,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
if (IC::ICUseVector(kind())) { if (IC::ICUseVector(kind())) {
PushVectorAndSlot(); PushVectorAndSlot();
} }
FrontendHeader(receiver(), name, &miss); FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
// Get the value from the cell. // Get the value from the cell.
Register result = StoreDescriptor::ValueRegister(); Register result = StoreDescriptor::ValueRegister();
......
...@@ -339,7 +339,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal( ...@@ -339,7 +339,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
if (IC::ICUseVector(kind())) { if (IC::ICUseVector(kind())) {
PushVectorAndSlot(); PushVectorAndSlot();
} }
FrontendHeader(receiver(), name, &miss); FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
// Get the value from the cell. // Get the value from the cell.
Register result = StoreDescriptor::ValueRegister(); Register result = StoreDescriptor::ValueRegister();
...@@ -461,14 +461,38 @@ void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type, ...@@ -461,14 +461,38 @@ void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type,
Register PropertyHandlerCompiler::CheckPrototypes( Register PropertyHandlerCompiler::CheckPrototypes(
Register object_reg, Register holder_reg, Register scratch1, Register object_reg, Register holder_reg, Register scratch1,
Register scratch2, Handle<Name> name, Label* miss, Register scratch2, Handle<Name> name, Label* miss, PrototypeCheckType check,
PrototypeCheckType check) { ReturnHolder return_what) {
Handle<Map> receiver_map = map(); Handle<Map> receiver_map = map();
// object_reg and holder_reg registers can alias. // object_reg and holder_reg registers can alias.
DCHECK(!AreAliased(object_reg, scratch1, scratch2)); DCHECK(!AreAliased(object_reg, scratch1, scratch2));
DCHECK(!AreAliased(holder_reg, scratch1, scratch2)); DCHECK(!AreAliased(holder_reg, scratch1, scratch2));
if (FLAG_eliminate_prototype_chain_checks) {
Handle<Cell> validity_cell =
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
if (!validity_cell.is_null()) {
DCHECK_EQ(Smi::FromInt(Map::kPrototypeChainValid),
validity_cell->value());
__ Mov(scratch1, Operand(validity_cell));
__ Ldr(scratch1, FieldMemOperand(scratch1, Cell::kValueOffset));
__ Cmp(scratch1, Operand(Smi::FromInt(Map::kPrototypeChainValid)));
__ B(ne, miss);
}
// The prototype chain of primitives (and their JSValue wrappers) depends
// on the native context, which can't be guarded by validity cells.
// |object_reg| holds the native context specific prototype in this case;
// we need to check its map.
if (check == CHECK_ALL_MAPS) {
__ Ldr(scratch1, FieldMemOperand(object_reg, HeapObject::kMapOffset));
Handle<WeakCell> cell = Map::WeakCellForMap(receiver_map);
__ CmpWeakValue(scratch1, cell, scratch2);
__ B(ne, miss);
}
}
// Keep track of the current object in register reg. // Keep track of the current object in register reg.
Register reg = object_reg; Register reg = object_reg;
int depth = 0; int depth = 0;
...@@ -513,30 +537,37 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -513,30 +537,37 @@ Register PropertyHandlerCompiler::CheckPrototypes(
DCHECK(current.is_null() || (current->property_dictionary()->FindEntry( DCHECK(current.is_null() || (current->property_dictionary()->FindEntry(
name) == NameDictionary::kNotFound)); name) == NameDictionary::kNotFound));
if (FLAG_eliminate_prototype_chain_checks && depth > 1) {
// TODO(jkummerow): Cache and re-use weak cell.
__ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss);
}
GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1, GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1,
scratch2); scratch2);
__ Ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); if (!FLAG_eliminate_prototype_chain_checks) {
reg = holder_reg; // From now on the object will be in holder_reg. __ Ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
__ Ldr(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset)); __ Ldr(holder_reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
}
} else { } else {
Register map_reg = scratch1; Register map_reg = scratch1;
__ Ldr(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset)); if (!FLAG_eliminate_prototype_chain_checks) {
__ Ldr(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset));
}
if (current_map->IsJSGlobalObjectMap()) { if (current_map->IsJSGlobalObjectMap()) {
GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current), GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
name, scratch2, miss); name, scratch2, miss);
} else if (depth != 1 || check == CHECK_ALL_MAPS) { } else if (!FLAG_eliminate_prototype_chain_checks &&
(depth != 1 || check == CHECK_ALL_MAPS)) {
Handle<WeakCell> cell = Map::WeakCellForMap(current_map); Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
__ CmpWeakValue(map_reg, cell, scratch2); __ CmpWeakValue(map_reg, cell, scratch2);
__ B(ne, miss); __ B(ne, miss);
} }
if (!FLAG_eliminate_prototype_chain_checks) {
reg = holder_reg; // From now on the object will be in holder_reg. __ Ldr(holder_reg, FieldMemOperand(map_reg, Map::kPrototypeOffset));
}
__ Ldr(reg, FieldMemOperand(map_reg, Map::kPrototypeOffset));
} }
reg = holder_reg; // From now on the object will be in holder_reg.
// Go to the next object in the prototype chain. // Go to the next object in the prototype chain.
current = prototype; current = prototype;
current_map = handle(current->map()); current_map = handle(current->map());
...@@ -547,8 +578,8 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -547,8 +578,8 @@ Register PropertyHandlerCompiler::CheckPrototypes(
// Log the check depth. // Log the check depth.
LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
// Check the holder map. if (!FLAG_eliminate_prototype_chain_checks &&
if (depth != 0 || check == CHECK_ALL_MAPS) { (depth != 0 || check == CHECK_ALL_MAPS)) {
// Check the holder map. // Check the holder map.
__ Ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); __ Ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
Handle<WeakCell> cell = Map::WeakCellForMap(current_map); Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
...@@ -556,8 +587,13 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -556,8 +587,13 @@ Register PropertyHandlerCompiler::CheckPrototypes(
__ B(ne, miss); __ B(ne, miss);
} }
bool return_holder = return_what == RETURN_HOLDER;
if (FLAG_eliminate_prototype_chain_checks && return_holder && depth != 0) {
__ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss);
}
// Return the register containing the holder. // Return the register containing the holder.
return reg; return return_holder ? reg : no_reg;
} }
......
...@@ -87,7 +87,8 @@ Handle<Code> PropertyHandlerCompiler::GetCode(Code::Kind kind, ...@@ -87,7 +87,8 @@ Handle<Code> PropertyHandlerCompiler::GetCode(Code::Kind kind,
Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg, Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg,
Handle<Name> name, Handle<Name> name,
Label* miss) { Label* miss,
ReturnHolder return_what) {
PrototypeCheckType check_type = CHECK_ALL_MAPS; PrototypeCheckType check_type = CHECK_ALL_MAPS;
int function_index = -1; int function_index = -1;
if (map()->instance_type() < FIRST_NONSTRING_TYPE) { if (map()->instance_type() < FIRST_NONSTRING_TYPE) {
...@@ -114,7 +115,7 @@ Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg, ...@@ -114,7 +115,7 @@ Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg,
// Check that the maps starting from the prototype haven't changed. // Check that the maps starting from the prototype haven't changed.
return CheckPrototypes(object_reg, scratch1(), scratch2(), scratch3(), name, return CheckPrototypes(object_reg, scratch1(), scratch2(), scratch3(), name,
miss, check_type); miss, check_type, return_what);
} }
...@@ -122,9 +123,10 @@ Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg, ...@@ -122,9 +123,10 @@ Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg,
// miss. // miss.
Register NamedStoreHandlerCompiler::FrontendHeader(Register object_reg, Register NamedStoreHandlerCompiler::FrontendHeader(Register object_reg,
Handle<Name> name, Handle<Name> name,
Label* miss) { Label* miss,
ReturnHolder return_what) {
return CheckPrototypes(object_reg, this->name(), scratch1(), scratch2(), name, return CheckPrototypes(object_reg, this->name(), scratch1(), scratch2(), name,
miss, SKIP_RECEIVER); miss, SKIP_RECEIVER, return_what);
} }
...@@ -133,7 +135,7 @@ Register PropertyHandlerCompiler::Frontend(Handle<Name> name) { ...@@ -133,7 +135,7 @@ Register PropertyHandlerCompiler::Frontend(Handle<Name> name) {
if (IC::ICUseVector(kind())) { if (IC::ICUseVector(kind())) {
PushVectorAndSlot(); PushVectorAndSlot();
} }
Register reg = FrontendHeader(receiver(), name, &miss); Register reg = FrontendHeader(receiver(), name, &miss, RETURN_HOLDER);
FrontendFooter(name, &miss); FrontendFooter(name, &miss);
// The footer consumes the vector and slot from the stack if miss occurs. // The footer consumes the vector and slot from the stack if miss occurs.
if (IC::ICUseVector(kind())) { if (IC::ICUseVector(kind())) {
...@@ -156,8 +158,13 @@ void PropertyHandlerCompiler::NonexistentFrontendHeader(Handle<Name> name, ...@@ -156,8 +158,13 @@ void PropertyHandlerCompiler::NonexistentFrontendHeader(Handle<Name> name,
// Handle<JSObject>::null(). // Handle<JSObject>::null().
DCHECK(last_map->prototype() == isolate()->heap()->null_value()); DCHECK(last_map->prototype() == isolate()->heap()->null_value());
} else { } else {
holder_reg = FrontendHeader(receiver(), name, miss);
last_map = handle(holder()->map()); last_map = handle(holder()->map());
// This condition matches the branches below.
bool need_holder =
last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap();
holder_reg =
FrontendHeader(receiver(), name, miss,
need_holder ? RETURN_HOLDER : DONT_RETURN_ANYTHING);
} }
if (last_map->is_dictionary_map()) { if (last_map->is_dictionary_map()) {
...@@ -328,7 +335,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor( ...@@ -328,7 +335,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor(
auto last_handle = handle(last); auto last_handle = handle(last);
set_holder(last_handle); set_holder(last_handle);
} }
Register reg = FrontendHeader(receiver(), it->name(), &miss); Register reg = FrontendHeader(receiver(), it->name(), &miss, RETURN_HOLDER);
// Reset the holder so further calculations are correct. // Reset the holder so further calculations are correct.
set_holder(holder_orig); set_holder(holder_orig);
if (lost_holder_register) { if (lost_holder_register) {
...@@ -363,7 +370,8 @@ void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor( ...@@ -363,7 +370,8 @@ void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor(
Label miss; Label miss;
InterceptorVectorSlotPush(interceptor_reg); InterceptorVectorSlotPush(interceptor_reg);
Register reg = FrontendHeader(interceptor_reg, it->name(), &miss); Register reg =
FrontendHeader(interceptor_reg, it->name(), &miss, RETURN_HOLDER);
FrontendFooter(it->name(), &miss); FrontendFooter(it->name(), &miss);
// We discard the vector and slot now because we don't miss below this point. // We discard the vector and slot now because we don't miss below this point.
InterceptorVectorSlotPop(reg, DISCARD); InterceptorVectorSlotPop(reg, DISCARD);
...@@ -428,7 +436,7 @@ Handle<Code> NamedStoreHandlerCompiler::CompileStoreTransition( ...@@ -428,7 +436,7 @@ Handle<Code> NamedStoreHandlerCompiler::CompileStoreTransition(
if (!last.is_null()) set_holder(last); if (!last.is_null()) set_holder(last);
NonexistentFrontendHeader(name, &miss, scratch1(), scratch2()); NonexistentFrontendHeader(name, &miss, scratch1(), scratch2());
} else { } else {
FrontendHeader(receiver(), name, &miss); FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
DCHECK(holder()->HasFastProperties()); DCHECK(holder()->HasFastProperties());
} }
......
...@@ -14,6 +14,7 @@ namespace internal { ...@@ -14,6 +14,7 @@ namespace internal {
class CallOptimization; class CallOptimization;
enum PrototypeCheckType { CHECK_ALL_MAPS, SKIP_RECEIVER }; enum PrototypeCheckType { CHECK_ALL_MAPS, SKIP_RECEIVER };
enum ReturnHolder { RETURN_HOLDER, DONT_RETURN_ANYTHING };
class PropertyHandlerCompiler : public PropertyAccessCompiler { class PropertyHandlerCompiler : public PropertyAccessCompiler {
public: public:
...@@ -30,7 +31,7 @@ class PropertyHandlerCompiler : public PropertyAccessCompiler { ...@@ -30,7 +31,7 @@ class PropertyHandlerCompiler : public PropertyAccessCompiler {
virtual ~PropertyHandlerCompiler() {} virtual ~PropertyHandlerCompiler() {}
virtual Register FrontendHeader(Register object_reg, Handle<Name> name, virtual Register FrontendHeader(Register object_reg, Handle<Name> name,
Label* miss) { Label* miss, ReturnHolder return_what) {
UNREACHABLE(); UNREACHABLE();
return receiver(); return receiver();
} }
...@@ -95,7 +96,7 @@ class PropertyHandlerCompiler : public PropertyAccessCompiler { ...@@ -95,7 +96,7 @@ class PropertyHandlerCompiler : public PropertyAccessCompiler {
Register CheckPrototypes(Register object_reg, Register holder_reg, Register CheckPrototypes(Register object_reg, Register holder_reg,
Register scratch1, Register scratch2, Register scratch1, Register scratch2,
Handle<Name> name, Label* miss, Handle<Name> name, Label* miss,
PrototypeCheckType check = CHECK_ALL_MAPS); PrototypeCheckType check, ReturnHolder return_what);
Handle<Code> GetCode(Code::Kind kind, Code::StubType type, Handle<Name> name); Handle<Code> GetCode(Code::Kind kind, Code::StubType type, Handle<Name> name);
void set_holder(Handle<JSObject> holder) { holder_ = holder; } void set_holder(Handle<JSObject> holder) { holder_ = holder; }
...@@ -172,7 +173,7 @@ class NamedLoadHandlerCompiler : public PropertyHandlerCompiler { ...@@ -172,7 +173,7 @@ class NamedLoadHandlerCompiler : public PropertyHandlerCompiler {
protected: protected:
virtual Register FrontendHeader(Register object_reg, Handle<Name> name, virtual Register FrontendHeader(Register object_reg, Handle<Name> name,
Label* miss); Label* miss, ReturnHolder return_what);
virtual void FrontendFooter(Handle<Name> name, Label* miss); virtual void FrontendFooter(Handle<Name> name, Label* miss);
...@@ -246,7 +247,7 @@ class NamedStoreHandlerCompiler : public PropertyHandlerCompiler { ...@@ -246,7 +247,7 @@ class NamedStoreHandlerCompiler : public PropertyHandlerCompiler {
protected: protected:
virtual Register FrontendHeader(Register object_reg, Handle<Name> name, virtual Register FrontendHeader(Register object_reg, Handle<Name> name,
Label* miss); Label* miss, ReturnHolder return_what);
virtual void FrontendFooter(Handle<Name> name, Label* miss); virtual void FrontendFooter(Handle<Name> name, Label* miss);
void GenerateRestoreName(Label* label, Handle<Name> name); void GenerateRestoreName(Label* label, Handle<Name> name);
......
...@@ -414,8 +414,8 @@ void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type, ...@@ -414,8 +414,8 @@ void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type,
Register PropertyHandlerCompiler::CheckPrototypes( Register PropertyHandlerCompiler::CheckPrototypes(
Register object_reg, Register holder_reg, Register scratch1, Register object_reg, Register holder_reg, Register scratch1,
Register scratch2, Handle<Name> name, Label* miss, Register scratch2, Handle<Name> name, Label* miss, PrototypeCheckType check,
PrototypeCheckType check) { ReturnHolder return_what) {
Handle<Map> receiver_map = map(); Handle<Map> receiver_map = map();
// Make sure there's no overlap between holder and object registers. // Make sure there's no overlap between holder and object registers.
...@@ -423,6 +423,30 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -423,6 +423,30 @@ Register PropertyHandlerCompiler::CheckPrototypes(
DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) && DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) &&
!scratch2.is(scratch1)); !scratch2.is(scratch1));
if (FLAG_eliminate_prototype_chain_checks) {
Handle<Cell> validity_cell =
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
if (!validity_cell.is_null()) {
DCHECK_EQ(Smi::FromInt(Map::kPrototypeChainValid),
validity_cell->value());
// Operand::ForCell(...) points to the cell's payload!
__ cmp(Operand::ForCell(validity_cell),
Immediate(Smi::FromInt(Map::kPrototypeChainValid)));
__ j(not_equal, miss);
}
// The prototype chain of primitives (and their JSValue wrappers) depends
// on the native context, which can't be guarded by validity cells.
// |object_reg| holds the native context specific prototype in this case;
// we need to check its map.
if (check == CHECK_ALL_MAPS) {
__ mov(scratch1, FieldOperand(object_reg, HeapObject::kMapOffset));
Handle<WeakCell> cell = Map::WeakCellForMap(receiver_map);
__ CmpWeakValue(scratch1, cell, scratch2);
__ j(not_equal, miss);
}
}
// Keep track of the current object in register reg. // Keep track of the current object in register reg.
Register reg = object_reg; Register reg = object_reg;
int depth = 0; int depth = 0;
...@@ -467,28 +491,37 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -467,28 +491,37 @@ Register PropertyHandlerCompiler::CheckPrototypes(
current->property_dictionary()->FindEntry(name) == current->property_dictionary()->FindEntry(name) ==
NameDictionary::kNotFound); NameDictionary::kNotFound);
if (FLAG_eliminate_prototype_chain_checks && depth > 1) {
// TODO(jkummerow): Cache and re-use weak cell.
__ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss);
}
GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1, GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1,
scratch2); scratch2);
__ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); if (!FLAG_eliminate_prototype_chain_checks) {
reg = holder_reg; // From now on the object will be in holder_reg. __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
__ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); __ mov(holder_reg, FieldOperand(scratch1, Map::kPrototypeOffset));
}
} else { } else {
Register map_reg = scratch1; Register map_reg = scratch1;
__ mov(map_reg, FieldOperand(reg, HeapObject::kMapOffset)); if (!FLAG_eliminate_prototype_chain_checks) {
__ mov(map_reg, FieldOperand(reg, HeapObject::kMapOffset));
}
if (current_map->IsJSGlobalObjectMap()) { if (current_map->IsJSGlobalObjectMap()) {
GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current), GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
name, scratch2, miss); name, scratch2, miss);
} else if (depth != 1 || check == CHECK_ALL_MAPS) { } else if (!FLAG_eliminate_prototype_chain_checks &&
(depth != 1 || check == CHECK_ALL_MAPS)) {
Handle<WeakCell> cell = Map::WeakCellForMap(current_map); Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
__ CmpWeakValue(map_reg, cell, scratch2); __ CmpWeakValue(map_reg, cell, scratch2);
__ j(not_equal, miss); __ j(not_equal, miss);
} }
if (!FLAG_eliminate_prototype_chain_checks) {
reg = holder_reg; // From now on the object will be in holder_reg. __ mov(holder_reg, FieldOperand(map_reg, Map::kPrototypeOffset));
__ mov(reg, FieldOperand(map_reg, Map::kPrototypeOffset)); }
} }
reg = holder_reg; // From now on the object will be in holder_reg.
// Go to the next object in the prototype chain. // Go to the next object in the prototype chain.
current = prototype; current = prototype;
current_map = handle(current->map()); current_map = handle(current->map());
...@@ -499,7 +532,8 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -499,7 +532,8 @@ Register PropertyHandlerCompiler::CheckPrototypes(
// Log the check depth. // Log the check depth.
LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
if (depth != 0 || check == CHECK_ALL_MAPS) { if (!FLAG_eliminate_prototype_chain_checks &&
(depth != 0 || check == CHECK_ALL_MAPS)) {
// Check the holder map. // Check the holder map.
__ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
Handle<WeakCell> cell = Map::WeakCellForMap(current_map); Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
...@@ -507,8 +541,13 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -507,8 +541,13 @@ Register PropertyHandlerCompiler::CheckPrototypes(
__ j(not_equal, miss); __ j(not_equal, miss);
} }
bool return_holder = return_what == RETURN_HOLDER;
if (FLAG_eliminate_prototype_chain_checks && return_holder && depth != 0) {
__ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss);
}
// Return the register containing the holder. // Return the register containing the holder.
return reg; return return_holder ? reg : no_reg;
} }
...@@ -738,7 +777,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal( ...@@ -738,7 +777,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
if (IC::ICUseVector(kind())) { if (IC::ICUseVector(kind())) {
PushVectorAndSlot(); PushVectorAndSlot();
} }
FrontendHeader(receiver(), name, &miss); FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
// Get the value from the cell. // Get the value from the cell.
Register result = StoreDescriptor::ValueRegister(); Register result = StoreDescriptor::ValueRegister();
Handle<WeakCell> weak_cell = factory()->NewWeakCell(cell); Handle<WeakCell> weak_cell = factory()->NewWeakCell(cell);
......
...@@ -401,8 +401,8 @@ void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type, ...@@ -401,8 +401,8 @@ void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type,
Register PropertyHandlerCompiler::CheckPrototypes( Register PropertyHandlerCompiler::CheckPrototypes(
Register object_reg, Register holder_reg, Register scratch1, Register object_reg, Register holder_reg, Register scratch1,
Register scratch2, Handle<Name> name, Label* miss, Register scratch2, Handle<Name> name, Label* miss, PrototypeCheckType check,
PrototypeCheckType check) { ReturnHolder return_what) {
Handle<Map> receiver_map = map(); Handle<Map> receiver_map = map();
// Make sure there's no overlap between holder and object registers. // Make sure there's no overlap between holder and object registers.
...@@ -410,6 +410,30 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -410,6 +410,30 @@ Register PropertyHandlerCompiler::CheckPrototypes(
DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) && DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) &&
!scratch2.is(scratch1)); !scratch2.is(scratch1));
if (FLAG_eliminate_prototype_chain_checks) {
Handle<Cell> validity_cell =
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
if (!validity_cell.is_null()) {
DCHECK_EQ(Smi::FromInt(Map::kPrototypeChainValid),
validity_cell->value());
__ li(scratch1, Operand(validity_cell));
__ lw(scratch1, FieldMemOperand(scratch1, Cell::kValueOffset));
__ Branch(miss, ne, scratch1,
Operand(Smi::FromInt(Map::kPrototypeChainValid)));
}
// The prototype chain of primitives (and their JSValue wrappers) depends
// on the native context, which can't be guarded by validity cells.
// |object_reg| holds the native context specific prototype in this case;
// we need to check its map.
if (check == CHECK_ALL_MAPS) {
__ lw(scratch1, FieldMemOperand(object_reg, HeapObject::kMapOffset));
Handle<WeakCell> cell = Map::WeakCellForMap(receiver_map);
__ GetWeakValue(scratch2, cell);
__ Branch(miss, ne, scratch1, Operand(scratch2));
}
}
// Keep track of the current object in register reg. // Keep track of the current object in register reg.
Register reg = object_reg; Register reg = object_reg;
int depth = 0; int depth = 0;
...@@ -454,30 +478,36 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -454,30 +478,36 @@ Register PropertyHandlerCompiler::CheckPrototypes(
current->property_dictionary()->FindEntry(name) == current->property_dictionary()->FindEntry(name) ==
NameDictionary::kNotFound); NameDictionary::kNotFound);
if (FLAG_eliminate_prototype_chain_checks && depth > 1) {
// TODO(jkummerow): Cache and re-use weak cell.
__ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss);
}
GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1, GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1,
scratch2); scratch2);
if (!FLAG_eliminate_prototype_chain_checks) {
__ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); __ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
reg = holder_reg; // From now on the object will be in holder_reg. __ lw(holder_reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
__ lw(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset)); }
} else { } else {
Register map_reg = scratch1; Register map_reg = scratch1;
__ lw(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset)); if (!FLAG_eliminate_prototype_chain_checks) {
__ lw(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset));
}
if (current_map->IsJSGlobalObjectMap()) { if (current_map->IsJSGlobalObjectMap()) {
GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current), GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
name, scratch2, miss); name, scratch2, miss);
} else if (depth != 1 || check == CHECK_ALL_MAPS) { } else if (!FLAG_eliminate_prototype_chain_checks &&
(depth != 1 || check == CHECK_ALL_MAPS)) {
Handle<WeakCell> cell = Map::WeakCellForMap(current_map); Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
__ GetWeakValue(scratch2, cell); __ GetWeakValue(scratch2, cell);
__ Branch(miss, ne, scratch2, Operand(map_reg)); __ Branch(miss, ne, scratch2, Operand(map_reg));
} }
if (!FLAG_eliminate_prototype_chain_checks) {
reg = holder_reg; // From now on the object will be in holder_reg. __ lw(holder_reg, FieldMemOperand(map_reg, Map::kPrototypeOffset));
}
__ lw(reg, FieldMemOperand(map_reg, Map::kPrototypeOffset));
} }
reg = holder_reg; // From now on the object will be in holder_reg.
// Go to the next object in the prototype chain. // Go to the next object in the prototype chain.
current = prototype; current = prototype;
current_map = handle(current->map()); current_map = handle(current->map());
...@@ -488,7 +518,8 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -488,7 +518,8 @@ Register PropertyHandlerCompiler::CheckPrototypes(
// Log the check depth. // Log the check depth.
LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
if (depth != 0 || check == CHECK_ALL_MAPS) { if (!FLAG_eliminate_prototype_chain_checks &&
(depth != 0 || check == CHECK_ALL_MAPS)) {
// Check the holder map. // Check the holder map.
__ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); __ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
Handle<WeakCell> cell = Map::WeakCellForMap(current_map); Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
...@@ -496,8 +527,13 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -496,8 +527,13 @@ Register PropertyHandlerCompiler::CheckPrototypes(
__ Branch(miss, ne, scratch2, Operand(scratch1)); __ Branch(miss, ne, scratch2, Operand(scratch1));
} }
bool return_holder = return_what == RETURN_HOLDER;
if (FLAG_eliminate_prototype_chain_checks && return_holder && depth != 0) {
__ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss);
}
// Return the register containing the holder. // Return the register containing the holder.
return reg; return return_holder ? reg : no_reg;
} }
...@@ -712,7 +748,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal( ...@@ -712,7 +748,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
PushVectorAndSlot(); PushVectorAndSlot();
} }
FrontendHeader(receiver(), name, &miss); FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
// Get the value from the cell. // Get the value from the cell.
Register result = StoreDescriptor::ValueRegister(); Register result = StoreDescriptor::ValueRegister();
......
...@@ -402,8 +402,8 @@ void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type, ...@@ -402,8 +402,8 @@ void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type,
Register PropertyHandlerCompiler::CheckPrototypes( Register PropertyHandlerCompiler::CheckPrototypes(
Register object_reg, Register holder_reg, Register scratch1, Register object_reg, Register holder_reg, Register scratch1,
Register scratch2, Handle<Name> name, Label* miss, Register scratch2, Handle<Name> name, Label* miss, PrototypeCheckType check,
PrototypeCheckType check) { ReturnHolder return_what) {
Handle<Map> receiver_map = map(); Handle<Map> receiver_map = map();
// Make sure there's no overlap between holder and object registers. // Make sure there's no overlap between holder and object registers.
...@@ -411,6 +411,30 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -411,6 +411,30 @@ Register PropertyHandlerCompiler::CheckPrototypes(
DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) && DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) &&
!scratch2.is(scratch1)); !scratch2.is(scratch1));
if (FLAG_eliminate_prototype_chain_checks) {
Handle<Cell> validity_cell =
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
if (!validity_cell.is_null()) {
DCHECK_EQ(Smi::FromInt(Map::kPrototypeChainValid),
validity_cell->value());
__ li(scratch1, Operand(validity_cell));
__ ld(scratch1, FieldMemOperand(scratch1, Cell::kValueOffset));
__ Branch(miss, ne, scratch1,
Operand(Smi::FromInt(Map::kPrototypeChainValid)));
}
// The prototype chain of primitives (and their JSValue wrappers) depends
// on the native context, which can't be guarded by validity cells.
// |object_reg| holds the native context specific prototype in this case;
// we need to check its map.
if (check == CHECK_ALL_MAPS) {
__ ld(scratch1, FieldMemOperand(object_reg, HeapObject::kMapOffset));
Handle<WeakCell> cell = Map::WeakCellForMap(receiver_map);
__ GetWeakValue(scratch2, cell);
__ Branch(miss, ne, scratch1, Operand(scratch2));
}
}
// Keep track of the current object in register reg. // Keep track of the current object in register reg.
Register reg = object_reg; Register reg = object_reg;
int depth = 0; int depth = 0;
...@@ -455,30 +479,36 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -455,30 +479,36 @@ Register PropertyHandlerCompiler::CheckPrototypes(
current->property_dictionary()->FindEntry(name) == current->property_dictionary()->FindEntry(name) ==
NameDictionary::kNotFound); NameDictionary::kNotFound);
if (FLAG_eliminate_prototype_chain_checks && depth > 1) {
// TODO(jkummerow): Cache and re-use weak cell.
__ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss);
}
GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1, GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1,
scratch2); scratch2);
if (!FLAG_eliminate_prototype_chain_checks) {
__ ld(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); __ ld(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
reg = holder_reg; // From now on the object will be in holder_reg. __ ld(holder_reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
__ ld(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset)); }
} else { } else {
Register map_reg = scratch1; Register map_reg = scratch1;
__ ld(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset)); if (!FLAG_eliminate_prototype_chain_checks) {
__ ld(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset));
}
if (current_map->IsJSGlobalObjectMap()) { if (current_map->IsJSGlobalObjectMap()) {
GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current), GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
name, scratch2, miss); name, scratch2, miss);
} else if (depth != 1 || check == CHECK_ALL_MAPS) { } else if (!FLAG_eliminate_prototype_chain_checks &&
(depth != 1 || check == CHECK_ALL_MAPS)) {
Handle<WeakCell> cell = Map::WeakCellForMap(current_map); Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
__ GetWeakValue(scratch2, cell); __ GetWeakValue(scratch2, cell);
__ Branch(miss, ne, scratch2, Operand(map_reg)); __ Branch(miss, ne, scratch2, Operand(map_reg));
} }
if (!FLAG_eliminate_prototype_chain_checks) {
reg = holder_reg; // From now on the object will be in holder_reg. __ ld(holder_reg, FieldMemOperand(map_reg, Map::kPrototypeOffset));
}
__ ld(reg, FieldMemOperand(map_reg, Map::kPrototypeOffset));
} }
reg = holder_reg; // From now on the object will be in holder_reg.
// Go to the next object in the prototype chain. // Go to the next object in the prototype chain.
current = prototype; current = prototype;
current_map = handle(current->map()); current_map = handle(current->map());
...@@ -489,7 +519,8 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -489,7 +519,8 @@ Register PropertyHandlerCompiler::CheckPrototypes(
// Log the check depth. // Log the check depth.
LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
if (depth != 0 || check == CHECK_ALL_MAPS) { if (!FLAG_eliminate_prototype_chain_checks &&
(depth != 0 || check == CHECK_ALL_MAPS)) {
// Check the holder map. // Check the holder map.
__ ld(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); __ ld(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
Handle<WeakCell> cell = Map::WeakCellForMap(current_map); Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
...@@ -497,8 +528,13 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -497,8 +528,13 @@ Register PropertyHandlerCompiler::CheckPrototypes(
__ Branch(miss, ne, scratch2, Operand(scratch1)); __ Branch(miss, ne, scratch2, Operand(scratch1));
} }
bool return_holder = return_what == RETURN_HOLDER;
if (FLAG_eliminate_prototype_chain_checks && return_holder && depth != 0) {
__ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss);
}
// Return the register containing the holder. // Return the register containing the holder.
return reg; return return_holder ? reg : no_reg;
} }
...@@ -713,7 +749,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal( ...@@ -713,7 +749,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
PushVectorAndSlot(); PushVectorAndSlot();
} }
FrontendHeader(receiver(), name, &miss); FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
// Get the value from the cell. // Get the value from the cell.
Register result = StoreDescriptor::ValueRegister(); Register result = StoreDescriptor::ValueRegister();
......
...@@ -413,8 +413,8 @@ void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type, ...@@ -413,8 +413,8 @@ void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type,
Register PropertyHandlerCompiler::CheckPrototypes( Register PropertyHandlerCompiler::CheckPrototypes(
Register object_reg, Register holder_reg, Register scratch1, Register object_reg, Register holder_reg, Register scratch1,
Register scratch2, Handle<Name> name, Label* miss, Register scratch2, Handle<Name> name, Label* miss, PrototypeCheckType check,
PrototypeCheckType check) { ReturnHolder return_what) {
Handle<Map> receiver_map = map(); Handle<Map> receiver_map = map();
// Make sure there's no overlap between holder and object registers. // Make sure there's no overlap between holder and object registers.
...@@ -422,6 +422,31 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -422,6 +422,31 @@ Register PropertyHandlerCompiler::CheckPrototypes(
DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) && DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) &&
!scratch2.is(scratch1)); !scratch2.is(scratch1));
if (FLAG_eliminate_prototype_chain_checks) {
Handle<Cell> validity_cell =
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
if (!validity_cell.is_null()) {
DCHECK_EQ(Smi::FromInt(Map::kPrototypeChainValid),
validity_cell->value());
__ Move(scratch1, validity_cell, RelocInfo::CELL);
// Move(..., CELL) loads the payload's address!
__ SmiCompare(Operand(scratch1, 0),
Smi::FromInt(Map::kPrototypeChainValid));
__ j(not_equal, miss);
}
// The prototype chain of primitives (and their JSValue wrappers) depends
// on the native context, which can't be guarded by validity cells.
// |object_reg| holds the native context specific prototype in this case;
// we need to check its map.
if (check == CHECK_ALL_MAPS) {
__ movp(scratch1, FieldOperand(object_reg, HeapObject::kMapOffset));
Handle<WeakCell> cell = Map::WeakCellForMap(receiver_map);
__ CmpWeakValue(scratch1, cell, scratch2);
__ j(not_equal, miss);
}
}
// Keep track of the current object in register reg. On the first // Keep track of the current object in register reg. On the first
// iteration, reg is an alias for object_reg, on later iterations, // iteration, reg is an alias for object_reg, on later iterations,
// it is an alias for holder_reg. // it is an alias for holder_reg.
...@@ -468,30 +493,37 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -468,30 +493,37 @@ Register PropertyHandlerCompiler::CheckPrototypes(
current->property_dictionary()->FindEntry(name) == current->property_dictionary()->FindEntry(name) ==
NameDictionary::kNotFound); NameDictionary::kNotFound);
if (FLAG_eliminate_prototype_chain_checks && depth > 1) {
// TODO(jkummerow): Cache and re-use weak cell.
__ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss);
}
GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1, GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1,
scratch2); scratch2);
__ movp(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); if (!FLAG_eliminate_prototype_chain_checks) {
reg = holder_reg; // From now on the object will be in holder_reg. __ movp(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
__ movp(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); __ movp(holder_reg, FieldOperand(scratch1, Map::kPrototypeOffset));
}
} else { } else {
Register map_reg = scratch1; Register map_reg = scratch1;
__ movp(map_reg, FieldOperand(reg, HeapObject::kMapOffset)); if (!FLAG_eliminate_prototype_chain_checks) {
__ movp(map_reg, FieldOperand(reg, HeapObject::kMapOffset));
}
if (current_map->IsJSGlobalObjectMap()) { if (current_map->IsJSGlobalObjectMap()) {
GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current), GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
name, scratch2, miss); name, scratch2, miss);
} else if (depth != 1 || check == CHECK_ALL_MAPS) { } else if (!FLAG_eliminate_prototype_chain_checks &&
(depth != 1 || check == CHECK_ALL_MAPS)) {
Handle<WeakCell> cell = Map::WeakCellForMap(current_map); Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
__ CmpWeakValue(map_reg, cell, scratch2); __ CmpWeakValue(map_reg, cell, scratch2);
__ j(not_equal, miss); __ j(not_equal, miss);
} }
if (!FLAG_eliminate_prototype_chain_checks) {
reg = holder_reg; // From now on the object will be in holder_reg. __ movp(holder_reg, FieldOperand(map_reg, Map::kPrototypeOffset));
}
__ movp(reg, FieldOperand(map_reg, Map::kPrototypeOffset));
} }
reg = holder_reg; // From now on the object will be in holder_reg.
// Go to the next object in the prototype chain. // Go to the next object in the prototype chain.
current = prototype; current = prototype;
current_map = handle(current->map()); current_map = handle(current->map());
...@@ -502,15 +534,22 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -502,15 +534,22 @@ Register PropertyHandlerCompiler::CheckPrototypes(
// Log the check depth. // Log the check depth.
LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
if (depth != 0 || check == CHECK_ALL_MAPS) { if (!FLAG_eliminate_prototype_chain_checks &&
(depth != 0 || check == CHECK_ALL_MAPS)) {
// Check the holder map.
__ movp(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); __ movp(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
Handle<WeakCell> cell = Map::WeakCellForMap(current_map); Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
__ CmpWeakValue(scratch1, cell, scratch2); __ CmpWeakValue(scratch1, cell, scratch2);
__ j(not_equal, miss); __ j(not_equal, miss);
} }
bool return_holder = return_what == RETURN_HOLDER;
if (FLAG_eliminate_prototype_chain_checks && return_holder && depth != 0) {
__ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss);
}
// Return the register containing the holder. // Return the register containing the holder.
return reg; return return_holder ? reg : no_reg;
} }
...@@ -732,7 +771,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal( ...@@ -732,7 +771,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
if (IC::ICUseVector(kind())) { if (IC::ICUseVector(kind())) {
PushVectorAndSlot(); PushVectorAndSlot();
} }
FrontendHeader(receiver(), name, &miss); FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
// Get the value from the cell. // Get the value from the cell.
Register result = StoreDescriptor::ValueRegister(); Register result = StoreDescriptor::ValueRegister();
......
...@@ -1905,6 +1905,9 @@ void Map::ConnectElementsTransition(Handle<Map> parent, Handle<Map> child) { ...@@ -1905,6 +1905,9 @@ void Map::ConnectElementsTransition(Handle<Map> parent, Handle<Map> child) {
void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map, void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
int expected_additional_properties) { int expected_additional_properties) {
if (object->map() == *new_map) return; if (object->map() == *new_map) return;
// If this object is a prototype (the callee will check), invalidate any
// prototype chains involving it.
InvalidatePrototypeChains(object->map());
Handle<Map> old_map(object->map()); Handle<Map> old_map(object->map());
if (object->HasFastProperties()) { if (object->HasFastProperties()) {
if (!new_map->is_dictionary_map()) { if (!new_map->is_dictionary_map()) {
...@@ -1932,6 +1935,8 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map, ...@@ -1932,6 +1935,8 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
object->set_map(*new_map); object->set_map(*new_map);
} }
if (old_map->is_prototype_map()) { if (old_map->is_prototype_map()) {
DCHECK(new_map->is_prototype_map());
DCHECK(object->map() == *new_map);
new_map->set_prototype_info(old_map->prototype_info()); new_map->set_prototype_info(old_map->prototype_info());
old_map->set_prototype_info(Smi::FromInt(0)); old_map->set_prototype_info(Smi::FromInt(0));
} }
...@@ -4710,6 +4715,12 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object, ...@@ -4710,6 +4715,12 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object,
Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map())); Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map()));
new_map->set_dictionary_map(false); new_map->set_dictionary_map(false);
if (object->map()->is_prototype_map()) {
DCHECK(new_map->is_prototype_map());
new_map->set_prototype_info(object->map()->prototype_info());
object->map()->set_prototype_info(Smi::FromInt(0));
}
#if TRACE_MAPS #if TRACE_MAPS
if (FLAG_trace_maps) { if (FLAG_trace_maps) {
PrintF("[TraceMaps: SlowToFast from= %p to= %p reason= %s ]\n", PrintF("[TraceMaps: SlowToFast from= %p to= %p reason= %s ]\n",
...@@ -6895,9 +6906,20 @@ Handle<Map> Map::Normalize(Handle<Map> fast_map, PropertyNormalizationMode mode, ...@@ -6895,9 +6906,20 @@ Handle<Map> Map::Normalize(Handle<Map> fast_map, PropertyNormalizationMode mode,
// applied to the shared map, dependent code and weak cell cache. // applied to the shared map, dependent code and weak cell cache.
Handle<Map> fresh = Map::CopyNormalized(fast_map, mode); Handle<Map> fresh = Map::CopyNormalized(fast_map, mode);
DCHECK(memcmp(fresh->address(), if (new_map->is_prototype_map()) {
new_map->address(), // For prototype maps, the PrototypeInfo is not copied.
Map::kCodeCacheOffset) == 0); DCHECK(memcmp(fresh->address(), new_map->address(),
kTransitionsOrPrototypeInfoOffset) == 0);
DCHECK(fresh->raw_transitions() == Smi::FromInt(0));
STATIC_ASSERT(kDescriptorsOffset ==
kTransitionsOrPrototypeInfoOffset + kPointerSize);
DCHECK(memcmp(HeapObject::RawField(*fresh, kDescriptorsOffset),
HeapObject::RawField(*new_map, kDescriptorsOffset),
kCodeCacheOffset - kDescriptorsOffset) == 0);
} else {
DCHECK(memcmp(fresh->address(), new_map->address(),
Map::kCodeCacheOffset) == 0);
}
STATIC_ASSERT(Map::kDependentCodeOffset == STATIC_ASSERT(Map::kDependentCodeOffset ==
Map::kCodeCacheOffset + kPointerSize); Map::kCodeCacheOffset + kPointerSize);
STATIC_ASSERT(Map::kWeakCellCacheOffset == STATIC_ASSERT(Map::kWeakCellCacheOffset ==
...@@ -8228,8 +8250,8 @@ Handle<WeakFixedArray> WeakFixedArray::Add( ...@@ -8228,8 +8250,8 @@ Handle<WeakFixedArray> WeakFixedArray::Add(
for (int i = 0; i < array->Length(); ++i) { for (int i = 0; i < array->Length(); ++i) {
if (array->Get(i) == *value) return array; if (array->Get(i) == *value) return array;
} }
#if 0 // Enable this if you want to check your search_for_duplicates flags.
} else { } else {
#ifdef DEBUG
for (int i = 0; i < array->Length(); ++i) { for (int i = 0; i < array->Length(); ++i) {
DCHECK_NE(*value, array->Get(i)); DCHECK_NE(*value, array->Get(i));
} }
...@@ -10049,7 +10071,74 @@ void JSObject::UnregisterPrototypeUser(Handle<JSObject> prototype, ...@@ -10049,7 +10071,74 @@ void JSObject::UnregisterPrototypeUser(Handle<JSObject> prototype,
} }
static void InvalidatePrototypeChainsInternal(Map* map) {
if (!map->is_prototype_map()) return;
Object* maybe_proto_info = map->prototype_info();
if (!maybe_proto_info->IsPrototypeInfo()) return;
PrototypeInfo* proto_info = PrototypeInfo::cast(maybe_proto_info);
Object* maybe_cell = proto_info->validity_cell();
if (maybe_cell->IsCell()) {
// Just set the value; the cell will be replaced lazily.
Cell* cell = Cell::cast(maybe_cell);
cell->set_value(Smi::FromInt(Map::kPrototypeChainInvalid));
}
Object* maybe_array = proto_info->prototype_users();
if (!maybe_array->IsWeakFixedArray()) return;
WeakFixedArray* users = WeakFixedArray::cast(maybe_array);
for (int i = 0; i < users->Length(); ++i) {
Object* maybe_user = users->Get(i);
if (maybe_user->IsSmi()) continue;
// For now, only maps register themselves as users.
Map* user = Map::cast(maybe_user);
// Walk the prototype chain (backwards, towards leaf objects) if necessary.
InvalidatePrototypeChainsInternal(user);
}
}
// static
void JSObject::InvalidatePrototypeChains(Map* map) {
if (!FLAG_eliminate_prototype_chain_checks) return;
DisallowHeapAllocation no_gc;
if (map->IsJSGlobalProxyMap()) {
PrototypeIterator iter(map);
map = JSObject::cast(iter.GetCurrent())->map();
}
InvalidatePrototypeChainsInternal(map);
}
// static // static
Handle<Cell> Map::GetOrCreatePrototypeChainValidityCell(Handle<Map> map,
Isolate* isolate) {
Handle<Object> maybe_prototype(map->prototype(), isolate);
if (!maybe_prototype->IsJSObject()) return Handle<Cell>::null();
Handle<JSObject> prototype = Handle<JSObject>::cast(maybe_prototype);
if (prototype->IsJSGlobalProxy()) {
PrototypeIterator iter(isolate, prototype);
prototype = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
}
PrototypeInfo* proto_info =
PrototypeInfo::cast(prototype->map()->prototype_info());
Object* maybe_cell = proto_info->validity_cell();
// Return existing cell if it's still valid.
if (maybe_cell->IsCell()) {
Handle<Cell> cell(Cell::cast(maybe_cell), isolate);
if (cell->value() == Smi::FromInt(Map::kPrototypeChainValid)) {
return handle(Cell::cast(maybe_cell), isolate);
}
}
// Otherwise create a new cell.
Handle<Cell> cell = isolate->factory()->NewCell(
handle(Smi::FromInt(Map::kPrototypeChainValid), isolate));
proto_info->set_validity_cell(*cell);
return cell;
}
void Map::SetPrototype(Handle<Map> map, Handle<Object> prototype, void Map::SetPrototype(Handle<Map> map, Handle<Object> prototype,
PrototypeOptimizationMode proto_mode) { PrototypeOptimizationMode proto_mode) {
if (map->prototype()->IsJSObject() && FLAG_track_prototype_users) { if (map->prototype()->IsJSObject() && FLAG_track_prototype_users) {
...@@ -10058,10 +10147,10 @@ void Map::SetPrototype(Handle<Map> map, Handle<Object> prototype, ...@@ -10058,10 +10147,10 @@ void Map::SetPrototype(Handle<Map> map, Handle<Object> prototype,
} }
if (prototype->IsJSObject()) { if (prototype->IsJSObject()) {
Handle<JSObject> prototype_jsobj = Handle<JSObject>::cast(prototype); Handle<JSObject> prototype_jsobj = Handle<JSObject>::cast(prototype);
JSObject::OptimizeAsPrototype(prototype_jsobj, proto_mode);
if (ShouldRegisterAsPrototypeUser(map, prototype_jsobj)) { if (ShouldRegisterAsPrototypeUser(map, prototype_jsobj)) {
JSObject::RegisterPrototypeUser(prototype_jsobj, map); JSObject::RegisterPrototypeUser(prototype_jsobj, map);
} }
JSObject::OptimizeAsPrototype(prototype_jsobj, proto_mode);
} }
WriteBarrierMode wb_mode = WriteBarrierMode wb_mode =
prototype->IsNull() ? SKIP_WRITE_BARRIER : UPDATE_WRITE_BARRIER; prototype->IsNull() ? SKIP_WRITE_BARRIER : UPDATE_WRITE_BARRIER;
......
...@@ -1832,6 +1832,7 @@ class JSObject: public JSReceiver { ...@@ -1832,6 +1832,7 @@ class JSObject: public JSReceiver {
Handle<HeapObject> user); Handle<HeapObject> user);
static void UnregisterPrototypeUser(Handle<JSObject> prototype, static void UnregisterPrototypeUser(Handle<JSObject> prototype,
Handle<HeapObject> user); Handle<HeapObject> user);
static void InvalidatePrototypeChains(Map* map);
// Retrieve interceptors. // Retrieve interceptors.
InterceptorInfo* GetNamedInterceptor(); InterceptorInfo* GetNamedInterceptor();
...@@ -6028,6 +6029,15 @@ class Map: public HeapObject { ...@@ -6028,6 +6029,15 @@ class Map: public HeapObject {
// (which prototype maps don't have). // (which prototype maps don't have).
DECL_ACCESSORS(prototype_info, Object) DECL_ACCESSORS(prototype_info, Object)
// [prototype chain validity cell]: Associated with a prototype object,
// stored in that object's map's PrototypeInfo, indicates that prototype
// chains through this object are currently valid. The cell will be
// invalidated and replaced when the prototype chain changes.
static Handle<Cell> GetOrCreatePrototypeChainValidityCell(Handle<Map> map,
Isolate* isolate);
static const int kPrototypeChainValid = 0;
static const int kPrototypeChainInvalid = 1;
Map* FindRootMap(); Map* FindRootMap();
Map* FindFieldOwner(int descriptor); Map* FindFieldOwner(int descriptor);
......
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