Commit 448a1d4b authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[ic] Drop Array constructor support from CallIC.

Calling the Array constructor is an edge case, and we don't seem to
benefit from doing the AllocationSite tracking there as well. In fact
it's a lot of complexity and somewhat blocking the more important 
optimization of the subclass constructors.

This is an attempt to nuke the CallIC support for AllocationSites. If
it regresses something important, we'll have to find another way.

Bug: v8:6399
Change-Id: I56f6da29679c516f0a5c3161c2696fc2b8762176
Reviewed-on: https://chromium-review.googlesource.com/600968Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47158}
parent ea82e096
......@@ -633,9 +633,7 @@ TF_STUB(CallICStub, CodeStubAssembler) {
BIND(&extra_checks);
{
Label check_initialized(this), mark_megamorphic(this),
create_allocation_site(this, Label::kDeferred),
create_weak_cell(this, Label::kDeferred);
Label mark_megamorphic(this), create_weak_cell(this, Label::kDeferred);
Comment("check if megamorphic");
// Check if it is a megamorphic target.
......@@ -644,21 +642,6 @@ TF_STUB(CallICStub, CodeStubAssembler) {
HeapConstant(FeedbackVector::MegamorphicSentinel(isolate())));
GotoIf(is_megamorphic, &call);
Comment("check if it is an allocation site");
GotoIfNot(IsAllocationSite(feedback_element), &check_initialized);
// If it is not the Array() function, mark megamorphic.
Node* context_slot = LoadContextElement(LoadNativeContext(context),
Context::ARRAY_FUNCTION_INDEX);
Node* is_array_function = WordEqual(context_slot, target);
GotoIfNot(is_array_function, &mark_megamorphic);
// Call ArrayConstructorStub.
Callable callable = CodeFactory::ArrayConstructor(isolate());
TailCallStub(callable, context, target, target, argc, feedback_element);
BIND(&check_initialized);
{
Comment("check if uninitialized");
// Check if it is uninitialized target first.
Node* is_uninitialized = WordEqual(
......@@ -675,19 +658,12 @@ TF_STUB(CallICStub, CodeStubAssembler) {
Node* is_js_function = IsJSFunction(target);
GotoIfNot(is_js_function, &mark_megamorphic);
// Check if it is the Array() function.
Node* context_slot = LoadContextElement(LoadNativeContext(context),
Context::ARRAY_FUNCTION_INDEX);
Node* is_array_function = WordEqual(context_slot, target);
GotoIf(is_array_function, &create_allocation_site);
// Check if the function belongs to the same native context.
Node* native_context = LoadNativeContext(
LoadObjectField(target, JSFunction::kContextOffset));
Node* native_context =
LoadNativeContext(LoadObjectField(target, JSFunction::kContextOffset));
Node* is_same_native_context =
WordEqual(native_context, LoadNativeContext(context));
Branch(is_same_native_context, &create_weak_cell, &mark_megamorphic);
}
BIND(&create_weak_cell);
{
......@@ -699,18 +675,6 @@ TF_STUB(CallICStub, CodeStubAssembler) {
Goto(&call_function);
}
BIND(&create_allocation_site);
{
// Create an AllocationSite for the {target}.
Comment("create allocation site");
CreateAllocationSiteInFeedbackVector(vector, SmiTag(slot));
// Call using CallFunction builtin. CallICs have a PREMONOMORPHIC state.
// They start collecting feedback only when a call is executed the second
// time. So, do not pass any feedback here.
Goto(&call_function);
}
BIND(&mark_megamorphic);
{
// Mark it as a megamorphic.
......
......@@ -699,8 +699,7 @@ Node* InterpreterAssembler::CallJSWithFeedback(
BIND(&extra_checks);
{
Label check_initialized(this), mark_megamorphic(this),
create_allocation_site(this);
Label mark_megamorphic(this);
Comment("check if megamorphic");
// Check if it is a megamorphic target.
......@@ -709,36 +708,6 @@ Node* InterpreterAssembler::CallJSWithFeedback(
HeapConstant(FeedbackVector::MegamorphicSentinel(isolate())));
GotoIf(is_megamorphic, &call);
Comment("check if it is an allocation site");
GotoIfNot(IsAllocationSite(feedback_element), &check_initialized);
if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) {
// For undefined receivers (mostly global calls), do an additional check
// for the monomorphic Array function, which would otherwise appear
// megamorphic.
// If it is not the Array() function, mark megamorphic.
Node* context_slot = LoadContextElement(LoadNativeContext(context),
Context::ARRAY_FUNCTION_INDEX);
Node* is_array_function = WordEqual(context_slot, function);
GotoIfNot(is_array_function, &mark_megamorphic);
// Call ArrayConstructorStub.
Callable callable_call =
CodeFactory::InterpreterPushArgsThenConstructArray(isolate());
Node* code_target_call = HeapConstant(callable_call.code());
Node* ret_value =
CallStub(callable_call.descriptor(), code_target_call, context,
arg_count, function, feedback_element, first_arg);
return_value.Bind(ret_value);
Goto(&end);
} else {
Goto(&mark_megamorphic);
}
BIND(&check_initialized);
{
Comment("check if uninitialized");
// Check if it is uninitialized target first.
Node* is_uninitialized = WordEqual(
......@@ -757,12 +726,6 @@ Node* InterpreterAssembler::CallJSWithFeedback(
Word32Equal(instance_type, Int32Constant(JS_FUNCTION_TYPE));
GotoIfNot(is_js_function, &mark_megamorphic);
// Check if it is the Array() function.
Node* context_slot = LoadContextElement(LoadNativeContext(context),
Context::ARRAY_FUNCTION_INDEX);
Node* is_array_function = WordEqual(context_slot, function);
GotoIf(is_array_function, &create_allocation_site);
// Check if the function belongs to the same native context.
Node* native_context = LoadNativeContext(
LoadObjectField(function, JSFunction::kContextOffset));
......@@ -770,22 +733,10 @@ Node* InterpreterAssembler::CallJSWithFeedback(
WordEqual(native_context, LoadNativeContext(context));
GotoIfNot(is_same_native_context, &mark_megamorphic);
CreateWeakCellInFeedbackVector(feedback_vector, SmiTag(slot_id),
function);
CreateWeakCellInFeedbackVector(feedback_vector, SmiTag(slot_id), function);
// Call using call function builtin.
Goto(&call_function);
}
BIND(&create_allocation_site);
{
CreateAllocationSiteInFeedbackVector(feedback_vector, SmiTag(slot_id));
// Call using CallFunction builtin. CallICs have a PREMONOMORPHIC state.
// They start collecting feedback only when a call is executed the second
// time. So, do not pass any feedback here.
Goto(&call_function);
}
BIND(&mark_megamorphic);
{
......
......@@ -220,7 +220,7 @@ TEST(VectorCallICStates) {
CHECK_EQ(GENERIC, nexus.StateFromFeedback());
}
TEST(VectorCallFeedbackForArray) {
TEST(VectorCallFeedback) {
if (i::FLAG_always_opt) return;
CcTest::InitializeVM();
LocalContext context;
......@@ -229,7 +229,32 @@ TEST(VectorCallFeedbackForArray) {
// Make sure function f has a call that uses a type feedback slot.
CompileRun(
"function foo() { return 17; }"
"function f(a) { a(); } f(Array);");
"function f(a) { a(); } f(foo);");
Handle<JSFunction> f = GetFunction("f");
Handle<JSFunction> foo = GetFunction("foo");
// There should be one IC.
Handle<FeedbackVector> feedback_vector =
Handle<FeedbackVector>(f->feedback_vector(), isolate);
FeedbackSlot slot(0);
CallICNexus nexus(feedback_vector, slot);
CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
CHECK(nexus.GetFeedback()->IsWeakCell());
CHECK(*foo == WeakCell::cast(nexus.GetFeedback())->value());
CcTest::CollectAllGarbage();
// It should stay monomorphic even after a GC.
CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
}
TEST(VectorCallFeedbackForArray) {
if (i::FLAG_always_opt) return;
CcTest::InitializeVM();
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
Isolate* isolate = CcTest::i_isolate();
// Make sure function f has a call that uses a type feedback slot.
CompileRun("function f(a) { a(); } f(Array);");
Handle<JSFunction> f = GetFunction("f");
// There should be one IC.
Handle<FeedbackVector> feedback_vector =
......@@ -237,9 +262,10 @@ TEST(VectorCallFeedbackForArray) {
FeedbackSlot slot(0);
CallICNexus nexus(feedback_vector, slot);
// A call to Array is special, it contains an AllocationSite as feedback.
CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
CHECK(nexus.GetFeedback()->IsAllocationSite());
CHECK(nexus.GetFeedback()->IsWeakCell());
CHECK(*isolate->array_function() ==
WeakCell::cast(nexus.GetFeedback())->value());
CcTest::CollectAllGarbage();
// It should stay monomorphic even after a GC.
......
......@@ -28,164 +28,6 @@
// Flags: --allow-natives-syntax --expose-gc
// Flags: --opt --no-always-opt --no-stress-fullcodegen
var elements_kind = {
fast_smi_only : 'fast smi only elements',
fast : 'fast elements',
fast_double : 'fast double elements',
dictionary : 'dictionary elements',
external_byte : 'external byte elements',
external_unsigned_byte : 'external unsigned byte elements',
external_short : 'external short elements',
external_unsigned_short : 'external unsigned short elements',
external_int : 'external int elements',
external_unsigned_int : 'external unsigned int elements',
external_float : 'external float elements',
external_double : 'external double elements',
external_pixel : 'external pixel elements'
}
function getKind(obj) {
if (%HasSmiElements(obj)) return elements_kind.fast_smi_only;
if (%HasObjectElements(obj)) return elements_kind.fast;
if (%HasDoubleElements(obj)) return elements_kind.fast_double;
if (%HasDictionaryElements(obj)) return elements_kind.dictionary;
}
function isHoley(obj) {
if (%HasHoleyElements(obj)) return true;
return false;
}
function assertKind(expected, obj, name_opt) {
assertEquals(expected, getKind(obj), name_opt);
}
// Verify that basic elements kind feedback works for non-constructor
// array calls (as long as the call is made through an IC, and not
// a CallStub).
(function (){
function create0() {
return Array();
}
// Calls through ICs need warm up through uninitialized, then
// premonomorphic first.
create0();
a = create0();
assertKind(elements_kind.fast_smi_only, a);
a[0] = 3.5;
b = create0();
assertKind(elements_kind.fast_double, b);
function create1(arg) {
return Array(arg);
}
create1(0);
create1(0);
a = create1(0);
assertTrue(isHoley(a));
assertKind(elements_kind.fast_smi_only, a);
a[0] = "hello";
b = create1(10);
assertTrue(isHoley(b));
assertKind(elements_kind.fast, b);
a = create1(100000);
assertKind(elements_kind.fast, a);
function create3(arg1, arg2, arg3) {
return Array(arg1, arg2, arg3);
}
create3(1,2,3);
create3(1,2,3);
a = create3(1,2,3);
a[0] = 3.035;
assertKind(elements_kind.fast_double, a);
b = create3(1,2,3);
assertKind(elements_kind.fast_double, b);
assertFalse(isHoley(b));
})();
// Verify that keyed calls work
(function (){
function create0(name) {
return this[name]();
}
name = "Array";
create0(name);
create0(name);
a = create0(name);
a[0] = 3.5;
b = create0(name);
assertKind(elements_kind.fast_double, b);
})();
// Verify that feedback is turned off if the call site goes megamorphic.
(function (){
function foo(arg) { return arg(); }
foo(Array);
foo(function() {});
foo(Array);
gc();
a = foo(Array);
a[0] = 3.5;
b = foo(Array);
// b doesn't benefit from elements kind feedback at a megamorphic site.
assertKind(elements_kind.fast_smi_only, b);
})();
// Verify that crankshaft consumes type feedback.
(function (){
function create0() {
return Array();
}
create0();
create0();
a = create0();
a[0] = 3.5;
%OptimizeFunctionOnNextCall(create0);
create0();
create0();
b = create0();
assertKind(elements_kind.fast_double, b);
assertOptimized(create0);
function create1(arg) {
return Array(arg);
}
create1(8);
create1(8);
a = create1(8);
a[0] = 3.5;
%OptimizeFunctionOnNextCall(create1);
b = create1(8);
assertKind(elements_kind.fast_double, b);
assertOptimized(create1);
function createN(arg1, arg2, arg3) {
return Array(arg1, arg2, arg3);
}
createN(1, 2, 3);
createN(1, 2, 3);
a = createN(1, 2, 3);
a[0] = 3.5;
%OptimizeFunctionOnNextCall(createN);
b = createN(1, 2, 3);
assertKind(elements_kind.fast_double, b);
assertOptimized(createN);
})();
// Verify that cross context calls work
(function (){
var realmA = Realm.current();
......
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