[torque] Port builtins/builtins-function-gen to Torque

- Ports FastFunctionPrototypeBind and FunctionPrototypeHasInstance
  to torque.

Bug: v8:9891
Change-Id: Iaebaf3c6025907a1b7310c4e08200b4855c7ca6b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2188929
Commit-Queue: Bill Budge <bbudge@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67832}
This commit is contained in:
Bill Budge 2020-05-15 07:07:46 -07:00 committed by Commit Bot
parent a3d5ad8384
commit 4436f5e95b
9 changed files with 141 additions and 207 deletions

View File

@ -1038,6 +1038,7 @@ torque_files = [
"src/builtins/finalization-registry.tq",
"src/builtins/frames.tq",
"src/builtins/frame-arguments.tq",
"src/builtins/function.tq",
"src/builtins/growable-fixed-array.tq",
"src/builtins/ic-callable.tq",
"src/builtins/ic.tq",
@ -1632,7 +1633,6 @@ v8_source_set("v8_initializers") {
"src/builtins/builtins-data-view-gen.h",
"src/builtins/builtins-date-gen.cc",
"src/builtins/builtins-debug-gen.cc",
"src/builtins/builtins-function-gen.cc",
"src/builtins/builtins-generator-gen.cc",
"src/builtins/builtins-global-gen.cc",
"src/builtins/builtins-handler-gen.cc",

View File

@ -394,6 +394,7 @@ extern macro LengthStringConstant(): String;
extern macro MatchSymbolConstant(): Symbol;
extern macro MessageStringConstant(): String;
extern macro NanConstant(): NaN;
extern macro NameStringConstant(): String;
extern macro NullConstant(): Null;
extern macro ReturnStringConstant(): String;
extern macro TheHoleConstant(): TheHole;

View File

@ -503,11 +503,7 @@ namespace internal {
CPP(FunctionConstructor) \
ASM(FunctionPrototypeApply, JSTrampoline) \
CPP(FunctionPrototypeBind) \
/* ES6 #sec-function.prototype.bind */ \
TFJ(FastFunctionPrototypeBind, kDontAdaptArgumentsSentinel) \
ASM(FunctionPrototypeCall, JSTrampoline) \
/* ES6 #sec-function.prototype-@@hasinstance */ \
TFJ(FunctionPrototypeHasInstance, 1, kReceiver, kV) \
/* ES6 #sec-function.prototype.tostring */ \
CPP(FunctionPrototypeToString) \
\

View File

@ -1,202 +0,0 @@
// Copyright 2017 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.
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
#include "src/codegen/code-stub-assembler.h"
#include "src/execution/frame-constants.h"
#include "src/objects/api-callbacks.h"
#include "src/objects/descriptor-array.h"
namespace v8 {
namespace internal {
TF_BUILTIN(FastFunctionPrototypeBind, CodeStubAssembler) {
Label slow(this);
TNode<Int32T> argc =
UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> new_target = CAST(Parameter(Descriptor::kJSNewTarget));
CodeStubArguments args(this, argc);
// Check that receiver has instance type of JS_FUNCTION_TYPE
TNode<Object> receiver = args.GetReceiver();
GotoIf(TaggedIsSmi(receiver), &slow);
TNode<Map> receiver_map = LoadMap(CAST(receiver));
{
TNode<Uint16T> instance_type = LoadMapInstanceType(receiver_map);
GotoIfNot(
Word32Or(InstanceTypeEqual(instance_type, JS_FUNCTION_TYPE),
InstanceTypeEqual(instance_type, JS_BOUND_FUNCTION_TYPE)),
&slow);
}
// Disallow binding of slow-mode functions. We need to figure out whether the
// length and name property are in the original state.
Comment("Disallow binding of slow-mode functions");
GotoIf(IsDictionaryMap(receiver_map), &slow);
// Check whether the length and name properties are still present as
// AccessorInfo objects. In that case, their value can be recomputed even if
// the actual value on the object changes.
Comment("Check descriptor array length");
// Minimum descriptor array length required for fast path.
const int min_nof_descriptors = i::Max(JSFunction::kLengthDescriptorIndex,
JSFunction::kNameDescriptorIndex) +
1;
TNode<Int32T> nof_descriptors = LoadNumberOfOwnDescriptors(receiver_map);
GotoIf(Int32LessThan(nof_descriptors, Int32Constant(min_nof_descriptors)),
&slow);
// Check whether the length and name properties are still present as
// AccessorInfo objects. In that case, their value can be recomputed even if
// the actual value on the object changes.
Comment("Check name and length properties");
{
TNode<DescriptorArray> descriptors = LoadMapDescriptors(receiver_map);
const int length_index = JSFunction::kLengthDescriptorIndex;
TNode<Name> maybe_length =
LoadKeyByDescriptorEntry(descriptors, length_index);
GotoIf(TaggedNotEqual(maybe_length, LengthStringConstant()), &slow);
TNode<Object> maybe_length_accessor =
LoadValueByDescriptorEntry(descriptors, length_index);
GotoIf(TaggedIsSmi(maybe_length_accessor), &slow);
TNode<Map> length_value_map = LoadMap(CAST(maybe_length_accessor));
GotoIfNot(IsAccessorInfoMap(length_value_map), &slow);
const int name_index = JSFunction::kNameDescriptorIndex;
TNode<Name> maybe_name = LoadKeyByDescriptorEntry(descriptors, name_index);
GotoIf(TaggedNotEqual(maybe_name, NameStringConstant()), &slow);
TNode<Object> maybe_name_accessor =
LoadValueByDescriptorEntry(descriptors, name_index);
GotoIf(TaggedIsSmi(maybe_name_accessor), &slow);
TNode<Map> name_value_map = LoadMap(CAST(maybe_name_accessor));
GotoIfNot(IsAccessorInfoMap(name_value_map), &slow);
}
// Choose the right bound function map based on whether the target is
// constructable.
Comment("Choose the right bound function map");
TVARIABLE(Map, bound_function_map);
{
Label with_constructor(this);
TNode<NativeContext> native_context = LoadNativeContext(context);
Label map_done(this, &bound_function_map);
GotoIf(IsConstructorMap(receiver_map), &with_constructor);
bound_function_map = CAST(LoadContextElement(
native_context, Context::BOUND_FUNCTION_WITHOUT_CONSTRUCTOR_MAP_INDEX));
Goto(&map_done);
BIND(&with_constructor);
bound_function_map = CAST(LoadContextElement(
native_context, Context::BOUND_FUNCTION_WITH_CONSTRUCTOR_MAP_INDEX));
Goto(&map_done);
BIND(&map_done);
}
// Verify that __proto__ matches that of a the target bound function.
Comment("Verify that __proto__ matches target bound function");
TNode<HeapObject> prototype = LoadMapPrototype(receiver_map);
TNode<HeapObject> expected_prototype =
LoadMapPrototype(bound_function_map.value());
GotoIf(TaggedNotEqual(prototype, expected_prototype), &slow);
// Allocate the arguments array.
Comment("Allocate the arguments array");
TVARIABLE(FixedArray, argument_array);
{
Label empty_arguments(this);
Label arguments_done(this, &argument_array);
GotoIf(Uint32LessThanOrEqual(argc, Int32Constant(1)), &empty_arguments);
TNode<IntPtrT> elements_length =
Signed(ChangeUint32ToWord(Unsigned(Int32Sub(argc, Int32Constant(1)))));
argument_array = CAST(AllocateFixedArray(PACKED_ELEMENTS, elements_length,
kAllowLargeObjectAllocation));
TVARIABLE(IntPtrT, index, IntPtrConstant(0));
VariableList foreach_vars({&index}, zone());
args.ForEach(
foreach_vars,
[&](TNode<Object> arg) {
StoreFixedArrayElement(argument_array.value(), index.value(), arg);
Increment(&index);
},
IntPtrConstant(1));
Goto(&arguments_done);
BIND(&empty_arguments);
argument_array = EmptyFixedArrayConstant();
Goto(&arguments_done);
BIND(&arguments_done);
}
// Determine bound receiver.
Comment("Determine bound receiver");
TVARIABLE(Object, bound_receiver);
{
Label has_receiver(this);
Label receiver_done(this, &bound_receiver);
GotoIf(Word32NotEqual(argc, Int32Constant(0)), &has_receiver);
bound_receiver = UndefinedConstant();
Goto(&receiver_done);
BIND(&has_receiver);
bound_receiver = args.AtIndex(0);
Goto(&receiver_done);
BIND(&receiver_done);
}
// Allocate the resulting bound function.
Comment("Allocate the resulting bound function");
{
TNode<HeapObject> bound_function = Allocate(JSBoundFunction::kHeaderSize);
StoreMapNoWriteBarrier(bound_function, bound_function_map.value());
StoreObjectFieldNoWriteBarrier(
bound_function, JSBoundFunction::kBoundTargetFunctionOffset, receiver);
StoreObjectFieldNoWriteBarrier(bound_function,
JSBoundFunction::kBoundThisOffset,
bound_receiver.value());
StoreObjectFieldNoWriteBarrier(bound_function,
JSBoundFunction::kBoundArgumentsOffset,
argument_array.value());
TNode<FixedArray> empty_fixed_array = EmptyFixedArrayConstant();
StoreObjectFieldNoWriteBarrier(
bound_function, JSObject::kPropertiesOrHashOffset, empty_fixed_array);
StoreObjectFieldNoWriteBarrier(bound_function, JSObject::kElementsOffset,
empty_fixed_array);
args.PopAndReturn(bound_function);
}
BIND(&slow);
{
// We are not using Parameter(Descriptor::kJSTarget) and loading the value
// from the current frame here in order to reduce register pressure on the
// fast path.
TNode<JSFunction> target = LoadTargetFromFrame();
TailCallBuiltin(Builtins::kFunctionPrototypeBind, context, target,
new_target, argc);
}
}
// ES6 #sec-function.prototype-@@hasinstance
TF_BUILTIN(FunctionPrototypeHasInstance, CodeStubAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> f = CAST(Parameter(Descriptor::kReceiver));
TNode<Object> v = CAST(Parameter(Descriptor::kV));
TNode<Oddball> result = OrdinaryHasInstance(context, f, v);
Return(result);
}
} // namespace internal
} // namespace v8

View File

@ -659,6 +659,13 @@ Cast<CoverageInfo>(implicit context: Context)(o: HeapObject): CoverageInfo
goto CastError;
}
extern macro IsAccessorInfo(HeapObject): bool;
Cast<AccessorInfo>(o: HeapObject): AccessorInfo
labels CastError {
if (IsAccessorInfo(o)) return %RawDownCast<AccessorInfo>(o);
goto CastError;
}
Cast<JSReceiver|Null>(o: HeapObject): JSReceiver|Null
labels CastError {
typeswitch (o) {
@ -752,6 +759,21 @@ Cast<JSBoundFunction>(o: HeapObject): JSBoundFunction labels CastError {
goto CastError;
}
Cast<JSFunction|JSBoundFunction>(implicit context: Context)(o: Object):
JSFunction|JSBoundFunction labels CastError {
typeswitch (o) {
case (o: JSFunction): {
return o;
}
case (o: JSBoundFunction): {
return o;
}
case (Object): {
goto CastError;
}
}
}
Cast<PromiseCapability>(o: HeapObject): PromiseCapability labels CastError {
if (IsPromiseCapability(o)) return %RawDownCast<PromiseCapability>(o);
goto CastError;

109
src/builtins/function.tq Normal file
View File

@ -0,0 +1,109 @@
// Copyright 2020 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.
namespace function {
extern macro OrdinaryHasInstance(Context, Object, Object): JSAny;
// ES6 section 19.2.3.6 Function.prototype[@@hasInstance]
javascript builtin FunctionPrototypeHasInstance(
js-implicit context: NativeContext, receiver: JSAny)(value: JSAny): JSAny {
return OrdinaryHasInstance(context, receiver, value);
}
extern transitioning builtin
FunctionPrototypeBind(implicit context: Context)(
JSFunction, JSAny, int32): JSAny;
const kLengthDescriptorIndex:
constexpr int32 generates 'JSFunction::kLengthDescriptorIndex';
const kNameDescriptorIndex:
constexpr int32 generates 'JSFunction::kNameDescriptorIndex';
const kMinDescriptorsForFastBind:
constexpr int31 generates 'JSFunction::kMinDescriptorsForFastBind';
macro CheckAccessor(implicit context: Context)(
array: DescriptorArray, index: constexpr int32, name: Name) labels Slow {
const descriptor: DescriptorEntry = array.descriptors[index];
const key: Name|Undefined = descriptor.key;
if (!TaggedEqual(key, name)) goto Slow;
// The descriptor value must be an AccessorInfo.
Cast<AccessorInfo>(descriptor.value) otherwise goto Slow;
}
// ES6 section 19.2.3.2 Function.prototype.bind
transitioning javascript builtin
FastFunctionPrototypeBind(
js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny,
target: JSFunction)(...arguments): JSAny {
const argc: intptr = arguments.length;
try {
typeswitch (receiver) {
case (fn: JSFunction|JSBoundFunction): {
// Disallow binding of slow-mode functions. We need to figure out
// whether the length and name property are in the original state.
Comment('Disallow binding of slow-mode functions');
if (IsDictionaryMap(fn.map)) goto Slow;
// Check whether the length and name properties are still present as
// AccessorInfo objects. If so, their value can be recomputed even if
// the actual value on the object changes.
if (fn.map.bit_field3.number_of_own_descriptors <
kMinDescriptorsForFastBind) {
goto Slow;
}
const descriptors: DescriptorArray = fn.map.instance_descriptors;
CheckAccessor(
descriptors, kLengthDescriptorIndex, LengthStringConstant())
otherwise Slow;
CheckAccessor(descriptors, kNameDescriptorIndex, NameStringConstant())
otherwise Slow;
// Choose the right bound function map based on whether the target is
// constructable.
const boundFunctionMap: Map = UnsafeCast<Map>(
IsConstructor(fn) ?
context[NativeContextSlot::
BOUND_FUNCTION_WITH_CONSTRUCTOR_MAP_INDEX] :
context[NativeContextSlot::
BOUND_FUNCTION_WITHOUT_CONSTRUCTOR_MAP_INDEX]);
// Verify that prototype matches that of the target bound function.
if (fn.map.prototype != boundFunctionMap.prototype) goto Slow;
// Allocate the arguments array.
const argumentsArray = arguments.length <= 1 ?
kEmptyFixedArray :
NewFixedArray(
arguments.length - 1, ArgumentsIterator{arguments, current: 1});
const boundReceiver: JSAny = arguments[0];
const result = new JSBoundFunction{
map: boundFunctionMap,
properties_or_hash: kEmptyFixedArray,
elements: kEmptyFixedArray,
bound_target_function: fn,
bound_this: boundReceiver,
bound_arguments: argumentsArray
};
return result;
}
case (JSAny): {
goto Slow;
}
}
} label Slow {
tail FunctionPrototypeBind(
LoadTargetFromFrame(), newTarget, Convert<int32>(argc));
}
}
} // namespace function

View File

@ -52,6 +52,9 @@ extern enum NativeContextSlot extends intptr constexpr 'Context::Field' {
STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX,
CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX,
BOUND_FUNCTION_WITH_CONSTRUCTOR_MAP_INDEX,
BOUND_FUNCTION_WITHOUT_CONSTRUCTOR_MAP_INDEX,
...
}

View File

@ -101,6 +101,9 @@ operator '[]=' macro StoreFixedArrayDirect(a: FixedArray, i: Smi, v: Object) {
a.objects[i] = v;
}
extern macro AllocateFixedArray(
constexpr ElementsKind, intptr, constexpr AllocationFlag): FixedArrayBase;
extern macro AllocateZeroedFixedArray(intptr): FixedArray;
extern macro AllocateZeroedFixedDoubleArray(intptr): FixedDoubleArray;
extern macro CalculateNewElementsCapacity(Smi): Smi;

View File

@ -973,6 +973,8 @@ class JSFunction : public JSFunctionOrBoundFunction {
static const int kNameDescriptorIndex = 1;
// Home object descriptor index when function has a [[HomeObject]] slot.
static const int kMaybeHomeObjectDescriptorIndex = 2;
// Fast binding requires length and name accessors.
static const int kMinDescriptorsForFastBind = 2;
// [context]: The context for this function.
inline Context context();