Commit be360af2 authored by mythria's avatar mythria Committed by Commit bot

[Interpreter] Optimize for monomorphic case in Call and New bytecode handlers.

Fast case the monomorphic case, by marking the additional checks as deferred
blocks. This increases the code size by about 50 bytes, but the monmorphic
case requires one fewer jump and the all the code is contiguous, so may help
caching.

Also cleanup call and new bytecode handlers by changing some of the Branches
to GotoIf/Unless for better readability.

BUG=v8:4280

Review-Url: https://codereview.chromium.org/2487573005
Cr-Commit-Position: refs/heads/master@{#40910}
parent ddba89c4
...@@ -546,22 +546,22 @@ Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context, ...@@ -546,22 +546,22 @@ Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context,
WeakCell::kValueOffset == Symbol::kHashFieldSlot); WeakCell::kValueOffset == Symbol::kHashFieldSlot);
Variable return_value(this, MachineRepresentation::kTagged); Variable return_value(this, MachineRepresentation::kTagged);
Label handle_monomorphic(this), extra_checks(this), end(this), call(this), Label call_function(this), extra_checks(this, Label::kDeferred), call(this),
call_function(this); end(this);
// The checks. First, does function match the recorded monomorphic target? // The checks. First, does function match the recorded monomorphic target?
Node* feedback_element = LoadFixedArrayElement(type_feedback_vector, slot_id); Node* feedback_element = LoadFixedArrayElement(type_feedback_vector, slot_id);
Node* feedback_value = LoadWeakCellValueUnchecked(feedback_element); Node* feedback_value = LoadWeakCellValueUnchecked(feedback_element);
Node* is_monomorphic = WordEqual(function, feedback_value); Node* is_monomorphic = WordEqual(function, feedback_value);
Branch(is_monomorphic, &handle_monomorphic, &extra_checks); GotoUnless(is_monomorphic, &extra_checks);
Bind(&handle_monomorphic); // The compare above could have been a SMI/SMI comparison. Guard against
{ // this convincing us that we have a monomorphic JSFunction.
// The compare above could have been a SMI/SMI comparison. Guard against Node* is_smi = TaggedIsSmi(function);
// this convincing us that we have a monomorphic JSFunction. Branch(is_smi, &extra_checks, &call_function);
Node* is_smi = TaggedIsSmi(function);
GotoIf(is_smi, &extra_checks);
Bind(&call_function);
{
// Increment the call count. // Increment the call count.
IncrementCallCount(type_feedback_vector, slot_id); IncrementCallCount(type_feedback_vector, slot_id);
...@@ -577,56 +577,56 @@ Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context, ...@@ -577,56 +577,56 @@ Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context,
Bind(&extra_checks); Bind(&extra_checks);
{ {
Label check_initialized(this, Label::kDeferred), mark_megamorphic(this), Label check_initialized(this), mark_megamorphic(this),
check_allocation_site(this), create_allocation_site(this);
create_allocation_site(this, Label::kDeferred);
// Check if it is a megamorphic target Comment("check if megamorphic");
// Check if it is a megamorphic target.
Node* is_megamorphic = WordEqual( Node* is_megamorphic = WordEqual(
feedback_element, feedback_element,
HeapConstant(TypeFeedbackVector::MegamorphicSentinel(isolate()))); HeapConstant(TypeFeedbackVector::MegamorphicSentinel(isolate())));
Branch(is_megamorphic, &call, &check_allocation_site); GotoIf(is_megamorphic, &call);
Bind(&check_allocation_site); Comment("check if it is an allocation site");
{ Node* is_allocation_site = WordEqual(
Node* is_allocation_site = LoadMap(feedback_element), LoadRoot(Heap::kAllocationSiteMapRootIndex));
WordEqual(LoadMap(feedback_element), GotoUnless(is_allocation_site, &check_initialized);
LoadRoot(Heap::kAllocationSiteMapRootIndex));
GotoUnless(is_allocation_site, &check_initialized);
// If it is not the Array() function, mark megamorphic. // If it is not the Array() function, mark megamorphic.
Node* context_slot = Node* context_slot =
LoadFixedArrayElement(LoadNativeContext(context), LoadFixedArrayElement(LoadNativeContext(context),
Int32Constant(Context::ARRAY_FUNCTION_INDEX)); Int32Constant(Context::ARRAY_FUNCTION_INDEX));
Node* is_array_function = WordEqual(context_slot, function); Node* is_array_function = WordEqual(context_slot, function);
GotoUnless(is_array_function, &mark_megamorphic); GotoUnless(is_array_function, &mark_megamorphic);
// It is a monomorphic Array function. Increment the call count. // It is a monomorphic Array function. Increment the call count.
IncrementCallCount(type_feedback_vector, slot_id); IncrementCallCount(type_feedback_vector, slot_id);
// Call ArrayConstructorStub. // Call ArrayConstructorStub.
Callable callable_call = Callable callable_call =
CodeFactory::InterpreterPushArgsAndConstructArray(isolate()); CodeFactory::InterpreterPushArgsAndConstructArray(isolate());
Node* code_target_call = HeapConstant(callable_call.code()); Node* code_target_call = HeapConstant(callable_call.code());
Node* ret_value = Node* ret_value =
CallStub(callable_call.descriptor(), code_target_call, context, CallStub(callable_call.descriptor(), code_target_call, context,
arg_count, function, feedback_element, first_arg); arg_count, function, feedback_element, first_arg);
return_value.Bind(ret_value); return_value.Bind(ret_value);
Goto(&end); Goto(&end);
}
Bind(&check_initialized); Bind(&check_initialized);
{ {
Label possibly_monomorphic(this); Comment("check if uninitialized");
// Check if it is uninitialized. // Check if it is uninitialized target first.
Node* is_uninitialized = WordEqual( Node* is_uninitialized = WordEqual(
feedback_element, feedback_element,
HeapConstant(TypeFeedbackVector::UninitializedSentinel(isolate()))); HeapConstant(TypeFeedbackVector::UninitializedSentinel(isolate())));
GotoUnless(is_uninitialized, &mark_megamorphic); GotoUnless(is_uninitialized, &mark_megamorphic);
Comment("handle_unitinitialized");
// If it is not a JSFunction mark it as megamorphic.
Node* is_smi = TaggedIsSmi(function); Node* is_smi = TaggedIsSmi(function);
GotoIf(is_smi, &mark_megamorphic); GotoIf(is_smi, &mark_megamorphic);
// Check if function is an object of JSFunction type // Check if function is an object of JSFunction type.
Node* instance_type = LoadInstanceType(function); Node* instance_type = LoadInstanceType(function);
Node* is_js_function = Node* is_js_function =
WordEqual(instance_type, Int32Constant(JS_FUNCTION_TYPE)); WordEqual(instance_type, Int32Constant(JS_FUNCTION_TYPE));
...@@ -639,7 +639,7 @@ Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context, ...@@ -639,7 +639,7 @@ Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context,
Node* is_array_function = WordEqual(context_slot, function); Node* is_array_function = WordEqual(context_slot, function);
GotoIf(is_array_function, &create_allocation_site); GotoIf(is_array_function, &create_allocation_site);
// Check if the function belongs to the same native context // Check if the function belongs to the same native context.
Node* native_context = LoadNativeContext( Node* native_context = LoadNativeContext(
LoadObjectField(function, JSFunction::kContextOffset)); LoadObjectField(function, JSFunction::kContextOffset));
Node* is_same_native_context = Node* is_same_native_context =
...@@ -678,22 +678,9 @@ Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context, ...@@ -678,22 +678,9 @@ Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context,
} }
} }
Bind(&call_function);
{
// Increment the call count.
IncrementCallCount(type_feedback_vector, slot_id);
Callable callable_call = CodeFactory::InterpreterPushArgsAndCall(
isolate(), tail_call_mode, CallableType::kJSFunction);
Node* code_target_call = HeapConstant(callable_call.code());
Node* ret_value = CallStub(callable_call.descriptor(), code_target_call,
context, arg_count, first_arg, function);
return_value.Bind(ret_value);
Goto(&end);
}
Bind(&call); Bind(&call);
{ {
Comment("Increment call count and call using Call builtin");
// Increment the call count. // Increment the call count.
IncrementCallCount(type_feedback_vector, slot_id); IncrementCallCount(type_feedback_vector, slot_id);
...@@ -725,10 +712,10 @@ Node* InterpreterAssembler::CallConstruct(Node* constructor, Node* context, ...@@ -725,10 +712,10 @@ Node* InterpreterAssembler::CallConstruct(Node* constructor, Node* context,
Node* new_target, Node* first_arg, Node* new_target, Node* first_arg,
Node* arg_count, Node* slot_id, Node* arg_count, Node* slot_id,
Node* type_feedback_vector) { Node* type_feedback_vector) {
Label call_construct(this), js_function(this), end(this);
Variable return_value(this, MachineRepresentation::kTagged); Variable return_value(this, MachineRepresentation::kTagged);
Variable allocation_feedback(this, MachineRepresentation::kTagged); Variable allocation_feedback(this, MachineRepresentation::kTagged);
allocation_feedback.Bind(UndefinedConstant()); Label call_construct_function(this, &allocation_feedback),
extra_checks(this, Label::kDeferred), call_construct(this), end(this);
// Slot id of 0 is used to indicate no type feedback is available. // Slot id of 0 is used to indicate no type feedback is available.
STATIC_ASSERT(TypeFeedbackVector::kReservedIndexCount > 0); STATIC_ASSERT(TypeFeedbackVector::kReservedIndexCount > 0);
...@@ -743,132 +730,118 @@ Node* InterpreterAssembler::CallConstruct(Node* constructor, Node* context, ...@@ -743,132 +730,118 @@ Node* InterpreterAssembler::CallConstruct(Node* constructor, Node* context,
Node* instance_type = LoadInstanceType(constructor); Node* instance_type = LoadInstanceType(constructor);
Node* is_js_function = Node* is_js_function =
WordEqual(instance_type, Int32Constant(JS_FUNCTION_TYPE)); WordEqual(instance_type, Int32Constant(JS_FUNCTION_TYPE));
Branch(is_js_function, &js_function, &call_construct); GotoUnless(is_js_function, &call_construct);
Bind(&js_function); // Check if it is a monomorphic constructor.
Node* feedback_element = LoadFixedArrayElement(type_feedback_vector, slot_id);
Node* feedback_value = LoadWeakCellValueUnchecked(feedback_element);
Node* is_monomorphic = WordEqual(constructor, feedback_value);
allocation_feedback.Bind(UndefinedConstant());
Branch(is_monomorphic, &call_construct_function, &extra_checks);
Bind(&call_construct_function);
{
Comment("call using callConstructFunction");
IncrementCallCount(type_feedback_vector, slot_id);
Callable callable_function = CodeFactory::InterpreterPushArgsAndConstruct(
isolate(), CallableType::kJSFunction);
return_value.Bind(CallStub(callable_function.descriptor(),
HeapConstant(callable_function.code()), context,
arg_count, new_target, constructor,
allocation_feedback.value(), first_arg));
Goto(&end);
}
Bind(&extra_checks);
{ {
// Cache the called function in a feedback vector slot. Cache states Label check_allocation_site(this), check_initialized(this),
// are uninitialized, monomorphic (indicated by a JSFunction), and initialize(this), mark_megamorphic(this);
// megamorphic.
// TODO(mythria/v8:5210): Check if it is better to mark extra_checks as a // Check if it is a megamorphic target.
// deferred block so that call_construct_function will be scheduled. Comment("check if megamorphic");
Label extra_checks(this), call_construct_function(this); Node* is_megamorphic = WordEqual(
feedback_element,
Node* feedback_element = HeapConstant(TypeFeedbackVector::MegamorphicSentinel(isolate())));
LoadFixedArrayElement(type_feedback_vector, slot_id); GotoIf(is_megamorphic, &call_construct_function);
Node* feedback_value = LoadWeakCellValueUnchecked(feedback_element);
Node* is_monomorphic = WordEqual(constructor, feedback_value); Comment("check if weak cell");
Branch(is_monomorphic, &call_construct_function, &extra_checks); Node* is_weak_cell = WordEqual(LoadMap(feedback_element),
LoadRoot(Heap::kWeakCellMapRootIndex));
Bind(&extra_checks); GotoUnless(is_weak_cell, &check_allocation_site);
// If the weak cell is cleared, we have a new chance to become
// monomorphic.
Comment("check if weak cell is cleared");
Node* is_smi = TaggedIsSmi(feedback_value);
Branch(is_smi, &initialize, &mark_megamorphic);
Bind(&check_allocation_site);
{ {
Label mark_megamorphic(this), initialize(this), Comment("check if it is an allocation site");
check_allocation_site(this), check_initialized(this), Node* is_allocation_site =
set_alloc_feedback_and_call(this); WordEqual(LoadObjectField(feedback_element, 0),
{ LoadRoot(Heap::kAllocationSiteMapRootIndex));
// Check if it is a megamorphic target GotoUnless(is_allocation_site, &check_initialized);
Comment("check if megamorphic");
Node* is_megamorphic = WordEqual(
feedback_element,
HeapConstant(TypeFeedbackVector::MegamorphicSentinel(isolate())));
GotoIf(is_megamorphic, &call_construct_function);
Comment("check if weak cell");
Node* is_weak_cell = WordEqual(LoadMap(feedback_element),
LoadRoot(Heap::kWeakCellMapRootIndex));
GotoUnless(is_weak_cell, &check_allocation_site);
// If the weak cell is cleared, we have a new chance to become
// monomorphic.
Comment("check if weak cell is cleared");
Node* is_smi = TaggedIsSmi(feedback_value);
Branch(is_smi, &initialize, &mark_megamorphic);
}
Bind(&check_allocation_site); // Make sure the function is the Array() function.
{ Node* context_slot =
Comment("check if it is an allocation site"); LoadFixedArrayElement(LoadNativeContext(context),
Node* is_allocation_site = Int32Constant(Context::ARRAY_FUNCTION_INDEX));
WordEqual(LoadObjectField(feedback_element, 0), Node* is_array_function = WordEqual(context_slot, constructor);
LoadRoot(Heap::kAllocationSiteMapRootIndex)); GotoUnless(is_array_function, &mark_megamorphic);
GotoUnless(is_allocation_site, &check_initialized);
// Make sure the function is the Array() function
Node* context_slot =
LoadFixedArrayElement(LoadNativeContext(context),
Int32Constant(Context::ARRAY_FUNCTION_INDEX));
Node* is_array_function = WordEqual(context_slot, constructor);
Branch(is_array_function, &set_alloc_feedback_and_call,
&mark_megamorphic);
}
Bind(&set_alloc_feedback_and_call); allocation_feedback.Bind(feedback_element);
{ Goto(&call_construct_function);
allocation_feedback.Bind(feedback_element); }
Goto(&call_construct_function);
}
Bind(&check_initialized); Bind(&check_initialized);
{ {
// Check if it is uninitialized. // Check if it is uninitialized.
Comment("check if uninitialized"); Comment("check if uninitialized");
Node* is_uninitialized = WordEqual( Node* is_uninitialized = WordEqual(
feedback_element, LoadRoot(Heap::kuninitialized_symbolRootIndex)); feedback_element, LoadRoot(Heap::kuninitialized_symbolRootIndex));
Branch(is_uninitialized, &initialize, &mark_megamorphic); Branch(is_uninitialized, &initialize, &mark_megamorphic);
} }
Bind(&initialize); Bind(&initialize);
{ {
Label create_weak_cell(this), create_allocation_site(this); Label create_allocation_site(this), create_weak_cell(this);
Comment("initialize the feedback element"); Comment("initialize the feedback element");
// Check that it is the Array() function. // Create an allocation site if the function is an array function,
Node* context_slot = // otherwise create a weak cell.
LoadFixedArrayElement(LoadNativeContext(context), Node* context_slot =
Int32Constant(Context::ARRAY_FUNCTION_INDEX)); LoadFixedArrayElement(LoadNativeContext(context),
Node* is_array_function = WordEqual(context_slot, constructor); Int32Constant(Context::ARRAY_FUNCTION_INDEX));
Branch(is_array_function, &create_allocation_site, &create_weak_cell); Node* is_array_function = WordEqual(context_slot, constructor);
Branch(is_array_function, &create_allocation_site, &create_weak_cell);
Bind(&create_allocation_site);
{
Node* site = CreateAllocationSiteInFeedbackVector(
type_feedback_vector, SmiTag(slot_id));
allocation_feedback.Bind(site);
Goto(&call_construct_function);
}
Bind(&create_weak_cell); Bind(&create_allocation_site);
{ {
CreateWeakCellInFeedbackVector(type_feedback_vector, SmiTag(slot_id), Node* site = CreateAllocationSiteInFeedbackVector(type_feedback_vector,
constructor); SmiTag(slot_id));
Goto(&call_construct_function); allocation_feedback.Bind(site);
} Goto(&call_construct_function);
} }
Bind(&mark_megamorphic); Bind(&create_weak_cell);
{ {
// MegamorphicSentinel is an immortal immovable object so CreateWeakCellInFeedbackVector(type_feedback_vector, SmiTag(slot_id),
// write-barrier is not needed. constructor);
Comment("transition to megamorphic");
DCHECK(
Heap::RootIsImmortalImmovable(Heap::kmegamorphic_symbolRootIndex));
StoreFixedArrayElement(
type_feedback_vector, slot_id,
HeapConstant(TypeFeedbackVector::MegamorphicSentinel(isolate())),
SKIP_WRITE_BARRIER);
Goto(&call_construct_function); Goto(&call_construct_function);
} }
} }
Bind(&call_construct_function); Bind(&mark_megamorphic);
{ {
Comment("call using callConstructFunction"); // MegamorphicSentinel is an immortal immovable object so
IncrementCallCount(type_feedback_vector, slot_id); // write-barrier is not needed.
Callable callable_function = CodeFactory::InterpreterPushArgsAndConstruct( Comment("transition to megamorphic");
isolate(), CallableType::kJSFunction); DCHECK(Heap::RootIsImmortalImmovable(Heap::kmegamorphic_symbolRootIndex));
return_value.Bind(CallStub(callable_function.descriptor(), StoreFixedArrayElement(
HeapConstant(callable_function.code()), type_feedback_vector, slot_id,
context, arg_count, new_target, constructor, HeapConstant(TypeFeedbackVector::MegamorphicSentinel(isolate())),
allocation_feedback.value(), first_arg)); SKIP_WRITE_BARRIER);
Goto(&end); Goto(&call_construct_function);
} }
} }
......
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