Do not inline store if field map was cleared.
BUG=v8:4023 LOG=NO Review URL: https://codereview.chromium.org/1081033004 Cr-Commit-Position: refs/heads/master@{#27779}
This commit is contained in:
parent
d93a0029dc
commit
2f327a5cb4
@ -6014,7 +6014,7 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::LoadResult(Handle<Map> map) {
|
|||||||
access_ = HObjectAccess::ForField(map, index, representation(), name_);
|
access_ = HObjectAccess::ForField(map, index, representation(), name_);
|
||||||
|
|
||||||
// Load field map for heap objects.
|
// Load field map for heap objects.
|
||||||
LoadFieldMaps(map);
|
return LoadFieldMaps(map);
|
||||||
} else if (IsAccessorConstant()) {
|
} else if (IsAccessorConstant()) {
|
||||||
Handle<Object> accessors = GetAccessorsFromMap(map);
|
Handle<Object> accessors = GetAccessorsFromMap(map);
|
||||||
if (!accessors->IsAccessorPair()) return false;
|
if (!accessors->IsAccessorPair()) return false;
|
||||||
@ -6040,7 +6040,7 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::LoadResult(Handle<Map> map) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void HOptimizedGraphBuilder::PropertyAccessInfo::LoadFieldMaps(
|
bool HOptimizedGraphBuilder::PropertyAccessInfo::LoadFieldMaps(
|
||||||
Handle<Map> map) {
|
Handle<Map> map) {
|
||||||
// Clear any previously collected field maps/type.
|
// Clear any previously collected field maps/type.
|
||||||
field_maps_.Clear();
|
field_maps_.Clear();
|
||||||
@ -6051,7 +6051,7 @@ void HOptimizedGraphBuilder::PropertyAccessInfo::LoadFieldMaps(
|
|||||||
|
|
||||||
// Collect the (stable) maps from the field type.
|
// Collect the (stable) maps from the field type.
|
||||||
int num_field_maps = field_type->NumClasses();
|
int num_field_maps = field_type->NumClasses();
|
||||||
if (num_field_maps == 0) return;
|
if (num_field_maps > 0) {
|
||||||
DCHECK(access_.representation().IsHeapObject());
|
DCHECK(access_.representation().IsHeapObject());
|
||||||
field_maps_.Reserve(num_field_maps, zone());
|
field_maps_.Reserve(num_field_maps, zone());
|
||||||
HeapType::Iterator<Map> it = field_type->Classes();
|
HeapType::Iterator<Map> it = field_type->Classes();
|
||||||
@ -6059,11 +6059,18 @@ void HOptimizedGraphBuilder::PropertyAccessInfo::LoadFieldMaps(
|
|||||||
Handle<Map> field_map = it.Current();
|
Handle<Map> field_map = it.Current();
|
||||||
if (!field_map->is_stable()) {
|
if (!field_map->is_stable()) {
|
||||||
field_maps_.Clear();
|
field_maps_.Clear();
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
field_maps_.Add(field_map, zone());
|
field_maps_.Add(field_map, zone());
|
||||||
it.Advance();
|
it.Advance();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field_maps_.is_empty()) {
|
||||||
|
// Store is not safe if the field map was cleared.
|
||||||
|
return IsLoad() || !field_type->Is(HeapType::None());
|
||||||
|
}
|
||||||
|
|
||||||
field_maps_.Sort();
|
field_maps_.Sort();
|
||||||
DCHECK_EQ(num_field_maps, field_maps_.length());
|
DCHECK_EQ(num_field_maps, field_maps_.length());
|
||||||
|
|
||||||
@ -6074,6 +6081,7 @@ void HOptimizedGraphBuilder::PropertyAccessInfo::LoadFieldMaps(
|
|||||||
// Add dependency on the map that introduced the field.
|
// Add dependency on the map that introduced the field.
|
||||||
Map::AddDependentCompilationInfo(GetFieldOwnerFromMap(map),
|
Map::AddDependentCompilationInfo(GetFieldOwnerFromMap(map),
|
||||||
DependentCode::kFieldTypeGroup, top_info());
|
DependentCode::kFieldTypeGroup, top_info());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -6132,8 +6140,7 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::CanAccessMonomorphic() {
|
|||||||
access_ = HObjectAccess::ForField(map_, index, representation, name_);
|
access_ = HObjectAccess::ForField(map_, index, representation, name_);
|
||||||
|
|
||||||
// Load field map for heap objects.
|
// Load field map for heap objects.
|
||||||
LoadFieldMaps(transition());
|
return LoadFieldMaps(transition());
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2644,7 +2644,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
|
|||||||
CompilationInfo* current_info() { return builder_->current_info(); }
|
CompilationInfo* current_info() { return builder_->current_info(); }
|
||||||
|
|
||||||
bool LoadResult(Handle<Map> map);
|
bool LoadResult(Handle<Map> map);
|
||||||
void LoadFieldMaps(Handle<Map> map);
|
bool LoadFieldMaps(Handle<Map> map);
|
||||||
bool LookupDescriptor();
|
bool LookupDescriptor();
|
||||||
bool LookupInPrototypes();
|
bool LookupInPrototypes();
|
||||||
bool IsIntegerIndexedExotic();
|
bool IsIntegerIndexedExotic();
|
||||||
|
@ -290,7 +290,7 @@ void JSObject::JSObjectVerify() {
|
|||||||
if (r.IsHeapObject()) DCHECK(value->IsHeapObject());
|
if (r.IsHeapObject()) DCHECK(value->IsHeapObject());
|
||||||
HeapType* field_type = descriptors->GetFieldType(i);
|
HeapType* field_type = descriptors->GetFieldType(i);
|
||||||
bool type_is_none = field_type->Is(HeapType::None());
|
bool type_is_none = field_type->Is(HeapType::None());
|
||||||
bool type_is_any = field_type->Is(HeapType::Any());
|
bool type_is_any = HeapType::Any()->Is(field_type);
|
||||||
if (r.IsNone()) {
|
if (r.IsNone()) {
|
||||||
CHECK(type_is_none);
|
CHECK(type_is_none);
|
||||||
} else if (!type_is_any && !(type_is_none && r.IsHeapObject())) {
|
} else if (!type_is_any && !(type_is_none && r.IsHeapObject())) {
|
||||||
|
67
test/mjsunit/regress/regress-4023.js
Normal file
67
test/mjsunit/regress/regress-4023.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// Copyright 2015 the V8 project authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
// Flags: --allow-natives-syntax --expose-gc --block-concurrent-recompilation
|
||||||
|
|
||||||
|
function Inner() {
|
||||||
|
this.property = "OK";
|
||||||
|
this.prop2 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Outer() {
|
||||||
|
this.o = "u";
|
||||||
|
}
|
||||||
|
function KeepMapAlive(o) {
|
||||||
|
return o.o;
|
||||||
|
}
|
||||||
|
function SetInner(o, i) {
|
||||||
|
o.inner_field = i;
|
||||||
|
}
|
||||||
|
function Crash(o) {
|
||||||
|
return o.inner_field.property;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inner = new Inner();
|
||||||
|
var outer = new Outer();
|
||||||
|
|
||||||
|
// Collect type feedback.
|
||||||
|
SetInner(new Outer(), inner);
|
||||||
|
SetInner(outer, inner);
|
||||||
|
|
||||||
|
// This function's only purpose is to stash away a Handle that keeps
|
||||||
|
// outer's map alive during the gc() call below. We store this handle
|
||||||
|
// on the compiler thread :-)
|
||||||
|
KeepMapAlive(outer);
|
||||||
|
KeepMapAlive(outer);
|
||||||
|
%OptimizeFunctionOnNextCall(KeepMapAlive, "concurrent");
|
||||||
|
KeepMapAlive(outer);
|
||||||
|
|
||||||
|
// So far, all is well. Collect type feedback and optimize.
|
||||||
|
print(Crash(outer));
|
||||||
|
print(Crash(outer));
|
||||||
|
%OptimizeFunctionOnNextCall(Crash);
|
||||||
|
print(Crash(outer));
|
||||||
|
|
||||||
|
// Null out references and perform GC. This will keep outer's map alive
|
||||||
|
// (due to the handle created above), but will let inner's map die. Hence,
|
||||||
|
// inner_field's field type stored in outer's map will get cleared.
|
||||||
|
inner = undefined;
|
||||||
|
outer = undefined;
|
||||||
|
gc();
|
||||||
|
|
||||||
|
// We could unblock the compiler thread now. But why bother?
|
||||||
|
|
||||||
|
// Now optimize SetInner while inner_field's type is still cleared!
|
||||||
|
// This will generate optimized code that stores arbitrary objects
|
||||||
|
// into inner_field without checking their type against the field type.
|
||||||
|
%OptimizeFunctionOnNextCall(SetInner);
|
||||||
|
|
||||||
|
// Use the optimized code to store an arbitrary object into
|
||||||
|
// o2's inner_field, without triggering any dependent code deopts...
|
||||||
|
var o2 = new Outer();
|
||||||
|
SetInner(o2, { invalid: 1.51, property: "OK" });
|
||||||
|
// ...and then use the existing code expecting an Inner-class object to
|
||||||
|
// read invalid data (in this case, a raw double).
|
||||||
|
// We crash trying to convert the raw double into a printable string.
|
||||||
|
print(Crash(o2));
|
Loading…
Reference in New Issue
Block a user