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:
zhengxing.li 2015-11-09 06:05:35 -08:00 committed by Commit bot
parent c701228534
commit 9acf00c78d
3 changed files with 77 additions and 63 deletions

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);