[builtins] Add FastCallFunction builtin that elides some checks
This CL adds a new "Call" stub that can be used by builtins that will call the same JS call-back function often (e.g. compare function in Array.p.sort). The checks have to be done upfront once, but can then be omitted. R=jgruber@chromium.org Bug: v8:7861 Change-Id: Id6e4ca27c3d488a7b1f708cbcb4cbe6cc382513e Reviewed-on: https://chromium-review.googlesource.com/1208574 Commit-Queue: Simon Zünd <szuend@google.com> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Reviewed-by: Camillo Bruni <cbruni@chromium.org> Cr-Commit-Position: refs/heads/master@{#55769}
This commit is contained in:
parent
d830602839
commit
99e13e587e
@ -1724,6 +1724,33 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm,
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
// static
|
||||
// The CSA macro "BranchIfCanUseFastCallFunction" should be used to determine
|
||||
// whether a JSFunction can be called using this stub.
|
||||
void Builtins::Generate_FastCallFunction(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- r0 : the number of arguments (not including the receiver)
|
||||
// -- r1 : the function to call (checked to be a JSFunction)
|
||||
// -----------------------------------
|
||||
__ AssertFunction(r1);
|
||||
__ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- r0 : the number of arguments (not including the receiver)
|
||||
// -- r1 : the function to call (checked to be a JSFunction)
|
||||
// -- cp : the function context.
|
||||
// -----------------------------------
|
||||
|
||||
// On function call, call into the debugger if necessary.
|
||||
__ CheckDebugHook(r1, no_reg, ParameterCount(r0), ParameterCount(r0));
|
||||
__ LoadRoot(r3, Heap::kUndefinedValueRootIndex);
|
||||
|
||||
Register code = kJavaScriptCallCodeStartRegister;
|
||||
__ ldr(code, FieldMemOperand(r1, JSFunction::kCodeOffset));
|
||||
__ add(code, code, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ Jump(code);
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
ConvertReceiverMode mode) {
|
||||
|
@ -2073,6 +2073,34 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm,
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
// static
|
||||
// The CSA macro "BranchIfCanUseFastCallFunction" should be used to determine
|
||||
// whether a JSFunction can be called using this stub.
|
||||
void Builtins::Generate_FastCallFunction(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- x0 : the number of arguments (not including the receiver)
|
||||
// -- x1 : the function to call (checked to be a JSFunction)
|
||||
// -----------------------------------
|
||||
__ AssertFunction(x1);
|
||||
__ Ldr(cp, FieldMemOperand(x1, JSFunction::kContextOffset));
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- x0 : the number of arguments (not including the receiver)
|
||||
// -- x1 : the function to call (checked to be a JSFunction)
|
||||
// -- cp : the function context.
|
||||
// -----------------------------------
|
||||
|
||||
// On function call, call into the debugger if necessary.
|
||||
__ Mov(x2, x0);
|
||||
__ CheckDebugHook(x1, no_reg, ParameterCount(x0), ParameterCount(x2));
|
||||
__ LoadRoot(x3, Heap::kUndefinedValueRootIndex);
|
||||
|
||||
Register code = kJavaScriptCallCodeStartRegister;
|
||||
__ Ldr(code, FieldMemOperand(x1, JSFunction::kCodeOffset));
|
||||
__ Add(code, code, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ Jump(code);
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
ConvertReceiverMode mode) {
|
||||
|
@ -761,6 +761,8 @@ extern macro Call(
|
||||
extern macro Call(
|
||||
Context, Callable, Object, Object, Object, Object, Object, Object): Object;
|
||||
|
||||
extern macro FastCall(Context, Callable, Object, Object): Object;
|
||||
|
||||
extern macro ExtractFixedArray(FixedArrayBase, Smi, Smi, Smi): FixedArrayBase;
|
||||
extern macro ExtractFixedArray(FixedArrayBase, Smi, Smi, Smi,
|
||||
constexpr ExtractFixedArrayFlags): FixedArrayBase;
|
||||
@ -828,6 +830,9 @@ macro NumberIsNaN(number: Number): bool {
|
||||
}
|
||||
|
||||
extern macro BranchIfToBooleanIsTrue(Object): never labels Taken, NotTaken;
|
||||
extern macro BranchIfCanUseFastCallFunction(HeapObject, int32):
|
||||
never labels Taken,
|
||||
NotTaken;
|
||||
|
||||
macro ToBoolean(obj: Object): bool {
|
||||
if (BranchIfToBooleanIsTrue(obj)) {
|
||||
|
@ -46,6 +46,11 @@ void Builtins::Generate_Call_ReceiverIsAny(MacroAssembler* masm) {
|
||||
Generate_Call(masm, ConvertReceiverMode::kAny);
|
||||
}
|
||||
|
||||
void Builtins::Generate_FastCallFunction_ReceiverIsNullOrUndefined(
|
||||
MacroAssembler* masm) {
|
||||
Generate_FastCallFunction(masm);
|
||||
}
|
||||
|
||||
void Builtins::Generate_CallVarargs(MacroAssembler* masm) {
|
||||
Generate_CallOrConstructVarargs(masm, masm->isolate()->builtins()->Call());
|
||||
}
|
||||
@ -230,9 +235,9 @@ void CallOrConstructBuiltinsAssembler::CallOrConstructWithArrayLike(
|
||||
}
|
||||
}
|
||||
|
||||
// Takes a FixedArray of doubles and creates a new FixedArray with those doubles
|
||||
// boxed as HeapNumbers, then tail calls CallVarargs/ConstructVarargs depending
|
||||
// on whether {new_target} was passed.
|
||||
// Takes a FixedArray of doubles and creates a new FixedArray with those
|
||||
// doubles boxed as HeapNumbers, then tail calls CallVarargs/ConstructVarargs
|
||||
// depending on whether {new_target} was passed.
|
||||
void CallOrConstructBuiltinsAssembler::CallOrConstructDoubleVarargs(
|
||||
TNode<Object> target, SloppyTNode<Object> new_target,
|
||||
TNode<FixedDoubleArray> elements, TNode<Int32T> length,
|
||||
|
@ -51,6 +51,8 @@ namespace internal {
|
||||
ASM(Call_ReceiverIsNotNullOrUndefined) \
|
||||
ASM(Call_ReceiverIsAny) \
|
||||
\
|
||||
ASM(FastCallFunction_ReceiverIsNullOrUndefined) \
|
||||
\
|
||||
/* ES6 section 9.5.12[[Call]] ( thisArgument, argumentsList ) */ \
|
||||
TFC(CallProxy, CallTrampoline, 1) \
|
||||
ASM(CallVarargs) \
|
||||
|
@ -165,6 +165,8 @@ class Builtins {
|
||||
static void Generate_CallFunction(MacroAssembler* masm,
|
||||
ConvertReceiverMode mode);
|
||||
|
||||
static void Generate_FastCallFunction(MacroAssembler* masm);
|
||||
|
||||
static void Generate_CallBoundFunctionImpl(MacroAssembler* masm);
|
||||
|
||||
static void Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode);
|
||||
|
@ -1853,6 +1853,33 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm,
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
// static
|
||||
// The CSA macro "BranchIfCanUseFastCallFunction" should be used to determine
|
||||
// whether a JSFunction can be called using this stub.
|
||||
void Builtins::Generate_FastCallFunction(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 JSFunction)
|
||||
// -----------------------------------
|
||||
__ AssertFunction(edi);
|
||||
__ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- eax : the number of arguments (not including the receiver)
|
||||
// -- edi : the function to call (checked to be a JSFunction)
|
||||
// -- esi : the function context.
|
||||
// -----------------------------------
|
||||
|
||||
// On function call, call into the debugger if necessary.
|
||||
__ CheckDebugHook(edi, no_reg, ParameterCount(eax), ParameterCount(eax));
|
||||
__ mov(edx, __ isolate()->factory()->undefined_value());
|
||||
|
||||
static_assert(kJavaScriptCallCodeStartRegister == ecx, "ABI mismatch");
|
||||
__ mov(ecx, FieldOperand(edi, JSFunction::kCodeOffset));
|
||||
__ add(ecx, Immediate(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ jmp(ecx);
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
ConvertReceiverMode mode) {
|
||||
|
@ -1722,6 +1722,33 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm,
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
// static
|
||||
// The CSA macro "BranchIfCanUseFastCallFunction" should be used to determine
|
||||
// whether a JSFunction can be called using this stub.
|
||||
void Builtins::Generate_FastCallFunction(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- a0 : the number of arguments (not including the receiver)
|
||||
// -- a1 : the function to call (checked to be a JSFunction)
|
||||
// -----------------------------------
|
||||
__ AssertFunction(a1);
|
||||
__ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- a0 : the number of arguments (not including the receiver)
|
||||
// -- a1 : the function to call (checked to be a JSFunction)
|
||||
// -- cp : the function context.
|
||||
// -----------------------------------
|
||||
|
||||
// On function call, call into the debugger if necessary.
|
||||
__ CheckDebugHook(a1, no_reg, ParameterCount(a0), ParameterCount(a0));
|
||||
__ LoadRoot(a3, Heap::kUndefinedValueRootIndex);
|
||||
|
||||
Register code = kJavaScriptCallCodeStartRegister;
|
||||
__ lw(code, FieldMemOperand(a1, JSFunction::kCodeOffset));
|
||||
__ Addu(code, code, Code::kHeaderSize - kHeapObjectTag);
|
||||
__ Jump(code);
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
ConvertReceiverMode mode) {
|
||||
|
@ -1743,6 +1743,33 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm,
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
// static
|
||||
// The CSA macro "BranchIfCanUseFastCallFunction" should be used to determine
|
||||
// whether a JSFunction can be called using this stub.
|
||||
void Builtins::Generate_FastCallFunction(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- a0 : the number of arguments (not including the receiver)
|
||||
// -- a1 : the function to call (checked to be a JSFunction)
|
||||
// -----------------------------------
|
||||
__ AssertFunction(a1);
|
||||
__ Ld(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- a0 : the number of arguments (not including the receiver)
|
||||
// -- a1 : the function to call (checked to be a JSFunction)
|
||||
// -- cp : the function context.
|
||||
// -----------------------------------
|
||||
|
||||
// On function call, call into the debugger if necessary.
|
||||
__ CheckDebugHook(a1, no_reg, ParameterCount(a0), ParameterCount(a0));
|
||||
__ LoadRoot(a3, Heap::kUndefinedValueRootIndex);
|
||||
|
||||
Register code = kJavaScriptCallCodeStartRegister;
|
||||
__ Ld(code, FieldMemOperand(a1, JSFunction::kCodeOffset));
|
||||
__ Daddu(code, code, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ Jump(code);
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
ConvertReceiverMode mode) {
|
||||
|
@ -1792,6 +1792,33 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm,
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
// static
|
||||
// The CSA macro "BranchIfCanUseFastCallFunction" should be used to determine
|
||||
// whether a JSFunction can be called using this stub.
|
||||
void Builtins::Generate_FastCallFunction(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- r3 : the number of arguments (not including the receiver)
|
||||
// -- r4 : the function to call (checked to be a JSFunction)
|
||||
// -----------------------------------
|
||||
__ AssertFunction(r4);
|
||||
__ LoadP(cp, FieldMemOperand(r4, JSFunction::kContextOffset));
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- r3 : the number of arguments (not including the receiver)
|
||||
// -- r4 : the function to call (checked to be a JSFunction)
|
||||
// -- cp : the function context.
|
||||
// -----------------------------------
|
||||
|
||||
// On function call, call into the debugger if necessary.
|
||||
__ CheckDebugHook(r4, no_reg, ParameterCount(r3), ParameterCount(r3));
|
||||
__ LoadRoot(r6, Heap::kUndefinedValueRootIndex);
|
||||
|
||||
Register code = kJavaScriptCallCodeStartRegister;
|
||||
__ LoadP(code, FieldMemOperand(r4, JSFunction::kCodeOffset));
|
||||
__ addi(code, code, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ JumpToJSEntry(code);
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
ConvertReceiverMode mode) {
|
||||
|
@ -1797,6 +1797,33 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm,
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
// static
|
||||
// The CSA macro "BranchIfCanUseFastCallFunction" should be used to determine
|
||||
// whether a JSFunction can be called using this stub.
|
||||
void Builtins::Generate_FastCallFunction(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- r2 : the number of arguments (not including the receiver)
|
||||
// -- r3 : the function to call (checked to be a JSFunction)
|
||||
// -----------------------------------
|
||||
__ AssertFunction(r3);
|
||||
__ LoadP(cp, FieldMemOperand(r3, JSFunction::kContextOffset));
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- r2 : the number of arguments (not including the receiver)
|
||||
// -- r3 : the function to call (checked to be a JSFunction)
|
||||
// -- cp : the function context.
|
||||
// -----------------------------------
|
||||
|
||||
// On function call, call into the debugger if necessary.
|
||||
__ CheckDebugHook(r3, no_reg, ParameterCount(r2), ParameterCount(r2));
|
||||
__ LoadRoot(r5, Heap::kUndefinedValueRootIndex);
|
||||
|
||||
Register code = kJavaScriptCallCodeStartRegister;
|
||||
__ LoadP(code, FieldMemOperand(r3, JSFunction::kCodeOffset));
|
||||
__ AddP(code, code, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ JumpToJSEntry(code);
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
ConvertReceiverMode mode) {
|
||||
|
@ -1903,6 +1903,32 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm,
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
// static
|
||||
// The CSA macro "BranchIfCanUseFastCallFunction" should be used to determine
|
||||
// whether a JSFunction can be called using this stub.
|
||||
void Builtins::Generate_FastCallFunction(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- rax : the number of arguments (not including the receiver)
|
||||
// -- rdi : the function to call (checked to be a JSFunction)
|
||||
// -----------------------------------
|
||||
__ AssertFunction(rdi);
|
||||
__ movp(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- rax : the number of arguments (not including the receiver)
|
||||
// -- rdi : the function to call (checked to be a JSFunction)
|
||||
// -- rsi : the function context.
|
||||
// -----------------------------------
|
||||
|
||||
// On function call, call into the debugger if necessary.
|
||||
__ CheckDebugHook(rdi, no_reg, ParameterCount(rax), ParameterCount(rax));
|
||||
__ LoadRoot(rdx, Heap::kUndefinedValueRootIndex);
|
||||
|
||||
__ movp(rcx, FieldOperand(rdi, JSFunction::kCodeOffset));
|
||||
__ addp(rcx, Immediate(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ jmp(rcx);
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
ConvertReceiverMode mode) {
|
||||
|
@ -2587,6 +2587,58 @@ TNode<Map> CodeStubAssembler::LoadJSArrayElementsMap(
|
||||
LoadContextElement(native_context, Context::ArrayMapIndex(kind)));
|
||||
}
|
||||
|
||||
void CodeStubAssembler::BranchIfCanUseFastCallFunction(
|
||||
TNode<HeapObject> callable, TNode<Int32T> actualParameterCount,
|
||||
Label* if_true, Label* if_false) {
|
||||
GotoIfNot(IsJSFunction(callable), if_false);
|
||||
|
||||
TNode<JSFunction> function = CAST(callable);
|
||||
TNode<SharedFunctionInfo> sfi = LoadSharedFunctionInfo(function);
|
||||
TNode<Word32T> flags = UncheckedCast<Word32T>(LoadObjectField(
|
||||
sfi, SharedFunctionInfo::kFlagsOffset, MachineType::Uint32()));
|
||||
|
||||
GotoIf(IsSetWord32<SharedFunctionInfo::IsClassConstructorBit>(flags),
|
||||
if_false);
|
||||
|
||||
// Receiver needs to be converted for non-native sloppy mode functions.
|
||||
GotoIfNot(IsSetWord32(flags, SharedFunctionInfo::IsNativeBit::kMask |
|
||||
SharedFunctionInfo::IsStrictBit::kMask),
|
||||
if_false);
|
||||
|
||||
Branch(Word32Equal(actualParameterCount, LoadFormalParameterCount(sfi)),
|
||||
if_true, if_false);
|
||||
}
|
||||
|
||||
TNode<BoolT> CodeStubAssembler::CanUseFastCallFunction(
|
||||
TNode<HeapObject> callable, TNode<Int32T> actualParameterCount) {
|
||||
Label if_true(this), if_false(this), done(this);
|
||||
TVARIABLE(BoolT, result);
|
||||
BranchIfCanUseFastCallFunction(callable, actualParameterCount, &if_true,
|
||||
&if_false);
|
||||
BIND(&if_true);
|
||||
result = Int32TrueConstant();
|
||||
Goto(&done);
|
||||
|
||||
BIND(&if_false);
|
||||
result = Int32FalseConstant();
|
||||
Goto(&done);
|
||||
|
||||
BIND(&done);
|
||||
return result.value();
|
||||
}
|
||||
|
||||
TNode<SharedFunctionInfo> CodeStubAssembler::LoadSharedFunctionInfo(
|
||||
TNode<JSFunction> function) {
|
||||
return CAST(LoadObjectField(function, JSFunction::kSharedFunctionInfoOffset));
|
||||
}
|
||||
|
||||
TNode<Int32T> CodeStubAssembler::LoadFormalParameterCount(
|
||||
TNode<SharedFunctionInfo> sfi) {
|
||||
return UncheckedCast<Int32T>(
|
||||
LoadObjectField(sfi, SharedFunctionInfo::kFormalParameterCountOffset,
|
||||
MachineType::Uint16()));
|
||||
}
|
||||
|
||||
TNode<BoolT> CodeStubAssembler::IsGeneratorFunction(
|
||||
TNode<JSFunction> function) {
|
||||
TNode<SharedFunctionInfo> const shared_function_info =
|
||||
|
@ -679,6 +679,34 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
callable, receiver, args...));
|
||||
}
|
||||
|
||||
// Checks whether we can use "FastCall" instead of "Call" when calling
|
||||
// a JSFunction. This can be used for builtins where the user provides a
|
||||
// callback. The callback doesn't change during execution of the builtin, so
|
||||
// a lot of the checks that "Call" does can be done once upfront.
|
||||
//
|
||||
// These checks need to be kept in-sync with the "FastCall" and "Call*" stubs.
|
||||
void BranchIfCanUseFastCallFunction(TNode<HeapObject> callable,
|
||||
TNode<Int32T> actualParameterCount,
|
||||
Label* if_true, Label* if_false);
|
||||
|
||||
// Uses the above function to simply return {true} or {false}, used in a
|
||||
// CSA_SLOW_ASSERT.
|
||||
TNode<BoolT> CanUseFastCallFunction(TNode<HeapObject> callable,
|
||||
TNode<Int32T> actualParameterCount);
|
||||
|
||||
template <class... TArgs>
|
||||
TNode<Object> FastCall(TNode<Context> context, TNode<Object> callable,
|
||||
TArgs... args) {
|
||||
CSA_SLOW_ASSERT(this, CanUseFastCallFunction(
|
||||
CAST(callable), Int32Constant(sizeof...(TArgs))));
|
||||
|
||||
Callable call(isolate()->builtins()->builtin_handle(
|
||||
Builtins::kFastCallFunction_ReceiverIsNullOrUndefined),
|
||||
CallTrampolineDescriptor{});
|
||||
return UncheckedCast<Object>(
|
||||
CallJS(call, context, callable, UndefinedConstant(), args...));
|
||||
}
|
||||
|
||||
template <class A, class F, class G>
|
||||
TNode<A> Select(SloppyTNode<BoolT> condition, const F& true_body,
|
||||
const G& false_body) {
|
||||
@ -1137,6 +1165,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
TNode<Map> LoadJSArrayElementsMap(SloppyTNode<Int32T> kind,
|
||||
SloppyTNode<Context> native_context);
|
||||
|
||||
TNode<SharedFunctionInfo> LoadSharedFunctionInfo(TNode<JSFunction> function);
|
||||
TNode<Int32T> LoadFormalParameterCount(TNode<SharedFunctionInfo> sfi);
|
||||
|
||||
TNode<BoolT> IsGeneratorFunction(TNode<JSFunction> function);
|
||||
TNode<BoolT> HasPrototypeProperty(TNode<JSFunction> function, TNode<Map> map);
|
||||
void GotoIfPrototypeRequiresRuntimeLookup(TNode<JSFunction> function,
|
||||
|
@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
"use strict";
|
||||
|
||||
const kArraySize = 4000;
|
||||
let template_array = [];
|
||||
|
||||
|
44
test/mjsunit/array-sort-fast-call-builtin.js
Normal file
44
test/mjsunit/array-sort-fast-call-builtin.js
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2018 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Exercises the check that determines whether to use the
|
||||
// "FastCallFunction_" stub when calling the comparison function.
|
||||
|
||||
(function TestClassConstructorAsCmpFn() {
|
||||
class FooBar {};
|
||||
assertThrows(() => [1, 2].sort(FooBar));
|
||||
})();
|
||||
|
||||
|
||||
const globalThis = this;
|
||||
(function TestGlobalProxyIsSetAsReceiverWhenSloppy() {
|
||||
[1, 2].sort((a, b) => {
|
||||
assertSame(globalThis, this);
|
||||
return a - b;
|
||||
});
|
||||
})();
|
||||
|
||||
|
||||
(function TestReceiverIsUndefinedWhenStrict() {
|
||||
"use strict";
|
||||
|
||||
[1, 2].sort((a, b) => {
|
||||
assertSame(undefined, this);
|
||||
return a - b;
|
||||
});
|
||||
})();
|
||||
|
||||
|
||||
(function TestBoundFunctionAsCmpFn() {
|
||||
const object = { foo: "bar" };
|
||||
|
||||
function cmpfn(a, b) {
|
||||
assertSame(this, object);
|
||||
assertSame(this.foo, "bar");
|
||||
return a - b;
|
||||
};
|
||||
|
||||
const bound_cmpfn = cmpfn.bind(object);
|
||||
[1, 2].sort(bound_cmpfn);
|
||||
})();
|
37
third_party/v8/builtins/array-sort.tq
vendored
37
third_party/v8/builtins/array-sort.tq
vendored
@ -323,6 +323,22 @@ module array {
|
||||
return v;
|
||||
}
|
||||
|
||||
builtin FastSortCompareUserFn(
|
||||
context: Context, comparefn: Object, x: Object, y: Object): Number {
|
||||
assert(comparefn != Undefined);
|
||||
const cmpfn: Callable = unsafe_cast<Callable>(comparefn);
|
||||
|
||||
// a. Let v be ? ToNumber(? Call(comparefn, undefined, x, y)).
|
||||
const v: Number =
|
||||
ToNumber_Inline(context, FastCall(context, cmpfn, x, y));
|
||||
|
||||
// b. If v is NaN, return +0.
|
||||
if (NumberIsNaN(v)) return 0;
|
||||
|
||||
// c. return v.
|
||||
return v;
|
||||
}
|
||||
|
||||
builtin CanUseSameAccessor<ElementsAccessor : type>(
|
||||
context: Context, receiver: JSReceiver, initialReceiverMap: Object,
|
||||
initialReceiverLength: Number): Boolean {
|
||||
@ -1650,6 +1666,24 @@ module array {
|
||||
CanUseSameAccessor<GenericElementsAccessor>;
|
||||
}
|
||||
|
||||
// If no comparison function was provided, the default lexicographic compare
|
||||
// is used. Otherwise we try to use a faster JS call by eliding some checks.
|
||||
macro InitializeSortCompareFn(sortState: FixedArray, comparefnObj: Object) {
|
||||
if (comparefnObj == Undefined) {
|
||||
sortState[kSortComparePtrIdx] = SortCompareDefault;
|
||||
return;
|
||||
}
|
||||
|
||||
assert(TaggedIsNotSmi(comparefnObj));
|
||||
assert(IsCallable(unsafe_cast<HeapObject>(comparefnObj)));
|
||||
|
||||
sortState[kSortComparePtrIdx] =
|
||||
BranchIfCanUseFastCallFunction(
|
||||
unsafe_cast<HeapObject>(comparefnObj), 2) ?
|
||||
FastSortCompareUserFn :
|
||||
SortCompareUserFn;
|
||||
}
|
||||
|
||||
macro ArrayTimSortImpl(context: Context, sortState: FixedArray, length: Smi)
|
||||
labels Bailout {
|
||||
InitializeSortState(sortState);
|
||||
@ -1741,11 +1775,10 @@ module array {
|
||||
let map: Map = obj.map;
|
||||
|
||||
const sort_state: FixedArray = AllocateZeroedFixedArray(kSortStateSize);
|
||||
InitializeSortCompareFn(sort_state, comparefnObj);
|
||||
|
||||
sort_state[kReceiverIdx] = obj;
|
||||
sort_state[kUserCmpFnIdx] = comparefnObj;
|
||||
sort_state[kSortComparePtrIdx] =
|
||||
comparefnObj != Undefined ? SortCompareUserFn : SortCompareDefault;
|
||||
sort_state[kInitialReceiverMapIdx] = map;
|
||||
sort_state[kBailoutStatusIdx] = kSuccess;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user