[runtime] Enable double-lazy boilerplate creation again

This mostly reverts commit c503b80595 but fixes
an issue where literals would always be pretenured on first instantiation.

As a cleanup we pass in a PretenureFlag instead of using the FeedbackVector as
indicator.

Bug: v8:6211
Change-Id: Id328552620e33f5083519bcba1e24396d162d516
Reviewed-on: https://chromium-review.googlesource.com/555670
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46342}
This commit is contained in:
Camillo Bruni 2017-06-30 10:04:32 +02:00 committed by Commit Bot
parent eeeae375b9
commit bbc89774a6
7 changed files with 260 additions and 88 deletions

View File

@ -46,6 +46,7 @@ void HeapObject::PrintHeader(std::ostream& os, const char* id) { // NOLINT
os << map()->instance_type();
}
os << "]";
if (GetHeap()->InOldSpace(this)) os << " in OldSpace";
}

View File

@ -30,8 +30,8 @@ void PreInitializeLiteralSite(Handle<FeedbackVector> vector,
}
Handle<Object> InnerCreateBoilerplate(Isolate* isolate,
Handle<FeedbackVector> vector,
Handle<FixedArray> compile_time_value);
Handle<FixedArray> compile_time_value,
PretenureFlag pretenure_flag);
enum DeepCopyHints { kNoHints = 0, kObjectIsShallow = 1 };
@ -207,6 +207,25 @@ MaybeHandle<JSObject> JSObjectWalkVisitor<ContextObject>::StructureWalk(
return copy;
}
class DeprecationUpdateContext {
public:
explicit DeprecationUpdateContext(Isolate* isolate) { isolate_ = isolate; }
Isolate* isolate() { return isolate_; }
bool ShouldCreateMemento(Handle<JSObject> object) { return false; }
inline void ExitScope(Handle<AllocationSite> scope_site,
Handle<JSObject> object) {}
Handle<AllocationSite> EnterNewScope() { return Handle<AllocationSite>(); }
Handle<AllocationSite> current() {
UNREACHABLE();
return Handle<AllocationSite>();
}
static const bool kCopying = false;
private:
Isolate* isolate_;
};
// AllocationSiteCreationContext aids in the creation of AllocationSites to
// accompany object literals.
class AllocationSiteCreationContext : public AllocationSiteContext {
@ -258,6 +277,15 @@ class AllocationSiteCreationContext : public AllocationSiteContext {
static const bool kCopying = false;
};
MaybeHandle<JSObject> DeepWalk(Handle<JSObject> object,
DeprecationUpdateContext* site_context) {
JSObjectWalkVisitor<DeprecationUpdateContext> v(site_context, kNoHints);
MaybeHandle<JSObject> result = v.StructureWalk(object);
Handle<JSObject> for_assert;
DCHECK(!result.ToHandle(&for_assert) || for_assert.is_identical_to(object));
return result;
}
MaybeHandle<JSObject> DeepWalk(Handle<JSObject> object,
AllocationSiteCreationContext* site_context) {
JSObjectWalkVisitor<AllocationSiteCreationContext> v(site_context, kNoHints);
@ -279,8 +307,8 @@ MaybeHandle<JSObject> DeepCopy(Handle<JSObject> object,
struct ObjectBoilerplate {
static Handle<JSObject> Create(Isolate* isolate,
Handle<FeedbackVector> vector,
Handle<HeapObject> description, int flags) {
Handle<HeapObject> description, int flags,
PretenureFlag pretenure_flag) {
Handle<Context> native_context = isolate->native_context();
Handle<BoilerplateDescription> boilerplate_description =
Handle<BoilerplateDescription>::cast(description);
@ -302,9 +330,6 @@ struct ObjectBoilerplate {
: isolate->factory()->ObjectLiteralMapFromCache(
native_context, number_of_properties);
PretenureFlag pretenure_flag =
isolate->heap()->InNewSpace(*vector) ? NOT_TENURED : TENURED;
Handle<JSObject> boilerplate =
map->is_dictionary_map()
? isolate->factory()->NewSlowJSObjectFromMap(
@ -324,7 +349,8 @@ struct ObjectBoilerplate {
// The value contains the CompileTimeValue with the boilerplate
// properties of a simple object or array literal.
Handle<FixedArray> compile_time_value = Handle<FixedArray>::cast(value);
value = InnerCreateBoilerplate(isolate, vector, compile_time_value);
value =
InnerCreateBoilerplate(isolate, compile_time_value, pretenure_flag);
}
uint32_t element_index = 0;
if (key->ToArrayIndex(&element_index)) {
@ -356,8 +382,8 @@ struct ObjectBoilerplate {
struct ArrayBoilerplate {
static Handle<JSObject> Create(Isolate* isolate,
Handle<FeedbackVector> vector,
Handle<HeapObject> description, int flags) {
Handle<HeapObject> description, int flags,
PretenureFlag pretenure_flag) {
Handle<ConstantElementsPair> elements =
Handle<ConstantElementsPair>::cast(description);
// Create the JSArray.
@ -397,16 +423,14 @@ struct ArrayBoilerplate {
// array literal.
Handle<FixedArray> compile_time_value(
FixedArray::cast(fixed_array_values->get(i)));
Handle<Object> result =
InnerCreateBoilerplate(isolate, vector, compile_time_value);
Handle<Object> result = InnerCreateBoilerplate(
isolate, compile_time_value, pretenure_flag);
fixed_array_values_copy->set(i, *result);
}
});
}
}
PretenureFlag pretenure_flag =
isolate->heap()->InNewSpace(*vector) ? NOT_TENURED : TENURED;
return isolate->factory()->NewJSArrayWithElements(
copied_elements_values, constant_elements_kind,
copied_elements_values->length(), pretenure_flag);
@ -414,15 +438,15 @@ struct ArrayBoilerplate {
};
Handle<Object> InnerCreateBoilerplate(Isolate* isolate,
Handle<FeedbackVector> vector,
Handle<FixedArray> compile_time_value) {
Handle<FixedArray> compile_time_value,
PretenureFlag pretenure_flag) {
Handle<HeapObject> elements =
CompileTimeValue::GetElements(compile_time_value);
int flags = CompileTimeValue::GetLiteralTypeFlags(compile_time_value);
if (flags == CompileTimeValue::kArrayLiteralFlag) {
return ArrayBoilerplate::Create(isolate, vector, elements, flags);
return ArrayBoilerplate::Create(isolate, elements, flags, pretenure_flag);
}
return ObjectBoilerplate::Create(isolate, vector, elements, flags);
return ObjectBoilerplate::Create(isolate, elements, flags, pretenure_flag);
}
template <typename Boilerplate>
@ -453,9 +477,22 @@ MaybeHandle<JSObject> CreateLiteral(Isolate* isolate,
Handle<JSObject>(JSObject::cast(site->transition_info()), isolate);
} else {
// Instantiate a JSArray or JSObject literal from the given {description}.
boilerplate = Boilerplate::Create(isolate, vector, description, flags);
// TODO(cbruni): enable pre-initialized state for boilerplates after
// investigating regressions.
if (IsUninitializedLiteralSite(literal_site)) {
PreInitializeLiteralSite(vector, literals_slot);
boilerplate =
Boilerplate::Create(isolate, description, flags, NOT_TENURED);
if (copy_hints == kNoHints) {
DeprecationUpdateContext update_context(isolate);
RETURN_ON_EXCEPTION(isolate, DeepWalk(boilerplate, &update_context),
JSObject);
}
return boilerplate;
} else {
PretenureFlag pretenure_flag =
isolate->heap()->InNewSpace(*vector) ? NOT_TENURED : TENURED;
boilerplate =
Boilerplate::Create(isolate, description, flags, pretenure_flag);
}
// Install AllocationSite objects.
AllocationSiteCreationContext creation_context(isolate);
site = creation_context.EnterNewScope();

View File

@ -165,6 +165,12 @@ function fastliteralcase_smiholey(index, value) {
obj = fastliteralcase_smiholey(5, 1);
assertKind(elements_kind.fast_smi_only, obj);
assertHoley(obj);
// We only start tracking tranistion with the second instantiation.
obj = fastliteralcase_smiholey(5, 1);
assertKind(elements_kind.fast_smi_only, obj);
assertHoley(obj);
obj = fastliteralcase_smiholey(0, 1);
assertKind(elements_kind.fast_smi_only, obj);
assertHoley(obj);
@ -262,27 +268,70 @@ assertKind(elements_kind.fast, obj);
// Case: array constructor calls with out of date feedback.
// The boilerplate should incorporate all feedback, but the input array
// should be minimally transitioned based on immediate need.
(function() {
function foo(i) {
// We have two cases, one for literals one for constructed arrays.
var a = (i == 0)
? [1, 2, 3]
: new Array(1, 2, 3);
return a;
(function TestLiteralTransition() {
function literal() {
return [1, 2, 3];
}
for (i = 0; i < 2; i++) {
a = foo(i);
b = foo(i);
b[5] = 1; // boilerplate goes holey
assertHoley(foo(i));
a[0] = 3.5; // boilerplate goes holey double
assertKind(elements_kind.fast_double, a);
assertNotHoley(a);
c = foo(i);
assertKind(elements_kind.fast_double, c);
assertHoley(c);
var a = literal(); // No boilerplate created yet.
var b = literal(); // Created boilerplate here.
var c = literal(); // Created copy from boilerplate.
// Boilerplate goes holey smi.
b[5] = 1;
assertKind(elements_kind.fast_smi_only, a);
assertKind(elements_kind.fast_smi_only, b);
assertKind(elements_kind.fast_smi_only, c);
assertHoley(literal());
// {a} has been created before tracking was active and thus doesn't affect
// the boilerplate.
a[0] = 3.5;
assertKind(elements_kind.fast_double, a);
assertNotHoley(a);
// Check that modifying {a} didn't change the boilerplate.
var d = literal();
assertKind(elements_kind.fast_smi_only, d);
assertHoley(d);
// Boilerplate goes from holey smi to holey double
c[0] = 3.5;
assertKind(elements_kind.fast_double, c);
assertNotHoley(c);
var e = literal();
assertKind(elements_kind.fast_double, e);
assertHoley(e);
})();
(function TestConstructedArrayTransition() {
// Allocation site tracking is on from the first instantiation for constructor
// calls.
function array() {
return new Array(1, 2, 3);
}
var a = array();
var b = array();
// Transition kind goes to smi holey.
b[5] = 1;
assertKind(elements_kind.fast_smi_only, a);
assertNotHoley(a);
assertHoley(b);
assertKind(elements_kind.fast_smi_only, b);
assertHoley(array());
// Confirm that modifying {b} did change the transition kind.
var d = array();
assertKind(elements_kind.fast_smi_only, d);
assertHoley(d);
// Sets the transition kind to double.
a[0] = 3.5;
assertKind(elements_kind.fast_double, a);
assertNotHoley(a);
// Confirm that we get the general kind holey + double.
var e = array();
assertKind(elements_kind.fast_double, e);
assertHoley(e);
})();
function newarraycase_onearg(len, value) {
@ -375,15 +424,35 @@ gc();
return literal;
}
obj = get_nested_literal();
var obj = get_nested_literal();
assertKind(elements_kind.fast, obj);
assertKind(elements_kind.fast_smi_only, obj[0]);
assertKind(elements_kind.fast_smi_only, obj[1]);
assertKind(elements_kind.fast_smi_only, obj[2]);
obj[0][0] = 3.5;
obj[2][0] = "hello";
assertKind(elements_kind.fast_double, obj[0]);
assertKind(elements_kind.fast_smi_only, obj[1]);
assertKind(elements_kind.fast, obj[2]);
// We start tracking the allocation site from the second instantiation on.
obj = get_nested_literal();
assertKind(elements_kind.fast, obj);
assertKind(elements_kind.fast_smi_only, obj[0]);
assertKind(elements_kind.fast_smi_only, obj[1]);
assertKind(elements_kind.fast_smi_only, obj[2]);
obj[0][0] = 3.5;
obj[2][0] = "hello";
assertKind(elements_kind.fast_double, obj[0]);
assertKind(elements_kind.fast_smi_only, obj[1]);
assertKind(elements_kind.fast, obj[2]);
obj = get_nested_literal();
assertKind(elements_kind.fast_double, obj[0]);
assertKind(elements_kind.fast_smi_only, obj[1]);
assertKind(elements_kind.fast, obj[2]);
// A more complex nested literal case.
function get_deep_nested_literal() {
var literal = [[1], [[2], "hello"], 3, [4]];
@ -391,6 +460,15 @@ gc();
}
obj = get_deep_nested_literal();
assertKind(elements_kind.fast_smi_only, obj[0]);
assertKind(elements_kind.fast_smi_only, obj[1][0]);
obj[0][0] = 3.5;
obj[1][0][0] = "goodbye";
assertKind(elements_kind.fast_double, obj[0]);
assertKind(elements_kind.fast, obj[1][0]);
obj = get_deep_nested_literal();
assertKind(elements_kind.fast_smi_only, obj[0]);
assertKind(elements_kind.fast_smi_only, obj[1][0]);
obj[0][0] = 3.5;
obj[1][0][0] = "goodbye";
@ -424,6 +502,12 @@ gc();
assertKind(elements_kind.fast_smi_only, obj.array);
obj.array[1] = 3.5;
assertKind(elements_kind.fast_double, obj.array);
obj = get_object_literal();
assertKind(elements_kind.fast_smi_only, obj.array);
obj.array[1] = 3.5;
assertKind(elements_kind.fast_double, obj.array);
obj = get_object_literal();
assertKind(elements_kind.fast_double, obj.array);
@ -440,6 +524,13 @@ gc();
assertKind(elements_kind.fast_smi_only, obj.array[1]);
obj.array[1][0] = 3.5;
assertKind(elements_kind.fast_double, obj.array[1]);
obj = get_nested_object_literal();
assertKind(elements_kind.fast, obj.array);
assertKind(elements_kind.fast_smi_only, obj.array[1]);
obj.array[1][0] = 3.5;
assertKind(elements_kind.fast_double, obj.array[1]);
obj = get_nested_object_literal();
assertKind(elements_kind.fast_double, obj.array[1]);
@ -456,8 +547,26 @@ gc();
obj = get_nested_literal();
assertKind(elements_kind.fast, obj);
assertKind(elements_kind.fast_smi_only, obj[0]);
assertKind(elements_kind.fast_smi_only, obj[1]);
assertKind(elements_kind.fast_smi_only, obj[2]);
obj[0][0] = 3.5;
obj[2][0] = "hello";
assertKind(elements_kind.fast_double, obj[0]);
assertKind(elements_kind.fast_smi_only, obj[1]);
assertKind(elements_kind.fast, obj[2]);
obj = get_nested_literal();
assertKind(elements_kind.fast, obj);
assertKind(elements_kind.fast_smi_only, obj[0]);
assertKind(elements_kind.fast_smi_only, obj[1]);
assertKind(elements_kind.fast_smi_only, obj[2]);
obj[0][0] = 3.5;
obj[2][0] = "hello";
assertKind(elements_kind.fast_double, obj[0]);
assertKind(elements_kind.fast_smi_only, obj[1]);
assertKind(elements_kind.fast, obj[2]);
obj = get_nested_literal();
assertKind(elements_kind.fast_double, obj[0]);
assertKind(elements_kind.fast_smi_only, obj[1]);
@ -470,6 +579,15 @@ gc();
}
obj = get_deep_nested_literal();
assertKind(elements_kind.fast_smi_only, obj[0]);
assertKind(elements_kind.fast_smi_only, obj[1][0]);
obj[0][0] = 3.5;
obj[1][0][0] = "goodbye";
assertKind(elements_kind.fast_double, obj[0]);
assertKind(elements_kind.fast, obj[1][0]);
obj = get_deep_nested_literal();
assertKind(elements_kind.fast_smi_only, obj[0]);
assertKind(elements_kind.fast_smi_only, obj[1][0]);
obj[0][0] = 3.5;
obj[1][0][0] = "goodbye";

View File

@ -69,7 +69,7 @@ get_literal(3);
// It's important to store a from before we crankshaft get_literal, because
// mementos won't be created from crankshafted code at all.
a = get_literal(3);
%OptimizeFunctionOnNextCall(get_literal);
%OptimizeFunctionOnNextCall(get_literal);
get_literal(3);
assertOptimized(get_literal);
assertTrue(%HasFastSmiElements(a));
@ -86,7 +86,7 @@ assertUnoptimized(get_literal);
// Optimize again
get_literal(3);
%OptimizeFunctionOnNextCall(get_literal);
%OptimizeFunctionOnNextCall(get_literal);
b = get_literal(3);
assertTrue(%HasFastDoubleElements(b));
assertOptimized(get_literal);
@ -99,9 +99,20 @@ assertOptimized(get_literal);
return [a, b, c];
}
a = bar(1, 2, 3);
var a = bar(1, 2, 3);
assertKind(elements_kind.fast_smi_only, a);
a[0] = 3.5;
a[1] = 'hi';
b = bar(1, 2, 3);
assertKind(elements_kind.fast, a);
// We only start tracking transition information with the second
// instantiation.
var b = bar(1, 2, 3);
assertKind(elements_kind.fast_smi_only, b);
b[0] = 3.5;
b[1] = 'hi';
assertKind(elements_kind.fast, b);
var c = bar(1, 2, 3);
assertKind(elements_kind.fast, c);
})();

View File

@ -113,11 +113,15 @@
assertEquals(3, l.y.z)
}
f(); f(); f();
f();
f();
f();
%OptimizeFunctionOnNextCall(f);
f(); f();
f();
f();
%OptimizeFunctionOnNextCall(f);
f(); f();
f();
f();
})();

View File

@ -57,8 +57,7 @@ function testBasicPrototype() {
assertEquals(Object.getPrototypeOf(obj), Object.prototype);
assertEquals(Object.getPrototypeOf(obj.b), Object.prototype);
};
testBasicPrototype();
testBasicPrototype();
runTest(testBasicPrototype);
function testDynamicValue() {
var z = 24;
@ -74,10 +73,9 @@ function testDynamicValue() {
assertEquals(24, obj2.b.y);
assertEquals('Zebra', obj2.c);
}
testDynamicValue();
testDynamicValue();
runTest(testDynamicValue);
(function testMultipleInstatiations() {
function testMultipleInstatiations() {
var arr = [];
for (var i = 0; i < 2; i++) {
arr[i] = {
@ -90,7 +88,8 @@ testDynamicValue();
arr[0].b.x = 2;
assertEquals(2, arr[0].b.x);
assertEquals(12, arr[1].b.x);
})();
}
runTest(testMultipleInstatiations);
function testSparseElements() {
let sa1 = {
@ -254,8 +253,7 @@ function TestSimpleElements() {
o[0] = 0;
assertEquals({0:0, 1:"one", 2:"two"}, o);
}
TestSimpleElements();
TestSimpleElements();
runTest(TestSimpleElements);
function TestNumericNames() {
var o = {
@ -279,8 +277,7 @@ function TestNumericNames() {
%HeapObjectVerify(o);
assertEquals(['1.2', '1.3'], Object.keys(o));
}
TestNumericNames();
TestNumericNames();
runTest(TestNumericNames);
function TestDictionaryElements() {
let o = {1024: true};
@ -301,10 +298,7 @@ function TestDictionaryElements() {
o2[1024] = "test";
assertEquals(["test"], Object.values(o2));
}
TestDictionaryElements();
TestDictionaryElements();
%OptimizeFunctionOnNextCall(TestDictionaryElements);
TestDictionaryElements();
runTest(TestDictionaryElements);
function TestLiteralElementsKind() {
let o = {0:0, 1:1, 2:2};
@ -330,10 +324,7 @@ function TestLiteralElementsKind() {
assertTrue(%HasDictionaryElements({0xFFFFFF:true}));
}
TestLiteralElementsKind();
TestLiteralElementsKind();
%OptimizeFunctionOnNextCall(TestLiteralElementsKind);
TestLiteralElementsKind();
runTest(TestLiteralElementsKind);
function TestNonNumberElementValues() {
var o = {
@ -388,11 +379,7 @@ function TestNonNumberElementValues() {
%HeapObjectVerify(o4);
assertEquals(['1', '2', '3', '4', 'a', 'b'], Object.keys(o4));
}
TestNonNumberElementValues();
TestNonNumberElementValues();
TestNonNumberElementValues();
%OptimizeFunctionOnNextCall(TestNonNumberElementValues);
TestNonNumberElementValues();
runTest(TestNonNumberElementValues);
function numericGetters() {
@ -419,8 +406,7 @@ function numericGetters() {
get 1.30() {}
});
}
numericGetters();
numericGetters();
runTest(numericGetters);
function numericSetters() {
function TestNumericNamesSetter(expectedKeys, object) {
@ -446,9 +432,7 @@ function numericSetters() {
set 1.30(_) {; }
});
};
numericSetters();
numericSetters();
runTest(numericSetters);
function TestProxyWithDefinitionInObjectLiteral() {
// Trap for set should not be used if the definition
@ -464,14 +448,12 @@ function TestProxyWithDefinitionInObjectLiteral() {
p[prop] = 'my value';
assertEquals(undefined, p[prop]);
var l = new Proxy({[prop]: 'my value'}, handler);
assertEquals('my value', l[prop]);
};
TestProxyWithDefinitionInObjectLiteral();
TestProxyWithDefinitionInObjectLiteral();
runTest(TestProxyWithDefinitionInObjectLiteral);
(function TestLiteralWithNullProto() {
function TestLiteralWithNullProto() {
// Assume dictionary usage for simple null prototype literal objects,
// this is equivalent to Object.create(null). Note that on the first call
// the literal boilerplate is initialized, and from then on we use a the
@ -498,7 +480,8 @@ TestProxyWithDefinitionInObjectLiteral();
testDictModeNullProtoLiteral(() => ({a:1, b:2, __proto__:null}));
testDictModeNullProtoLiteral(() => ({["a"]: 1, __proto__: null}));
testDictModeNullProtoLiteral(() => ({a: Object, __proto__: null}));
})();
}
runTest(TestLiteralWithNullProto);
function testNestedNullProtoLiteral() {
let obj;
@ -524,8 +507,7 @@ function testNestedNullProtoLiteral() {
obj.foo.bar = "barValue2";
assertEquals("barValue2", obj.foo.bar);
}
testNestedNullProtoLiteral();
testNestedNullProtoLiteral();
runTest(testNestedNullProtoLiteral);
function TestSlowLiteralOptimized() {
@ -549,10 +531,9 @@ function TestSlowLiteralOptimized() {
obj.bar = "barValue2";
assertEquals("barValue2", obj.bar);
};
TestSlowLiteralOptimized();
TestSlowLiteralOptimized();
runTest(TestSlowLiteralOptimized);
(function TestLargeDictionaryLiteral() {
function TestLargeDictionaryLiteral() {
// Create potential large-space object literal.
function createObject() {
// This literal has least kMaxRegularHeapObjectSize / 64 number of
@ -1568,7 +1549,8 @@ TestSlowLiteralOptimized();
assertFalse(%HasFastProperties(object2));
assertEquals(Object.getPrototypeOf(object2), null);
assertEquals(keys, Object.keys(object2));
})();
}
runTest(TestLargeDictionaryLiteral);
(function TestPrototypeInObjectLiteral() {
@ -1592,3 +1574,21 @@ TestSlowLiteralOptimized();
delete Object.prototype.c;
})();
(function testNewLiteralObjectSpace() {
// The first-time literals are created they should reside in new-space.
assertTrue(%InNewSpace([]));
assertTrue(%InNewSpace({}));
let result = [ [0], [1], [2], [3]];
assertTrue(%InNewSpace(result));
for (let i = 0; i < result.length; i++) {
assertTrue(%InNewSpace(result[i]));
}
result = {a:{x:{}}, b:{x:{}}, c:{x:{}}};
assertTrue(%InNewSpace(result));
for (let key in result) {
assertTrue(%InNewSpace(result[key]));
assertTrue(%InNewSpace(result[key].x));
}
})();

View File

@ -29,11 +29,12 @@ function literals_sharing_test(warmup, optimize) {
function test() {
var warmup = true;
for (var i = 0; i < 3; i++) {
// We only start tracking allocation information with the second
// instantiation.
var warmup = i < 2;
print("iter: " + i + ", warmup: "+ warmup);
literals_sharing_test(warmup, false);
warmup = false;
}
print("iter: " + i + ", opt: true");
literals_sharing_test(warmup, true);