Hydrogen object literals: always initialize in-object properties

This fixes a bug where new-space GC could be triggered by non-folded allocations for some of the in-object properties, while the object was only partially initialized.

BUG=chromium:500497
LOG=y
R=ishell@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#29079}
This commit is contained in:
jkummerow 2015-06-17 04:24:15 -07:00 committed by Commit bot
parent 14151c81a2
commit 5fca3947cf
4 changed files with 62 additions and 15 deletions

View File

@ -9667,15 +9667,7 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
HObjectAccess::ForMapAndOffset(initial_map,
JSObject::kElementsOffset),
empty_fixed_array);
if (initial_map->inobject_properties() != 0) {
HConstant* undefined = graph()->GetConstantUndefined();
for (int i = 0; i < initial_map->inobject_properties(); i++) {
int property_offset = initial_map->GetInObjectPropertyOffset(i);
Add<HStoreNamedField>(receiver,
HObjectAccess::ForMapAndOffset(initial_map, property_offset),
undefined);
}
}
BuildInitializeInobjectProperties(receiver, initial_map);
}
// Replace the constructor function with a newly allocated receiver using
@ -9718,6 +9710,20 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
}
void HOptimizedGraphBuilder::BuildInitializeInobjectProperties(
HValue* receiver, Handle<Map> initial_map) {
if (initial_map->inobject_properties() != 0) {
HConstant* undefined = graph()->GetConstantUndefined();
for (int i = 0; i < initial_map->inobject_properties(); i++) {
int property_offset = initial_map->GetInObjectPropertyOffset(i);
Add<HStoreNamedField>(receiver, HObjectAccess::ForMapAndOffset(
initial_map, property_offset),
undefined);
}
}
}
HValue* HGraphBuilder::BuildAllocateEmptyArrayBuffer(HValue* byte_length) {
HAllocate* result =
BuildAllocate(Add<HConstant>(JSArrayBuffer::kSizeWithInternalFields),
@ -11302,13 +11308,13 @@ HInstruction* HOptimizedGraphBuilder::BuildFastLiteral(
Handle<JSObject> boilerplate_object,
AllocationSiteUsageContext* site_context) {
NoObservableSideEffectsScope no_effects(this);
InstanceType instance_type = boilerplate_object->map()->instance_type();
Handle<Map> initial_map(boilerplate_object->map());
InstanceType instance_type = initial_map->instance_type();
DCHECK(instance_type == JS_ARRAY_TYPE || instance_type == JS_OBJECT_TYPE);
HType type = instance_type == JS_ARRAY_TYPE
? HType::JSArray() : HType::JSObject();
HValue* object_size_constant = Add<HConstant>(
boilerplate_object->map()->instance_size());
HValue* object_size_constant = Add<HConstant>(initial_map->instance_size());
PretenureFlag pretenure_flag = NOT_TENURED;
Handle<AllocationSite> current_site(*site_context->current(), isolate());
@ -11333,6 +11339,11 @@ HInstruction* HOptimizedGraphBuilder::BuildFastLiteral(
BuildEmitObjectHeader(boilerplate_object, object);
// Similarly to the elements pointer, there is no guarantee that all
// property allocations can get folded, so pre-initialize all in-object
// properties to a safe value.
BuildInitializeInobjectProperties(object, initial_map);
Handle<FixedArrayBase> elements(boilerplate_object->elements());
int elements_size = (elements->length() > 0 &&
elements->map() != isolate()->heap()->fixed_cow_array_map()) ?
@ -11371,8 +11382,8 @@ HInstruction* HOptimizedGraphBuilder::BuildFastLiteral(
}
// Copy in-object properties.
if (boilerplate_object->map()->NumberOfFields() != 0 ||
boilerplate_object->map()->unused_property_fields() > 0) {
if (initial_map->NumberOfFields() != 0 ||
initial_map->unused_property_fields() > 0) {
BuildEmitInObjectProperties(boilerplate_object, object, site_context,
pretenure_flag);
}

View File

@ -2505,6 +2505,9 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
void BuildInlinedCallArray(Expression* expression, int argument_count,
Handle<AllocationSite> site);
void BuildInitializeInobjectProperties(HValue* receiver,
Handle<Map> initial_map);
class PropertyAccessInfo {
public:
PropertyAccessInfo(HOptimizedGraphBuilder* builder,

View File

@ -180,7 +180,7 @@ RUNTIME_FUNCTION(Runtime_GetOptimizationStatus) {
base::OS::Sleep(base::TimeDelta::FromMilliseconds(50));
}
}
if (FLAG_always_opt) {
if (FLAG_always_opt || FLAG_prepare_always_opt) {
// With --always-opt, optimization status expectations might not
// match up, so just return a sentinel.
return Smi::FromInt(3); // 3 == "always".

View File

@ -0,0 +1,33 @@
// 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.
// New space must be at max capacity to trigger pretenuring decision.
// Flags: --allow-natives-syntax --verify-heap --max-semi-space-size=1
var global = []; // Used to keep some objects alive.
function Ctor() {
var result = {a: {}, b: {}, c: {}, d: {}, e: {}, f: {}, g: {}};
return result;
}
for (var i = 0; i < 120; i++) {
// Make the "a" property long-lived, while everything else is short-lived.
global.push(Ctor().a);
(function FillNewSpace() { new Array(10000); })();
}
// The bad situation is only triggered if Ctor wasn't optimized too early.
assertUnoptimized(Ctor);
// Optimized code for Ctor will pretenure the "a" property, so it will have
// three allocations:
// #1 Allocate the "result" object in new-space.
// #2 Allocate the object stored in the "a" property in old-space.
// #3 Allocate the objects for the "b" through "g" properties in new-space.
%OptimizeFunctionOnNextCall(Ctor);
for (var i = 0; i < 10000; i++) {
// At least one of these calls will run out of new space. The bug is
// triggered when it is allocation #3 that triggers GC.
Ctor();
}