X87: [runtime] Introduce dedicated JSBoundFunction to represent bound functions.
port 97def8070c
(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}
This commit is contained in:
parent
fa98795500
commit
37d1dd823b
@ -3024,9 +3024,9 @@ void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) {
|
||||
&if_true, &if_false, &fall_through);
|
||||
|
||||
__ JumpIfSmi(eax, if_false);
|
||||
__ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx);
|
||||
__ CmpObjectType(eax, FIRST_FUNCTION_TYPE, ebx);
|
||||
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);
|
||||
}
|
||||
|
150
src/x87/builtins-x87.cc
Executable file → Normal file
150
src/x87/builtins-x87.cc
Executable file → Normal file
@ -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
|
||||
void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
|
||||
// ----------- S t a t e -------------
|
||||
@ -1786,6 +1897,9 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
|
||||
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
|
||||
__ j(equal, masm->isolate()->builtins()->CallFunction(mode),
|
||||
RelocInfo::CODE_TARGET);
|
||||
__ CmpInstanceType(ecx, JS_BOUND_FUNCTION_TYPE);
|
||||
__ j(equal, masm->isolate()->builtins()->CallBoundFunction(),
|
||||
RelocInfo::CODE_TARGET);
|
||||
__ CmpInstanceType(ecx, JS_PROXY_TYPE);
|
||||
__ j(not_equal, &non_function);
|
||||
|
||||
@ -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
|
||||
void Builtins::Generate_ConstructProxy(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
@ -1890,6 +2034,12 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
|
||||
__ test_b(FieldOperand(ecx, Map::kBitFieldOffset), 1 << Map::kIsConstructor);
|
||||
__ 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.
|
||||
__ CmpInstanceType(ecx, JS_PROXY_TYPE);
|
||||
__ j(equal, masm->isolate()->builtins()->ConstructProxy(),
|
||||
|
@ -2269,14 +2269,6 @@ void InstanceOfStub::Generate(MacroAssembler* masm) {
|
||||
static_cast<uint8_t>(1 << Map::kHasNonInstancePrototype));
|
||||
__ 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}.
|
||||
__ mov(function_prototype,
|
||||
FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
|
@ -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) {
|
||||
if (emit_debug_code()) {
|
||||
Label done_checking;
|
||||
|
@ -513,6 +513,10 @@ class MacroAssembler: public Assembler {
|
||||
// Abort execution if argument is not a JSFunction, enabled via --debug-code.
|
||||
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
|
||||
// via --debug-code.
|
||||
void AssertUndefinedOrAllocationSite(Register object);
|
||||
|
Loading…
Reference in New Issue
Block a user