From 448a1d4bb513613d8c39d2e4eafbb2642602e651 Mon Sep 17 00:00:00 2001 From: Benedikt Meurer Date: Thu, 3 Aug 2017 20:30:22 +0200 Subject: [PATCH] [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/600968 Reviewed-by: Jaroslav Sevcik Reviewed-by: Ross McIlroy Commit-Queue: Benedikt Meurer Cr-Commit-Position: refs/heads/master@{#47158} --- src/code-stubs.cc | 76 +++-------- src/interpreter/interpreter-assembler.cc | 99 ++++---------- test/cctest/test-feedback-vector.cc | 34 ++++- test/mjsunit/array-feedback.js | 158 ----------------------- 4 files changed, 75 insertions(+), 292 deletions(-) diff --git a/src/code-stubs.cc b/src/code-stubs.cc index 9420c1ff4f..53a2c7d2e3 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -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,50 +642,28 @@ 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); + Comment("check if uninitialized"); + // Check if it is uninitialized target first. + Node* is_uninitialized = WordEqual( + feedback_element, + HeapConstant(FeedbackVector::UninitializedSentinel(isolate()))); + GotoIfNot(is_uninitialized, &mark_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, target); - GotoIfNot(is_array_function, &mark_megamorphic); + Comment("handle unitinitialized"); + // If it is not a JSFunction mark it as megamorphic. + Node* is_smi = TaggedIsSmi(target); + GotoIf(is_smi, &mark_megamorphic); - // Call ArrayConstructorStub. - Callable callable = CodeFactory::ArrayConstructor(isolate()); - TailCallStub(callable, context, target, target, argc, feedback_element); + // Check if function is an object of JSFunction type. + Node* is_js_function = IsJSFunction(target); + GotoIfNot(is_js_function, &mark_megamorphic); - BIND(&check_initialized); - { - Comment("check if uninitialized"); - // Check if it is uninitialized target first. - Node* is_uninitialized = WordEqual( - feedback_element, - HeapConstant(FeedbackVector::UninitializedSentinel(isolate()))); - GotoIfNot(is_uninitialized, &mark_megamorphic); - - Comment("handle unitinitialized"); - // If it is not a JSFunction mark it as megamorphic. - Node* is_smi = TaggedIsSmi(target); - GotoIf(is_smi, &mark_megamorphic); - - // Check if function is an object of JSFunction type. - 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* is_same_native_context = - WordEqual(native_context, LoadNativeContext(context)); - Branch(is_same_native_context, &create_weak_cell, &mark_megamorphic); - } + // Check if the function belongs to the same native context. + 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. diff --git a/src/interpreter/interpreter-assembler.cc b/src/interpreter/interpreter-assembler.cc index a1a7be5a6e..9a82d9c336 100644 --- a/src/interpreter/interpreter-assembler.cc +++ b/src/interpreter/interpreter-assembler.cc @@ -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,83 +708,35 @@ 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); + Comment("check if uninitialized"); + // Check if it is uninitialized target first. + Node* is_uninitialized = WordEqual( + feedback_element, + HeapConstant(FeedbackVector::UninitializedSentinel(isolate()))); + GotoIfNot(is_uninitialized, &mark_megamorphic); - 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. + Comment("handle_uninitialized"); + // If it is not a JSFunction mark it as megamorphic. + Node* is_smi = TaggedIsSmi(function); + GotoIf(is_smi, &mark_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); + // Check if function is an object of JSFunction type. + Node* instance_type = LoadInstanceType(function); + Node* is_js_function = + Word32Equal(instance_type, Int32Constant(JS_FUNCTION_TYPE)); + GotoIfNot(is_js_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); + // Check if the function belongs to the same native context. + Node* native_context = LoadNativeContext( + LoadObjectField(function, JSFunction::kContextOffset)); + Node* is_same_native_context = + WordEqual(native_context, LoadNativeContext(context)); + GotoIfNot(is_same_native_context, &mark_megamorphic); - } else { - Goto(&mark_megamorphic); - } + CreateWeakCellInFeedbackVector(feedback_vector, SmiTag(slot_id), function); - BIND(&check_initialized); - { - Comment("check if uninitialized"); - // Check if it is uninitialized target first. - Node* is_uninitialized = WordEqual( - feedback_element, - HeapConstant(FeedbackVector::UninitializedSentinel(isolate()))); - GotoIfNot(is_uninitialized, &mark_megamorphic); - - Comment("handle_uninitialized"); - // If it is not a JSFunction mark it as megamorphic. - Node* is_smi = TaggedIsSmi(function); - GotoIf(is_smi, &mark_megamorphic); - - // Check if function is an object of JSFunction type. - Node* instance_type = LoadInstanceType(function); - Node* is_js_function = - 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)); - Node* is_same_native_context = - WordEqual(native_context, LoadNativeContext(context)); - GotoIfNot(is_same_native_context, &mark_megamorphic); - - 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); - } + // Call using call function builtin. + Goto(&call_function); BIND(&mark_megamorphic); { diff --git a/test/cctest/test-feedback-vector.cc b/test/cctest/test-feedback-vector.cc index 67e988ad1a..a46c6a6ce2 100644 --- a/test/cctest/test-feedback-vector.cc +++ b/test/cctest/test-feedback-vector.cc @@ -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 f = GetFunction("f"); + Handle foo = GetFunction("foo"); + // There should be one IC. + Handle feedback_vector = + Handle(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 f = GetFunction("f"); // There should be one IC. Handle 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. diff --git a/test/mjsunit/array-feedback.js b/test/mjsunit/array-feedback.js index 223c50391f..fb3a53bcd5 100644 --- a/test/mjsunit/array-feedback.js +++ b/test/mjsunit/array-feedback.js @@ -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();