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,
DEFINE_BOOL(trace_weak_arrays, false, "trace WeakFixedArray usage")
DEFINE_BOOL(track_prototype_users, false,
"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")
#if TRACE_MAPS
DEFINE_BOOL(trace_maps, false, "trace map creation")
......
......@@ -411,8 +411,8 @@ void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type,
Register PropertyHandlerCompiler::CheckPrototypes(
Register object_reg, Register holder_reg, Register scratch1,
Register scratch2, Handle<Name> name, Label* miss,
PrototypeCheckType check) {
Register scratch2, Handle<Name> name, Label* miss, PrototypeCheckType check,
ReturnHolder return_what) {
Handle<Map> receiver_map = map();
// Make sure there's no overlap between holder and object registers.
......@@ -420,6 +420,30 @@ Register PropertyHandlerCompiler::CheckPrototypes(
DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) &&
!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.
Register reg = object_reg;
int depth = 0;
......@@ -464,30 +488,36 @@ Register PropertyHandlerCompiler::CheckPrototypes(
current->property_dictionary()->FindEntry(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,
scratch2);
__ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
reg = holder_reg; // From now on the object will be in holder_reg.
__ ldr(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
if (!FLAG_eliminate_prototype_chain_checks) {
__ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
__ ldr(holder_reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
}
} else {
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()) {
GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
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);
__ CmpWeakValue(map_reg, cell, scratch2);
__ b(ne, miss);
}
reg = holder_reg; // From now on the object will be in holder_reg.
__ ldr(reg, FieldMemOperand(map_reg, Map::kPrototypeOffset));
if (!FLAG_eliminate_prototype_chain_checks) {
__ ldr(holder_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.
current = prototype;
current_map = handle(current->map());
......@@ -498,7 +528,8 @@ Register PropertyHandlerCompiler::CheckPrototypes(
// Log the check depth.
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.
__ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
......@@ -506,8 +537,13 @@ Register PropertyHandlerCompiler::CheckPrototypes(
__ 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 reg;
return return_holder ? reg : no_reg;
}
......@@ -722,7 +758,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
if (IC::ICUseVector(kind())) {
PushVectorAndSlot();
}
FrontendHeader(receiver(), name, &miss);
FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
// Get the value from the cell.
Register result = StoreDescriptor::ValueRegister();
......
......@@ -339,7 +339,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
if (IC::ICUseVector(kind())) {
PushVectorAndSlot();
}
FrontendHeader(receiver(), name, &miss);
FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
// Get the value from the cell.
Register result = StoreDescriptor::ValueRegister();
......@@ -461,14 +461,38 @@ void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type,
Register PropertyHandlerCompiler::CheckPrototypes(
Register object_reg, Register holder_reg, Register scratch1,
Register scratch2, Handle<Name> name, Label* miss,
PrototypeCheckType check) {
Register scratch2, Handle<Name> name, Label* miss, PrototypeCheckType check,
ReturnHolder return_what) {
Handle<Map> receiver_map = map();
// object_reg and holder_reg registers can alias.
DCHECK(!AreAliased(object_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.
Register reg = object_reg;
int depth = 0;
......@@ -513,30 +537,37 @@ Register PropertyHandlerCompiler::CheckPrototypes(
DCHECK(current.is_null() || (current->property_dictionary()->FindEntry(
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,
scratch2);
__ Ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
reg = holder_reg; // From now on the object will be in holder_reg.
__ Ldr(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
if (!FLAG_eliminate_prototype_chain_checks) {
__ Ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
__ Ldr(holder_reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
}
} else {
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()) {
GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
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);
__ CmpWeakValue(map_reg, cell, scratch2);
__ B(ne, miss);
}
reg = holder_reg; // From now on the object will be in holder_reg.
__ Ldr(reg, FieldMemOperand(map_reg, Map::kPrototypeOffset));
if (!FLAG_eliminate_prototype_chain_checks) {
__ Ldr(holder_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.
current = prototype;
current_map = handle(current->map());
......@@ -547,8 +578,8 @@ Register PropertyHandlerCompiler::CheckPrototypes(
// Log the check depth.
LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
// Check the holder map.
if (depth != 0 || check == CHECK_ALL_MAPS) {
if (!FLAG_eliminate_prototype_chain_checks &&
(depth != 0 || check == CHECK_ALL_MAPS)) {
// Check the holder map.
__ Ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
......@@ -556,8 +587,13 @@ Register PropertyHandlerCompiler::CheckPrototypes(
__ 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 reg;
return return_holder ? reg : no_reg;
}
......
......@@ -87,7 +87,8 @@ Handle<Code> PropertyHandlerCompiler::GetCode(Code::Kind kind,
Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg,
Handle<Name> name,
Label* miss) {
Label* miss,
ReturnHolder return_what) {
PrototypeCheckType check_type = CHECK_ALL_MAPS;
int function_index = -1;
if (map()->instance_type() < FIRST_NONSTRING_TYPE) {
......@@ -114,7 +115,7 @@ Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg,
// Check that the maps starting from the prototype haven't changed.
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,
// miss.
Register NamedStoreHandlerCompiler::FrontendHeader(Register object_reg,
Handle<Name> name,
Label* miss) {
Label* miss,
ReturnHolder return_what) {
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) {
if (IC::ICUseVector(kind())) {
PushVectorAndSlot();
}
Register reg = FrontendHeader(receiver(), name, &miss);
Register reg = FrontendHeader(receiver(), name, &miss, RETURN_HOLDER);
FrontendFooter(name, &miss);
// The footer consumes the vector and slot from the stack if miss occurs.
if (IC::ICUseVector(kind())) {
......@@ -156,8 +158,13 @@ void PropertyHandlerCompiler::NonexistentFrontendHeader(Handle<Name> name,
// Handle<JSObject>::null().
DCHECK(last_map->prototype() == isolate()->heap()->null_value());
} else {
holder_reg = FrontendHeader(receiver(), name, miss);
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()) {
......@@ -328,7 +335,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor(
auto last_handle = handle(last);
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.
set_holder(holder_orig);
if (lost_holder_register) {
......@@ -363,7 +370,8 @@ void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor(
Label miss;
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);
// We discard the vector and slot now because we don't miss below this point.
InterceptorVectorSlotPop(reg, DISCARD);
......@@ -428,7 +436,7 @@ Handle<Code> NamedStoreHandlerCompiler::CompileStoreTransition(
if (!last.is_null()) set_holder(last);
NonexistentFrontendHeader(name, &miss, scratch1(), scratch2());
} else {
FrontendHeader(receiver(), name, &miss);
FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
DCHECK(holder()->HasFastProperties());
}
......
......@@ -14,6 +14,7 @@ namespace internal {
class CallOptimization;
enum PrototypeCheckType { CHECK_ALL_MAPS, SKIP_RECEIVER };
enum ReturnHolder { RETURN_HOLDER, DONT_RETURN_ANYTHING };
class PropertyHandlerCompiler : public PropertyAccessCompiler {
public:
......@@ -30,7 +31,7 @@ class PropertyHandlerCompiler : public PropertyAccessCompiler {
virtual ~PropertyHandlerCompiler() {}
virtual Register FrontendHeader(Register object_reg, Handle<Name> name,
Label* miss) {
Label* miss, ReturnHolder return_what) {
UNREACHABLE();
return receiver();
}
......@@ -95,7 +96,7 @@ class PropertyHandlerCompiler : public PropertyAccessCompiler {
Register CheckPrototypes(Register object_reg, Register holder_reg,
Register scratch1, Register scratch2,
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);
void set_holder(Handle<JSObject> holder) { holder_ = holder; }
......@@ -172,7 +173,7 @@ class NamedLoadHandlerCompiler : public PropertyHandlerCompiler {
protected:
virtual Register FrontendHeader(Register object_reg, Handle<Name> name,
Label* miss);
Label* miss, ReturnHolder return_what);
virtual void FrontendFooter(Handle<Name> name, Label* miss);
......@@ -246,7 +247,7 @@ class NamedStoreHandlerCompiler : public PropertyHandlerCompiler {
protected:
virtual Register FrontendHeader(Register object_reg, Handle<Name> name,
Label* miss);
Label* miss, ReturnHolder return_what);
virtual void FrontendFooter(Handle<Name> name, Label* miss);
void GenerateRestoreName(Label* label, Handle<Name> name);
......
......@@ -414,8 +414,8 @@ void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type,
Register PropertyHandlerCompiler::CheckPrototypes(
Register object_reg, Register holder_reg, Register scratch1,
Register scratch2, Handle<Name> name, Label* miss,
PrototypeCheckType check) {
Register scratch2, Handle<Name> name, Label* miss, PrototypeCheckType check,
ReturnHolder return_what) {
Handle<Map> receiver_map = map();
// Make sure there's no overlap between holder and object registers.
......@@ -423,6 +423,30 @@ Register PropertyHandlerCompiler::CheckPrototypes(
DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) &&
!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.
Register reg = object_reg;
int depth = 0;
......@@ -467,28 +491,37 @@ Register PropertyHandlerCompiler::CheckPrototypes(
current->property_dictionary()->FindEntry(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,
scratch2);
__ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
reg = holder_reg; // From now on the object will be in holder_reg.
__ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset));
if (!FLAG_eliminate_prototype_chain_checks) {
__ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
__ mov(holder_reg, FieldOperand(scratch1, Map::kPrototypeOffset));
}
} else {
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()) {
GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
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);
__ CmpWeakValue(map_reg, cell, scratch2);
__ j(not_equal, miss);
}
reg = holder_reg; // From now on the object will be in holder_reg.
__ mov(reg, FieldOperand(map_reg, Map::kPrototypeOffset));
if (!FLAG_eliminate_prototype_chain_checks) {
__ mov(holder_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.
current = prototype;
current_map = handle(current->map());
......@@ -499,7 +532,8 @@ Register PropertyHandlerCompiler::CheckPrototypes(
// Log the check depth.
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.
__ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
......@@ -507,8 +541,13 @@ Register PropertyHandlerCompiler::CheckPrototypes(
__ 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 reg;
return return_holder ? reg : no_reg;
}
......@@ -738,7 +777,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
if (IC::ICUseVector(kind())) {
PushVectorAndSlot();
}
FrontendHeader(receiver(), name, &miss);
FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
// Get the value from the cell.
Register result = StoreDescriptor::ValueRegister();
Handle<WeakCell> weak_cell = factory()->NewWeakCell(cell);
......
......@@ -401,8 +401,8 @@ void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type,
Register PropertyHandlerCompiler::CheckPrototypes(
Register object_reg, Register holder_reg, Register scratch1,
Register scratch2, Handle<Name> name, Label* miss,
PrototypeCheckType check) {
Register scratch2, Handle<Name> name, Label* miss, PrototypeCheckType check,
ReturnHolder return_what) {
Handle<Map> receiver_map = map();
// Make sure there's no overlap between holder and object registers.
......@@ -410,6 +410,30 @@ Register PropertyHandlerCompiler::CheckPrototypes(
DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) &&
!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.
Register reg = object_reg;
int depth = 0;
......@@ -454,30 +478,36 @@ Register PropertyHandlerCompiler::CheckPrototypes(
current->property_dictionary()->FindEntry(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,
scratch2);
__ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
reg = holder_reg; // From now on the object will be in holder_reg.
__ lw(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
if (!FLAG_eliminate_prototype_chain_checks) {
__ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
__ lw(holder_reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
}
} else {
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()) {
GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
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);
__ GetWeakValue(scratch2, cell);
__ Branch(miss, ne, scratch2, Operand(map_reg));
}
reg = holder_reg; // From now on the object will be in holder_reg.
__ lw(reg, FieldMemOperand(map_reg, Map::kPrototypeOffset));
if (!FLAG_eliminate_prototype_chain_checks) {
__ lw(holder_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.
current = prototype;
current_map = handle(current->map());
......@@ -488,7 +518,8 @@ Register PropertyHandlerCompiler::CheckPrototypes(
// Log the check depth.
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.
__ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
......@@ -496,8 +527,13 @@ Register PropertyHandlerCompiler::CheckPrototypes(
__ 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 reg;
return return_holder ? reg : no_reg;
}
......@@ -712,7 +748,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
PushVectorAndSlot();
}
FrontendHeader(receiver(), name, &miss);
FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
// Get the value from the cell.
Register result = StoreDescriptor::ValueRegister();
......
......@@ -402,8 +402,8 @@ void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type,
Register PropertyHandlerCompiler::CheckPrototypes(
Register object_reg, Register holder_reg, Register scratch1,
Register scratch2, Handle<Name> name, Label* miss,
PrototypeCheckType check) {
Register scratch2, Handle<Name> name, Label* miss, PrototypeCheckType check,
ReturnHolder return_what) {
Handle<Map> receiver_map = map();
// Make sure there's no overlap between holder and object registers.
......@@ -411,6 +411,30 @@ Register PropertyHandlerCompiler::CheckPrototypes(
DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) &&
!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.
Register reg = object_reg;
int depth = 0;
......@@ -455,30 +479,36 @@ Register PropertyHandlerCompiler::CheckPrototypes(
current->property_dictionary()->FindEntry(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,
scratch2);
__ ld(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
reg = holder_reg; // From now on the object will be in holder_reg.
__ ld(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
if (!FLAG_eliminate_prototype_chain_checks) {
__ ld(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
__ ld(holder_reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
}
} else {
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()) {
GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
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);
__ GetWeakValue(scratch2, cell);
__ Branch(miss, ne, scratch2, Operand(map_reg));
}
reg = holder_reg; // From now on the object will be in holder_reg.
__ ld(reg, FieldMemOperand(map_reg, Map::kPrototypeOffset));
if (!FLAG_eliminate_prototype_chain_checks) {
__ ld(holder_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.
current = prototype;
current_map = handle(current->map());
......@@ -489,7 +519,8 @@ Register PropertyHandlerCompiler::CheckPrototypes(
// Log the check depth.
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.
__ ld(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
......@@ -497,8 +528,13 @@ Register PropertyHandlerCompiler::CheckPrototypes(
__ 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 reg;
return return_holder ? reg : no_reg;
}
......@@ -713,7 +749,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
PushVectorAndSlot();
}
FrontendHeader(receiver(), name, &miss);
FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
// Get the value from the cell.
Register result = StoreDescriptor::ValueRegister();
......
......@@ -413,8 +413,8 @@ void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type,
Register PropertyHandlerCompiler::CheckPrototypes(
Register object_reg, Register holder_reg, Register scratch1,
Register scratch2, Handle<Name> name, Label* miss,
PrototypeCheckType check) {
Register scratch2, Handle<Name> name, Label* miss, PrototypeCheckType check,
ReturnHolder return_what) {
Handle<Map> receiver_map = map();
// Make sure there's no overlap between holder and object registers.
......@@ -422,6 +422,31 @@ Register PropertyHandlerCompiler::CheckPrototypes(
DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) &&
!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
// iteration, reg is an alias for object_reg, on later iterations,
// it is an alias for holder_reg.
......@@ -468,30 +493,37 @@ Register PropertyHandlerCompiler::CheckPrototypes(
current->property_dictionary()->FindEntry(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,
scratch2);
__ movp(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
reg = holder_reg; // From now on the object will be in holder_reg.
__ movp(reg, FieldOperand(scratch1, Map::kPrototypeOffset));
if (!FLAG_eliminate_prototype_chain_checks) {
__ movp(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
__ movp(holder_reg, FieldOperand(scratch1, Map::kPrototypeOffset));
}
} else {
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()) {
GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
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);
__ CmpWeakValue(map_reg, cell, scratch2);
__ j(not_equal, miss);
}
reg = holder_reg; // From now on the object will be in holder_reg.
__ movp(reg, FieldOperand(map_reg, Map::kPrototypeOffset));
if (!FLAG_eliminate_prototype_chain_checks) {
__ movp(holder_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.
current = prototype;
current_map = handle(current->map());
......@@ -502,15 +534,22 @@ Register PropertyHandlerCompiler::CheckPrototypes(
// Log the check depth.
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));
Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
__ CmpWeakValue(scratch1, cell, scratch2);
__ 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 reg;
return return_holder ? reg : no_reg;
}
......@@ -732,7 +771,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
if (IC::ICUseVector(kind())) {
PushVectorAndSlot();
}
FrontendHeader(receiver(), name, &miss);
FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
// Get the value from the cell.
Register result = StoreDescriptor::ValueRegister();
......
......@@ -1905,6 +1905,9 @@ void Map::ConnectElementsTransition(Handle<Map> parent, Handle<Map> child) {
void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
int expected_additional_properties) {
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());
if (object->HasFastProperties()) {
if (!new_map->is_dictionary_map()) {
......@@ -1932,6 +1935,8 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
object->set_map(*new_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());
old_map->set_prototype_info(Smi::FromInt(0));
}
......@@ -4710,6 +4715,12 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object,
Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map()));
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 (FLAG_trace_maps) {
PrintF("[TraceMaps: SlowToFast from= %p to= %p reason= %s ]\n",
......@@ -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.
Handle<Map> fresh = Map::CopyNormalized(fast_map, mode);
DCHECK(memcmp(fresh->address(),
new_map->address(),
Map::kCodeCacheOffset) == 0);
if (new_map->is_prototype_map()) {
// For prototype maps, the PrototypeInfo is not copied.
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 ==
Map::kCodeCacheOffset + kPointerSize);
STATIC_ASSERT(Map::kWeakCellCacheOffset ==
......@@ -8228,8 +8250,8 @@ Handle<WeakFixedArray> WeakFixedArray::Add(
for (int i = 0; i < array->Length(); ++i) {
if (array->Get(i) == *value) return array;
}
#if 0 // Enable this if you want to check your search_for_duplicates flags.
} else {
#ifdef DEBUG
for (int i = 0; i < array->Length(); ++i) {
DCHECK_NE(*value, array->Get(i));
}
......@@ -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
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,
PrototypeOptimizationMode proto_mode) {
if (map->prototype()->IsJSObject() && FLAG_track_prototype_users) {
......@@ -10058,10 +10147,10 @@ void Map::SetPrototype(Handle<Map> map, Handle<Object> prototype,
}
if (prototype->IsJSObject()) {
Handle<JSObject> prototype_jsobj = Handle<JSObject>::cast(prototype);
JSObject::OptimizeAsPrototype(prototype_jsobj, proto_mode);
if (ShouldRegisterAsPrototypeUser(map, prototype_jsobj)) {
JSObject::RegisterPrototypeUser(prototype_jsobj, map);
}
JSObject::OptimizeAsPrototype(prototype_jsobj, proto_mode);
}
WriteBarrierMode wb_mode =
prototype->IsNull() ? SKIP_WRITE_BARRIER : UPDATE_WRITE_BARRIER;
......
......@@ -1832,6 +1832,7 @@ class JSObject: public JSReceiver {
Handle<HeapObject> user);
static void UnregisterPrototypeUser(Handle<JSObject> prototype,
Handle<HeapObject> user);
static void InvalidatePrototypeChains(Map* map);
// Retrieve interceptors.
InterceptorInfo* GetNamedInterceptor();
......@@ -6028,6 +6029,15 @@ class Map: public HeapObject {
// (which prototype maps don't have).
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* 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