X87: [builtins] Introduce specialized Call/CallFunction builtins.
port 7c3396d01c
(r31871)
original commit message:
Introduce receiver conversion mode specialization for the Call and
CallFunction builtins, so we can specialize the builtin functionality
(actually an optimization only) based on static information from the
callsite (this is basically a superset of the optimizations that were
available with the CallFunctionStub and CallICStub, except that these
optimizations are correct now).
This fixes a regression introduced by the removal of CallFunctionStub,
for programs that call a lot.
BUG=
Review URL: https://codereview.chromium.org/1431133002
Cr-Commit-Position: refs/heads/master@{#31884}
This commit is contained in:
parent
c701228534
commit
9acf00c78d
@ -4161,6 +4161,7 @@ void LCodeGen::DoCallFunction(LCallFunction* instr) {
|
||||
DCHECK(ToRegister(instr->result()).is(eax));
|
||||
|
||||
int arity = instr->arity();
|
||||
ConvertReceiverMode mode = instr->hydrogen()->convert_mode();
|
||||
if (instr->hydrogen()->HasVectorAndSlot()) {
|
||||
Register slot_register = ToRegister(instr->temp_slot());
|
||||
Register vector_register = ToRegister(instr->temp_vector());
|
||||
@ -4175,11 +4176,11 @@ void LCodeGen::DoCallFunction(LCallFunction* instr) {
|
||||
__ mov(slot_register, Immediate(Smi::FromInt(index)));
|
||||
|
||||
Handle<Code> ic =
|
||||
CodeFactory::CallICInOptimizedCode(isolate(), arity).code();
|
||||
CodeFactory::CallICInOptimizedCode(isolate(), arity, mode).code();
|
||||
CallCode(ic, RelocInfo::CODE_TARGET, instr);
|
||||
} else {
|
||||
__ Set(eax, arity);
|
||||
CallCode(isolate()->builtins()->Call(), RelocInfo::CODE_TARGET, instr);
|
||||
CallCode(isolate()->builtins()->Call(mode), RelocInfo::CODE_TARGET, instr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2029,7 +2029,9 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
__ mov(Operand(esp, 2 * kPointerSize), edi);
|
||||
SetCallPosition(expr, 1);
|
||||
__ Set(eax, 1);
|
||||
__ Call(isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
|
||||
__ Call(
|
||||
isolate()->builtins()->Call(ConvertReceiverMode::kNotNullOrUndefined),
|
||||
RelocInfo::CODE_TARGET);
|
||||
|
||||
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
|
||||
__ Drop(1); // The function is still on the stack; drop it.
|
||||
@ -2699,6 +2701,7 @@ void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) {
|
||||
Expression* callee = expr->expression();
|
||||
|
||||
// Get the target function.
|
||||
ConvertReceiverMode convert_mode;
|
||||
if (callee->IsVariableProxy()) {
|
||||
{ StackValueContext context(this);
|
||||
EmitVariableLoad(callee->AsVariableProxy());
|
||||
@ -2707,6 +2710,7 @@ void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) {
|
||||
// Push undefined as receiver. This is patched in the method prologue if it
|
||||
// is a sloppy mode method.
|
||||
__ push(Immediate(isolate()->factory()->undefined_value()));
|
||||
convert_mode = ConvertReceiverMode::kNullOrUndefined;
|
||||
} else {
|
||||
// Load the function from the receiver.
|
||||
DCHECK(callee->IsProperty());
|
||||
@ -2717,9 +2721,10 @@ void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) {
|
||||
// Push the target function under the receiver.
|
||||
__ push(Operand(esp, 0));
|
||||
__ mov(Operand(esp, kPointerSize), eax);
|
||||
convert_mode = ConvertReceiverMode::kNotNullOrUndefined;
|
||||
}
|
||||
|
||||
EmitCall(expr);
|
||||
EmitCall(expr, convert_mode);
|
||||
}
|
||||
|
||||
|
||||
@ -2779,7 +2784,7 @@ void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr,
|
||||
__ push(Operand(esp, 0));
|
||||
__ mov(Operand(esp, kPointerSize), eax);
|
||||
|
||||
EmitCall(expr);
|
||||
EmitCall(expr, ConvertReceiverMode::kNotNullOrUndefined);
|
||||
}
|
||||
|
||||
|
||||
@ -2818,7 +2823,7 @@ void FullCodeGenerator::EmitKeyedSuperCallWithLoadIC(Call* expr) {
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitCall(Call* expr) {
|
||||
void FullCodeGenerator::EmitCall(Call* expr, ConvertReceiverMode mode) {
|
||||
// Load the arguments.
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
int arg_count = args->length();
|
||||
@ -2828,7 +2833,7 @@ void FullCodeGenerator::EmitCall(Call* expr) {
|
||||
|
||||
PrepareForBailoutForId(expr->CallId(), NO_REGISTERS);
|
||||
SetCallPosition(expr, arg_count);
|
||||
Handle<Code> ic = CodeFactory::CallIC(isolate(), arg_count).code();
|
||||
Handle<Code> ic = CodeFactory::CallIC(isolate(), arg_count, mode).code();
|
||||
__ Move(edx, Immediate(SmiFromSlot(expr->CallFeedbackICSlot())));
|
||||
__ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize));
|
||||
// Don't assign a type feedback id to the IC, since type feedback is provided
|
||||
@ -4144,7 +4149,8 @@ void FullCodeGenerator::EmitCallJSRuntimeFunction(CallRuntime* expr) {
|
||||
SetCallPosition(expr, arg_count);
|
||||
__ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize));
|
||||
__ Set(eax, arg_count);
|
||||
__ Call(isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
|
||||
__ Call(isolate()->builtins()->Call(ConvertReceiverMode::kNullOrUndefined),
|
||||
RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1524,30 +1524,21 @@ static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) {
|
||||
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallFunction(MacroAssembler* masm) {
|
||||
void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
ConvertReceiverMode mode) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- eax : the number of arguments (not including the receiver)
|
||||
// -- edi : the function to call (checked to be a JSFunction)
|
||||
// -----------------------------------
|
||||
// See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList)
|
||||
|
||||
Label convert, convert_global_proxy, convert_to_object, done_convert;
|
||||
__ AssertFunction(edi);
|
||||
__ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
|
||||
|
||||
{
|
||||
Label non_class_constructor;
|
||||
// Check whether the current function is a classConstructor.
|
||||
__ test_b(FieldOperand(edx, SharedFunctionInfo::kFunctionKindByteOffset),
|
||||
SharedFunctionInfo::kClassConstructorBitsWithinByte);
|
||||
__ j(zero, &non_class_constructor, Label::kNear);
|
||||
// Step: 2, If we call a classConstructor Function throw a TypeError.
|
||||
{
|
||||
FrameScope frame(masm, StackFrame::INTERNAL);
|
||||
__ CallRuntime(Runtime::kThrowConstructorNonCallableError, 0);
|
||||
}
|
||||
__ bind(&non_class_constructor);
|
||||
}
|
||||
// See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList)
|
||||
// Check that the function is not a "classConstructor".
|
||||
Label class_constructor;
|
||||
__ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ test_b(FieldOperand(edx, SharedFunctionInfo::kFunctionKindByteOffset),
|
||||
SharedFunctionInfo::kClassConstructorBitsWithinByte);
|
||||
__ j(not_zero, &class_constructor);
|
||||
|
||||
// Enter the context of the function; ToObject has to run in the function
|
||||
// context, and we also need to take the global proxy from the function
|
||||
@ -1556,55 +1547,62 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm) {
|
||||
SharedFunctionInfo::kStrictModeByteOffset);
|
||||
__ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
|
||||
// We need to convert the receiver for non-native sloppy mode functions.
|
||||
Label done_convert;
|
||||
__ test_b(FieldOperand(edx, SharedFunctionInfo::kNativeByteOffset),
|
||||
(1 << SharedFunctionInfo::kNativeBitWithinByte) |
|
||||
(1 << SharedFunctionInfo::kStrictModeBitWithinByte));
|
||||
__ j(not_zero, &done_convert);
|
||||
{
|
||||
__ mov(ecx, Operand(esp, eax, times_pointer_size, kPointerSize));
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- eax : the number of arguments (not including the receiver)
|
||||
// -- ecx : the receiver
|
||||
// -- edx : the shared function info.
|
||||
// -- edi : the function to call (checked to be a JSFunction)
|
||||
// -- esi : the function context.
|
||||
// -----------------------------------
|
||||
|
||||
Label convert_receiver;
|
||||
__ JumpIfSmi(ecx, &convert_to_object, Label::kNear);
|
||||
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
|
||||
__ CmpObjectType(ecx, FIRST_JS_RECEIVER_TYPE, ebx);
|
||||
__ j(above_equal, &done_convert);
|
||||
__ JumpIfRoot(ecx, Heap::kUndefinedValueRootIndex, &convert_global_proxy,
|
||||
Label::kNear);
|
||||
__ JumpIfNotRoot(ecx, Heap::kNullValueRootIndex, &convert_to_object,
|
||||
Label::kNear);
|
||||
__ bind(&convert_global_proxy);
|
||||
{
|
||||
if (mode == ConvertReceiverMode::kNullOrUndefined) {
|
||||
// Patch receiver to global proxy.
|
||||
__ LoadGlobalProxy(ecx);
|
||||
} else {
|
||||
Label convert_to_object, convert_receiver;
|
||||
__ mov(ecx, Operand(esp, eax, times_pointer_size, kPointerSize));
|
||||
__ JumpIfSmi(ecx, &convert_to_object, Label::kNear);
|
||||
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
|
||||
__ CmpObjectType(ecx, FIRST_JS_RECEIVER_TYPE, ebx);
|
||||
__ j(above_equal, &done_convert);
|
||||
if (mode != ConvertReceiverMode::kNotNullOrUndefined) {
|
||||
Label convert_global_proxy;
|
||||
__ JumpIfRoot(ecx, Heap::kUndefinedValueRootIndex,
|
||||
&convert_global_proxy, Label::kNear);
|
||||
__ JumpIfNotRoot(ecx, Heap::kNullValueRootIndex, &convert_to_object,
|
||||
Label::kNear);
|
||||
__ bind(&convert_global_proxy);
|
||||
{
|
||||
// Patch receiver to global proxy.
|
||||
__ LoadGlobalProxy(ecx);
|
||||
}
|
||||
__ jmp(&convert_receiver);
|
||||
}
|
||||
__ bind(&convert_to_object);
|
||||
{
|
||||
// Convert receiver using ToObject.
|
||||
// TODO(bmeurer): Inline the allocation here to avoid building the frame
|
||||
// in the fast case? (fall back to AllocateInNewSpace?)
|
||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||
__ SmiTag(eax);
|
||||
__ Push(eax);
|
||||
__ Push(edi);
|
||||
__ mov(eax, ecx);
|
||||
ToObjectStub stub(masm->isolate());
|
||||
__ CallStub(&stub);
|
||||
__ mov(ecx, eax);
|
||||
__ Pop(edi);
|
||||
__ Pop(eax);
|
||||
__ SmiUntag(eax);
|
||||
}
|
||||
__ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ bind(&convert_receiver);
|
||||
}
|
||||
__ jmp(&convert_receiver);
|
||||
__ bind(&convert_to_object);
|
||||
{
|
||||
// Convert receiver using ToObject.
|
||||
// TODO(bmeurer): Inline the allocation here to avoid building the frame
|
||||
// in the fast case? (fall back to AllocateInNewSpace?)
|
||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||
__ SmiTag(eax);
|
||||
__ Push(eax);
|
||||
__ Push(edi);
|
||||
__ mov(eax, ecx);
|
||||
ToObjectStub stub(masm->isolate());
|
||||
__ CallStub(&stub);
|
||||
__ mov(ecx, eax);
|
||||
__ Pop(edi);
|
||||
__ Pop(eax);
|
||||
__ SmiUntag(eax);
|
||||
}
|
||||
__ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ bind(&convert_receiver);
|
||||
__ mov(Operand(esp, eax, times_pointer_size, kPointerSize), ecx);
|
||||
}
|
||||
__ bind(&done_convert);
|
||||
@ -1623,11 +1621,18 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm) {
|
||||
ParameterCount expected(ebx);
|
||||
__ InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset), expected,
|
||||
actual, JUMP_FUNCTION, NullCallWrapper());
|
||||
|
||||
// The function is a "classConstructor", need to raise an exception.
|
||||
__ bind(&class_constructor);
|
||||
{
|
||||
FrameScope frame(masm, StackFrame::INTERNAL);
|
||||
__ CallRuntime(Runtime::kThrowConstructorNonCallableError, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
void Builtins::Generate_Call(MacroAssembler* masm) {
|
||||
void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- eax : the number of arguments (not including the receiver)
|
||||
// -- edi : the target to call (can be any Object).
|
||||
@ -1637,7 +1642,7 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
|
||||
__ JumpIfSmi(edi, &non_callable);
|
||||
__ bind(&non_smi);
|
||||
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
|
||||
__ j(equal, masm->isolate()->builtins()->CallFunction(),
|
||||
__ j(equal, masm->isolate()->builtins()->CallFunction(mode),
|
||||
RelocInfo::CODE_TARGET);
|
||||
__ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
|
||||
__ j(not_equal, &non_function);
|
||||
@ -1658,7 +1663,9 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
|
||||
__ mov(Operand(esp, eax, times_pointer_size, kPointerSize), edi);
|
||||
// Let the "call_as_function_delegate" take care of the rest.
|
||||
__ LoadGlobalFunction(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, edi);
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET);
|
||||
__ Jump(masm->isolate()->builtins()->CallFunction(
|
||||
ConvertReceiverMode::kNotNullOrUndefined),
|
||||
RelocInfo::CODE_TARGET);
|
||||
|
||||
// 3. Call to something that is not callable.
|
||||
__ bind(&non_callable);
|
||||
|
Loading…
Reference in New Issue
Block a user