Commit 37d1dd82 authored by zhengxing.li's avatar zhengxing.li Committed by Commit bot

X87: [runtime] Introduce dedicated JSBoundFunction to represent bound functions.

  port 97def807 (r33044)

  original commit message:
  According to the ES2015 specification, bound functions are exotic
  objects, and thus don't need to be implemented as JSFunctions. So
  we introduce a new JSBoundFunction type to represent bound functions
  and make them optimizable. This already improves the performance of
  calling or constructing bound functions by 10-100x depending on the
  use case because we avoid the crazy dance between JavaScript and C++
  that was implemented in v8natives.js previously.

  There's still room for improvement in the performance of actually
  creating bound functions, which is also relevant in practice, but
  we already have a plan how to accomplish that later.

  The mips/mips64 ports were contributed by akos.palfi@imgtec.com.

BUG=

Review URL: https://codereview.chromium.org/1548253002

Cr-Commit-Position: refs/heads/master@{#33046}
parent fa987955
...@@ -3024,9 +3024,9 @@ void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) { ...@@ -3024,9 +3024,9 @@ void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) {
&if_true, &if_false, &fall_through); &if_true, &if_false, &fall_through);
__ JumpIfSmi(eax, if_false); __ JumpIfSmi(eax, if_false);
__ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx); __ CmpObjectType(eax, FIRST_FUNCTION_TYPE, ebx);
PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
Split(equal, if_true, if_false, fall_through); Split(above_equal, if_true, if_false, fall_through);
context()->Plug(if_true, if_false); context()->Plug(if_true, if_false);
} }
......
...@@ -1773,6 +1773,117 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, ...@@ -1773,6 +1773,117 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
} }
namespace {
void Generate_PushBoundArguments(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : the number of arguments (not including the receiver)
// -- edx : new.target (only in case of [[Construct]])
// -- edi : target (checked to be a JSBoundFunction)
// -----------------------------------
// Load [[BoundArguments]] into ecx and length of that into ebx.
Label no_bound_arguments;
__ mov(ecx, FieldOperand(edi, JSBoundFunction::kBoundArgumentsOffset));
__ mov(ebx, FieldOperand(ecx, FixedArray::kLengthOffset));
__ SmiUntag(ebx);
__ test(ebx, ebx);
__ j(zero, &no_bound_arguments);
{
// ----------- S t a t e -------------
// -- eax : the number of arguments (not including the receiver)
// -- edx : new.target (only in case of [[Construct]])
// -- edi : target (checked to be a JSBoundFunction)
// -- ecx : the [[BoundArguments]] (implemented as FixedArray)
// -- ebx : the number of [[BoundArguments]]
// -----------------------------------
// Reserve stack space for the [[BoundArguments]].
{
Label done;
__ lea(ecx, Operand(ebx, times_pointer_size, 0));
__ sub(esp, ecx);
// Check the stack for overflow. We are not trying to catch interruptions
// (i.e. debug break and preemption) here, so check the "real stack
// limit".
__ CompareRoot(esp, ecx, Heap::kRealStackLimitRootIndex);
__ j(greater, &done, Label::kNear); // Signed comparison.
// Restore the stack pointer.
__ lea(esp, Operand(esp, ebx, times_pointer_size, 0));
{
FrameScope scope(masm, StackFrame::MANUAL);
__ EnterFrame(StackFrame::INTERNAL);
__ CallRuntime(Runtime::kThrowStackOverflow, 0);
}
__ bind(&done);
}
// Adjust effective number of arguments to include return address.
__ inc(eax);
// Relocate arguments and return address down the stack.
{
Label loop;
__ Set(ecx, 0);
__ lea(ebx, Operand(esp, ebx, times_pointer_size, 0));
__ bind(&loop);
__ fld_s(Operand(ebx, ecx, times_pointer_size, 0));
__ fstp_s(Operand(esp, ecx, times_pointer_size, 0));
__ inc(ecx);
__ cmp(ecx, eax);
__ j(less, &loop);
}
// Copy [[BoundArguments]] to the stack (below the arguments).
{
Label loop;
__ mov(ecx, FieldOperand(edi, JSBoundFunction::kBoundArgumentsOffset));
__ mov(ebx, FieldOperand(ecx, FixedArray::kLengthOffset));
__ SmiUntag(ebx);
__ bind(&loop);
__ dec(ebx);
__ fld_s(
FieldOperand(ecx, ebx, times_pointer_size, FixedArray::kHeaderSize));
__ fstp_s(Operand(esp, eax, times_pointer_size, 0));
__ lea(eax, Operand(eax, 1));
__ j(greater, &loop);
}
// Adjust effective number of arguments (eax contains the number of
// arguments from the call plus return address plus the number of
// [[BoundArguments]]), so we need to subtract one for the return address.
__ dec(eax);
}
__ bind(&no_bound_arguments);
}
} // namespace
// static
void Builtins::Generate_CallBoundFunction(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : the number of arguments (not including the receiver)
// -- edi : the function to call (checked to be a JSBoundFunction)
// -----------------------------------
__ AssertBoundFunction(edi);
// Patch the receiver to [[BoundThis]].
__ mov(ebx, FieldOperand(edi, JSBoundFunction::kBoundThisOffset));
__ mov(Operand(esp, eax, times_pointer_size, kPointerSize), ebx);
// Push the [[BoundArguments]] onto the stack.
Generate_PushBoundArguments(masm);
// Call the [[BoundTargetFunction]] via the Call builtin.
__ mov(edi, FieldOperand(edi, JSBoundFunction::kBoundTargetFunctionOffset));
__ mov(ecx, Operand::StaticVariable(ExternalReference(
Builtins::kCall_ReceiverIsAny, masm->isolate())));
__ lea(ecx, FieldOperand(ecx, Code::kHeaderSize));
__ jmp(ecx);
}
// static // static
void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) { void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
// ----------- S t a t e ------------- // ----------- S t a t e -------------
...@@ -1786,6 +1897,9 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) { ...@@ -1786,6 +1897,9 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
__ j(equal, masm->isolate()->builtins()->CallFunction(mode), __ j(equal, masm->isolate()->builtins()->CallFunction(mode),
RelocInfo::CODE_TARGET); RelocInfo::CODE_TARGET);
__ CmpInstanceType(ecx, JS_BOUND_FUNCTION_TYPE);
__ j(equal, masm->isolate()->builtins()->CallBoundFunction(),
RelocInfo::CODE_TARGET);
__ CmpInstanceType(ecx, JS_PROXY_TYPE); __ CmpInstanceType(ecx, JS_PROXY_TYPE);
__ j(not_equal, &non_function); __ j(not_equal, &non_function);
...@@ -1846,6 +1960,36 @@ void Builtins::Generate_ConstructFunction(MacroAssembler* masm) { ...@@ -1846,6 +1960,36 @@ void Builtins::Generate_ConstructFunction(MacroAssembler* masm) {
} }
// static
void Builtins::Generate_ConstructBoundFunction(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : the number of arguments (not including the receiver)
// -- edx : the new target (checked to be a constructor)
// -- edi : the constructor to call (checked to be a JSBoundFunction)
// -----------------------------------
__ AssertBoundFunction(edi);
// Push the [[BoundArguments]] onto the stack.
Generate_PushBoundArguments(masm);
// Patch new.target to [[BoundTargetFunction]] if new.target equals target.
{
Label done;
__ cmp(edi, edx);
__ j(not_equal, &done, Label::kNear);
__ mov(edx, FieldOperand(edi, JSBoundFunction::kBoundTargetFunctionOffset));
__ bind(&done);
}
// Construct the [[BoundTargetFunction]] via the Construct builtin.
__ mov(edi, FieldOperand(edi, JSBoundFunction::kBoundTargetFunctionOffset));
__ mov(ecx, Operand::StaticVariable(
ExternalReference(Builtins::kConstruct, masm->isolate())));
__ lea(ecx, FieldOperand(ecx, Code::kHeaderSize));
__ jmp(ecx);
}
// static // static
void Builtins::Generate_ConstructProxy(MacroAssembler* masm) { void Builtins::Generate_ConstructProxy(MacroAssembler* masm) {
// ----------- S t a t e ------------- // ----------- S t a t e -------------
...@@ -1890,6 +2034,12 @@ void Builtins::Generate_Construct(MacroAssembler* masm) { ...@@ -1890,6 +2034,12 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
__ test_b(FieldOperand(ecx, Map::kBitFieldOffset), 1 << Map::kIsConstructor); __ test_b(FieldOperand(ecx, Map::kBitFieldOffset), 1 << Map::kIsConstructor);
__ j(zero, &non_constructor, Label::kNear); __ j(zero, &non_constructor, Label::kNear);
// Only dispatch to bound functions after checking whether they are
// constructors.
__ CmpInstanceType(ecx, JS_BOUND_FUNCTION_TYPE);
__ j(equal, masm->isolate()->builtins()->ConstructBoundFunction(),
RelocInfo::CODE_TARGET);
// Only dispatch to proxies after checking whether they are constructors. // Only dispatch to proxies after checking whether they are constructors.
__ CmpInstanceType(ecx, JS_PROXY_TYPE); __ CmpInstanceType(ecx, JS_PROXY_TYPE);
__ j(equal, masm->isolate()->builtins()->ConstructProxy(), __ j(equal, masm->isolate()->builtins()->ConstructProxy(),
......
...@@ -2269,14 +2269,6 @@ void InstanceOfStub::Generate(MacroAssembler* masm) { ...@@ -2269,14 +2269,6 @@ void InstanceOfStub::Generate(MacroAssembler* masm) {
static_cast<uint8_t>(1 << Map::kHasNonInstancePrototype)); static_cast<uint8_t>(1 << Map::kHasNonInstancePrototype));
__ j(not_zero, &slow_case); __ j(not_zero, &slow_case);
// Ensure that {function} is not bound.
Register const shared_info = scratch;
__ mov(shared_info,
FieldOperand(function, JSFunction::kSharedFunctionInfoOffset));
__ BooleanBitTest(shared_info, SharedFunctionInfo::kCompilerHintsOffset,
SharedFunctionInfo::kBoundFunction);
__ j(not_zero, &slow_case);
// Get the "prototype" (or initial map) of the {function}. // Get the "prototype" (or initial map) of the {function}.
__ mov(function_prototype, __ mov(function_prototype,
FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset)); FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
......
...@@ -793,6 +793,18 @@ void MacroAssembler::AssertFunction(Register object) { ...@@ -793,6 +793,18 @@ void MacroAssembler::AssertFunction(Register object) {
} }
void MacroAssembler::AssertBoundFunction(Register object) {
if (emit_debug_code()) {
test(object, Immediate(kSmiTagMask));
Check(not_equal, kOperandIsASmiAndNotABoundFunction);
Push(object);
CmpObjectType(object, JS_BOUND_FUNCTION_TYPE, object);
Pop(object);
Check(equal, kOperandIsNotABoundFunction);
}
}
void MacroAssembler::AssertUndefinedOrAllocationSite(Register object) { void MacroAssembler::AssertUndefinedOrAllocationSite(Register object) {
if (emit_debug_code()) { if (emit_debug_code()) {
Label done_checking; Label done_checking;
......
...@@ -513,6 +513,10 @@ class MacroAssembler: public Assembler { ...@@ -513,6 +513,10 @@ class MacroAssembler: public Assembler {
// Abort execution if argument is not a JSFunction, enabled via --debug-code. // Abort execution if argument is not a JSFunction, enabled via --debug-code.
void AssertFunction(Register object); void AssertFunction(Register object);
// Abort execution if argument is not a JSBoundFunction,
// enabled via --debug-code.
void AssertBoundFunction(Register object);
// Abort execution if argument is not undefined or an AllocationSite, enabled // Abort execution if argument is not undefined or an AllocationSite, enabled
// via --debug-code. // via --debug-code.
void AssertUndefinedOrAllocationSite(Register object); void AssertUndefinedOrAllocationSite(Register object);
......
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