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,
WeakCell::kValueOffset == Symbol::kHashFieldSlot);
Variable return_value(this, MachineRepresentation::kTagged);
Label handle_monomorphic(this), extra_checks(this), end(this), call(this),
call_function(this);
Label call_function(this), extra_checks(this, Label::kDeferred), call(this),
end(this);
// The checks. First, does function match the recorded monomorphic target?
Node* feedback_element = LoadFixedArrayElement(type_feedback_vector, slot_id);
Node* feedback_value = LoadWeakCellValueUnchecked(feedback_element);
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.
Node* is_smi = TaggedIsSmi(function);
GotoIf(is_smi, &extra_checks);
// The compare above could have been a SMI/SMI comparison. Guard against
// this convincing us that we have a monomorphic JSFunction.
Node* is_smi = TaggedIsSmi(function);
Branch(is_smi, &extra_checks, &call_function);
Bind(&call_function);
{
// Increment the call count.
IncrementCallCount(type_feedback_vector, slot_id);
......@@ -577,56 +577,56 @@ Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context,
Bind(&extra_checks);
{
Label check_initialized(this, Label::kDeferred), mark_megamorphic(this),
check_allocation_site(this),
create_allocation_site(this, Label::kDeferred);
// Check if it is a megamorphic target
Label check_initialized(this), mark_megamorphic(this),
create_allocation_site(this);
Comment("check if megamorphic");
// Check if it is a megamorphic target.
Node* is_megamorphic = WordEqual(
feedback_element,
HeapConstant(TypeFeedbackVector::MegamorphicSentinel(isolate())));
Branch(is_megamorphic, &call, &check_allocation_site);
GotoIf(is_megamorphic, &call);
Bind(&check_allocation_site);
{
Node* is_allocation_site =
WordEqual(LoadMap(feedback_element),
LoadRoot(Heap::kAllocationSiteMapRootIndex));
GotoUnless(is_allocation_site, &check_initialized);
Comment("check if it is an allocation site");
Node* is_allocation_site = WordEqual(
LoadMap(feedback_element), LoadRoot(Heap::kAllocationSiteMapRootIndex));
GotoUnless(is_allocation_site, &check_initialized);
// If it is not the Array() function, mark megamorphic.
Node* context_slot =
LoadFixedArrayElement(LoadNativeContext(context),
Int32Constant(Context::ARRAY_FUNCTION_INDEX));
Node* is_array_function = WordEqual(context_slot, function);
GotoUnless(is_array_function, &mark_megamorphic);
// If it is not the Array() function, mark megamorphic.
Node* context_slot =
LoadFixedArrayElement(LoadNativeContext(context),
Int32Constant(Context::ARRAY_FUNCTION_INDEX));
Node* is_array_function = WordEqual(context_slot, function);
GotoUnless(is_array_function, &mark_megamorphic);
// It is a monomorphic Array function. Increment the call count.
IncrementCallCount(type_feedback_vector, slot_id);
// Call ArrayConstructorStub.
Callable callable_call =
CodeFactory::InterpreterPushArgsAndConstructArray(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);
}
// It is a monomorphic Array function. Increment the call count.
IncrementCallCount(type_feedback_vector, slot_id);
// Call ArrayConstructorStub.
Callable callable_call =
CodeFactory::InterpreterPushArgsAndConstructArray(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);
Bind(&check_initialized);
{
Label possibly_monomorphic(this);
// Check if it is uninitialized.
Comment("check if uninitialized");
// Check if it is uninitialized target first.
Node* is_uninitialized = WordEqual(
feedback_element,
HeapConstant(TypeFeedbackVector::UninitializedSentinel(isolate())));
GotoUnless(is_uninitialized, &mark_megamorphic);
Comment("handle_unitinitialized");
// 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
// Check if function is an object of JSFunction type.
Node* instance_type = LoadInstanceType(function);
Node* is_js_function =
WordEqual(instance_type, Int32Constant(JS_FUNCTION_TYPE));
......@@ -639,7 +639,7 @@ Node* InterpreterAssembler::CallJSWithFeedback(Node* function, Node* context,
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
// Check if the function belongs to the same native context.
Node* native_context = LoadNativeContext(
LoadObjectField(function, JSFunction::kContextOffset));
Node* is_same_native_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);
{
Comment("Increment call count and call using Call builtin");
// Increment the call count.
IncrementCallCount(type_feedback_vector, slot_id);
......@@ -725,10 +712,10 @@ Node* InterpreterAssembler::CallConstruct(Node* constructor, Node* context,
Node* new_target, Node* first_arg,
Node* arg_count, Node* slot_id,
Node* type_feedback_vector) {
Label call_construct(this), js_function(this), end(this);
Variable return_value(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.
STATIC_ASSERT(TypeFeedbackVector::kReservedIndexCount > 0);
......@@ -743,132 +730,118 @@ Node* InterpreterAssembler::CallConstruct(Node* constructor, Node* context,
Node* instance_type = LoadInstanceType(constructor);
Node* is_js_function =
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
// are uninitialized, monomorphic (indicated by a JSFunction), and
// megamorphic.
// TODO(mythria/v8:5210): Check if it is better to mark extra_checks as a
// deferred block so that call_construct_function will be scheduled.
Label extra_checks(this), call_construct_function(this);
Node* feedback_element =
LoadFixedArrayElement(type_feedback_vector, slot_id);
Node* feedback_value = LoadWeakCellValueUnchecked(feedback_element);
Node* is_monomorphic = WordEqual(constructor, feedback_value);
Branch(is_monomorphic, &call_construct_function, &extra_checks);
Bind(&extra_checks);
Label check_allocation_site(this), check_initialized(this),
initialize(this), mark_megamorphic(this);
// Check if it is a megamorphic target.
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);
{
Label mark_megamorphic(this), initialize(this),
check_allocation_site(this), check_initialized(this),
set_alloc_feedback_and_call(this);
{
// Check if it is a megamorphic target
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);
}
Comment("check if it is an allocation site");
Node* is_allocation_site =
WordEqual(LoadObjectField(feedback_element, 0),
LoadRoot(Heap::kAllocationSiteMapRootIndex));
GotoUnless(is_allocation_site, &check_initialized);
Bind(&check_allocation_site);
{
Comment("check if it is an allocation site");
Node* is_allocation_site =
WordEqual(LoadObjectField(feedback_element, 0),
LoadRoot(Heap::kAllocationSiteMapRootIndex));
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);
}
// 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);
GotoUnless(is_array_function, &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);
{
// Check if it is uninitialized.
Comment("check if uninitialized");
Node* is_uninitialized = WordEqual(
feedback_element, LoadRoot(Heap::kuninitialized_symbolRootIndex));
Branch(is_uninitialized, &initialize, &mark_megamorphic);
}
Bind(&check_initialized);
{
// Check if it is uninitialized.
Comment("check if uninitialized");
Node* is_uninitialized = WordEqual(
feedback_element, LoadRoot(Heap::kuninitialized_symbolRootIndex));
Branch(is_uninitialized, &initialize, &mark_megamorphic);
}
Bind(&initialize);
{
Label create_weak_cell(this), create_allocation_site(this);
Comment("initialize the feedback element");
// Check that it 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, &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(&initialize);
{
Label create_allocation_site(this), create_weak_cell(this);
Comment("initialize the feedback element");
// Create an allocation site if the function is an array function,
// otherwise create a weak cell.
Node* context_slot =
LoadFixedArrayElement(LoadNativeContext(context),
Int32Constant(Context::ARRAY_FUNCTION_INDEX));
Node* is_array_function = WordEqual(context_slot, constructor);
Branch(is_array_function, &create_allocation_site, &create_weak_cell);
Bind(&create_weak_cell);
{
CreateWeakCellInFeedbackVector(type_feedback_vector, SmiTag(slot_id),
constructor);
Goto(&call_construct_function);
}
Bind(&create_allocation_site);
{
Node* site = CreateAllocationSiteInFeedbackVector(type_feedback_vector,
SmiTag(slot_id));
allocation_feedback.Bind(site);
Goto(&call_construct_function);
}
Bind(&mark_megamorphic);
Bind(&create_weak_cell);
{
// MegamorphicSentinel is an immortal immovable object so
// write-barrier is not needed.
Comment("transition to megamorphic");
DCHECK(
Heap::RootIsImmortalImmovable(Heap::kmegamorphic_symbolRootIndex));
StoreFixedArrayElement(
type_feedback_vector, slot_id,
HeapConstant(TypeFeedbackVector::MegamorphicSentinel(isolate())),
SKIP_WRITE_BARRIER);
CreateWeakCellInFeedbackVector(type_feedback_vector, SmiTag(slot_id),
constructor);
Goto(&call_construct_function);
}
}
Bind(&call_construct_function);
Bind(&mark_megamorphic);
{
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);
// MegamorphicSentinel is an immortal immovable object so
// write-barrier is not needed.
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);
}
}
......
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