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:
ulan 2015-04-13 02:43:55 -07:00 committed by Commit bot
parent d93a0029dc
commit 2f327a5cb4
4 changed files with 91 additions and 17 deletions

View File

@ -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,19 +6051,26 @@ 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();
while (!it.Done()) { while (!it.Done()) {
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());
it.Advance();
} }
field_maps_.Add(field_map, zone());
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;
} }

View File

@ -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();

View File

@ -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())) {

View 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));