[Builtins] Torque version of Array.prototype.forEach()

BUG=v8:7672

Change-Id: I0c157ce88b31312dfbea7a149c1d9fbdfb398278
Reviewed-on: https://chromium-review.googlesource.com/1013524
Commit-Queue: Michael Stanton <mvstanton@chromium.org>
Reviewed-by: Daniel Clifford <danno@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53091}
This commit is contained in:
Mike Stanton 2018-05-09 10:16:07 +02:00 committed by Commit Bot
parent 4a1ceff1aa
commit 5945e1ccd0
7 changed files with 245 additions and 80 deletions

View File

@ -297,4 +297,207 @@ module array {
return a;
}
macro ArrayForEachTorqueContinuation(
context: Context, o: Object, len: Number, callbackfn: Callable,
thisArg: Object, initial_k: Smi): Object {
// 5. Let k be 0.
// 6. Repeat, while k < len
for (let k: Smi = initial_k; k < len; k = k + 1) {
// 6a. Let Pk be ! ToString(k).
let pK: String = ToString_Inline(context, k);
// 6b. Let kPresent be ? HasProperty(O, Pk).
let kPresent: Oddball = HasPropertyObject(o, pK, context, kHasProperty);
// 6c. If kPresent is true, then
if (kPresent == True) {
// 6c. i. Let kValue be ? Get(O, Pk).
let kValue: Object = GetProperty(context, o, pK);
// 6c. ii. Perform ? Call(callbackfn, T, <kValue, k, O>).
Call(context, callbackfn, thisArg, kValue, k, o);
}
// 6d. Increase k by 1. (done by the loop).
}
return Undefined;
}
javascript builtin ArrayForEachLoopEagerDeoptContinuation(
context: Context, receiver: Object, callback: Object, thisArg: Object,
initialK: Object, length: Object): Object {
return ArrayForEachLoopContinuation(
context, receiver, callback, thisArg, Undefined, receiver, initialK,
length, Undefined);
}
javascript builtin ArrayForEachLoopLazyDeoptContinuation(
context: Context, receiver: Object, callback: Object, thisArg: Object,
initialK: Object, length: Object, result: Object): Object {
return ArrayForEachLoopContinuation(
context, receiver, callback, thisArg, Undefined, receiver, initialK,
length, Undefined);
}
builtin ArrayForEachLoopContinuation(
context: Context, receiver: Object, callback: Object, thisArg: Object,
array: Object, object: Object, initialK: Object, length: Object,
to: Object): Object {
try {
let callbackfn: Callable = cast<Callable>(callback) otherwise TypeError;
let k: Smi = cast<Smi>(initialK) otherwise TypeError;
let number_length: Number = cast<Number>(length) otherwise TypeError;
return ArrayForEachTorqueContinuation(
context, object, number_length, callbackfn, thisArg, k);
}
label TypeError {
ThrowTypeError(context, kCalledNonCallable, callback);
// TODO(mvstanton): this should be unreachable.
}
}
macro VisitAllDoubleElements(
context: Context, a: JSArray, len: Smi, callbackfn: Callable,
thisArg: Object): void labels
Bailout(Smi) {
let k: Smi = 0;
let map: Map = a.map;
try {
// Build a fast loop over the smi array.
for (; k < len; k = k + 1) {
// Ensure that the map didn't change.
if (map != a.map) goto slow;
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= a.length) goto slow;
let elements: FixedDoubleArray = cast<FixedDoubleArray>(a.elements)
otherwise slow;
try {
let value: float64 = LoadDoubleWithHoleCheck(elements, k)
otherwise found_hole;
let boxed_value: HeapNumber = AllocateHeapNumberWithValue(value);
Call(context, callbackfn, thisArg, boxed_value, k, a);
}
label found_hole {
// If we found the hole, we need to bail out if the initial
// array prototype has had elements inserted. This is preferable
// to walking the prototype chain looking for elements.
if (IsNoElementsProtectorCellInvalid()) goto Bailout(k);
}
}
}
label slow {
goto Bailout(k);
}
}
macro VisitAllFastElements(
context: Context, a: JSArray, len: Smi, callbackfn: Callable,
thisArg: Object): void labels
Bailout(Smi) {
let k: Smi = 0;
let map: Map = a.map;
try {
// Build a fast loop over the smi array.
for (; k < len; k = k + 1) {
// Ensure that the map didn't change.
if (map != a.map) goto slow;
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= a.length) goto slow;
let elements: FixedArray = cast<FixedArray>(a.elements)
otherwise slow;
let value: Object = elements[k];
if (value != Hole) {
Call(context, callbackfn, thisArg, value, k, a);
} else {
// If we found the hole, we need to bail out if the initial
// array prototype has had elements inserted. This is preferable
// to walking the prototype chain looking for elements.
if (IsNoElementsProtectorCellInvalid()) goto slow;
}
}
}
label slow {
goto Bailout(k);
}
}
macro FastArrayForEach(
context: Context, o: Object, len: Number, callbackfn: Callable,
thisArg: Object): Object labels
Bailout(Smi) {
let k: Smi = 0;
try {
let smi_len: Smi = cast<Smi>(len) otherwise slow;
let a: JSArray = cast<JSArray>(o) otherwise slow;
let map: Map = a.map;
if (!IsPrototypeInitialArrayPrototype(context, map)) goto slow;
let elementsKind: ElementsKind = map.elements_kind;
if (!IsFastElementsKind(elementsKind)) goto slow;
if (IsElementsKindGreaterThan(elementsKind, HOLEY_ELEMENTS)) {
VisitAllDoubleElements(context, a, smi_len, callbackfn, thisArg)
otherwise Bailout;
} else {
VisitAllFastElements(context, a, smi_len, callbackfn, thisArg)
otherwise Bailout;
}
}
label slow {
goto Bailout(k);
}
return Undefined;
}
// https://tc39.github.io/ecma262/#sec-array.prototype.foreach
javascript builtin ArrayForEach(
context: Context, receiver: Object, ...arguments): Object {
try {
if (IsNullOrUndefined(receiver)) {
goto NullOrUndefinedError;
}
// 1. Let O be ? ToObject(this value).
let o: Object = ToObject(context, receiver);
// 2. Let len be ? ToLength(? Get(O, "length")).
let len: Number =
ToLength_Inline(context, GetProperty(context, o, 'length'));
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto TypeError;
}
let callbackfn: Callable =
cast<Callable>(arguments[0]) otherwise TypeError;
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
let thisArg: Object = arguments.length > 1 ? arguments[1] : Undefined;
// Special cases.
let k: Smi = 0;
try {
return FastArrayForEach(context, o, len, callbackfn, thisArg)
otherwise Bailout;
}
label Bailout(k_value: Smi) {
k = k_value;
}
return ArrayForEachTorqueContinuation(
context, o, len, callbackfn, thisArg, k);
}
label TypeError {
ThrowTypeError(context, kCalledNonCallable, arguments[0]);
}
label NullOrUndefinedError {
ThrowTypeError(
context, kCalledOnNullOrUndefined, 'Array.prototype.forEach');
}
}
}

View File

@ -17,15 +17,17 @@ type RawPtr generates 'TNode<RawPtrT>' constexpr 'void*';
type Number extends Object generates 'TNode<Number>';
type Smi extends Number generates 'TNode<Smi>';
type HeapObject extends Object generates 'TNode<HeapObject>';
type JSReceiver extends HeapObject generates 'TNode<JSReceiver>';
type Context extends HeapObject generates 'TNode<Context>';
type String extends HeapObject generates 'TNode<String>';
type Oddball extends HeapObject generates 'TNode<Oddball>';
type HeapNumber extends HeapObject generates 'TNode<HeapNumber>';
type Boolean extends Oddball generates 'TNode<Oddball>';
type JSArray extends Object generates 'TNode<JSArray>';
type Callable extends HeapObject generates 'TNode<Object>';
type JSArray extends HeapObject generates 'TNode<JSArray>';
type Callable extends JSReceiver generates 'TNode<JSReceiver>';
type JSFunction extends Callable;
type Map extends Object generates 'TNode<Map>';
type FixedArrayBase extends Object generates 'TNode<FixedArrayBase>';
type Map extends HeapObject generates 'TNode<Map>';
type FixedArrayBase extends HeapObject generates 'TNode<FixedArrayBase>';
type FixedArray extends FixedArrayBase generates 'TNode<FixedArray>';
type FixedDoubleArray extends FixedArrayBase generates
'TNode<FixedDoubleArray>';
@ -71,6 +73,10 @@ const kEmptyFixedArray: FixedArrayBase =
const kInvalidArrayLengthMessage: MessageTemplate =
'MessageTemplate::kInvalidArrayLength';
const kCalledNonCallable: MessageTemplate =
'MessageTemplate::kCalledNonCallable';
const kCalledOnNullOrUndefined: MessageTemplate =
'MessageTemplate::kCalledOnNullOrUndefined';
const kHasProperty: HasPropertyFlag = 'kHasProperty';
@ -108,6 +114,7 @@ extern macro ArraySpeciesCreate(Context, Object, Number): Object;
extern macro EnsureArrayPushable(Map): ElementsKind labels Bailout;
extern builtin ToObject(Context, Object): Object;
extern macro IsNullOrUndefined(Object): bool;
extern runtime CreateDataProperty(Context, Object, Object, Object);
extern runtime SetProperty(Context, Object, Object, Object, LanguageMode);
@ -170,8 +177,8 @@ extern operator 'max' macro NumberMax(Number, Number): Number;
extern operator '!' macro Word32BinaryNot(bool): bool;
extern operator '.map' macro LoadMap(Object): Map;
extern operator '.map=' macro StoreMap(Object, Map);
extern operator '.map' macro LoadMap(HeapObject): Map;
extern operator '.map=' macro StoreMap(HeapObject, Map);
extern operator '.instanceType' macro LoadInstanceType(Object): InstanceType;
extern operator '.length' macro LoadStringLengthAsWord(String): intptr;
@ -195,6 +202,8 @@ extern operator 'cast<>' macro ConvertFixedArrayBaseToFixedArray(
extern operator 'cast<>' macro ConvertFixedArrayBaseToFixedDoubleArray(
FixedArrayBase): FixedDoubleArray labels CastError;
extern macro AllocateHeapNumberWithValue(float64): HeapNumber;
extern implicit operator
'convert<>' macro AllocateHeapNumberWithValue(constexpr float64): Number;
extern implicit operator
@ -253,6 +262,7 @@ extern operator
extern macro IsFastElementsKind(ElementsKind): bool;
extern macro IsFastSmiOrTaggedElementsKind(ElementsKind): bool;
extern macro IsFastSmiElementsKind(ElementsKind): bool;
extern macro IsHoleyFastElementsKind(ElementsKind): bool;
extern macro AllocateFixedArray(constexpr ElementsKind, Smi): FixedArray;
extern macro AllocateFixedArray(constexpr ElementsKind, Smi, Map): FixedArray;
@ -266,6 +276,11 @@ extern macro CopyFixedArrayElements(
extern macro AllocateJSArray(constexpr ElementsKind, Map, intptr, Smi): JSArray;
extern macro AllocateJSArray(constexpr ElementsKind, Map, Smi, Smi): JSArray;
extern macro IsElementsKindGreaterThan(
ElementsKind, constexpr ElementsKind): bool;
extern macro LoadDoubleWithHoleCheck(FixedDoubleArray, Smi): float64
labels IfHole;
extern macro Call(Context, Callable, Object, ...): Object;

View File

@ -2236,72 +2236,6 @@ TF_BUILTIN(TypedArrayPrototypeFindIndex, ArrayBuiltinsAssembler) {
&ArrayBuiltinsAssembler::NullPostLoopAction);
}
TF_BUILTIN(ArrayForEachLoopContinuation, ArrayBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
Node* this_arg = Parameter(Descriptor::kThisArg);
Node* array = Parameter(Descriptor::kArray);
TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject));
Node* initial_k = Parameter(Descriptor::kInitialK);
TNode<Number> len = CAST(Parameter(Descriptor::kLength));
Node* to = Parameter(Descriptor::kTo);
InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn,
this_arg, array, object, initial_k,
len, to);
GenerateIteratingArrayBuiltinLoopContinuation(
&ArrayBuiltinsAssembler::ForEachProcessor,
&ArrayBuiltinsAssembler::NullPostLoopAction, MissingPropertyMode::kSkip);
}
TF_BUILTIN(ArrayForEachLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
Node* this_arg = Parameter(Descriptor::kThisArg);
Node* initial_k = Parameter(Descriptor::kInitialK);
TNode<Number> len = CAST(Parameter(Descriptor::kLength));
Return(CallBuiltin(Builtins::kArrayForEachLoopContinuation, context, receiver,
callbackfn, this_arg, UndefinedConstant(), receiver,
initial_k, len, UndefinedConstant()));
}
TF_BUILTIN(ArrayForEachLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
Node* this_arg = Parameter(Descriptor::kThisArg);
Node* initial_k = Parameter(Descriptor::kInitialK);
TNode<Number> len = CAST(Parameter(Descriptor::kLength));
Return(CallBuiltin(Builtins::kArrayForEachLoopContinuation, context, receiver,
callbackfn, this_arg, UndefinedConstant(), receiver,
initial_k, len, UndefinedConstant()));
}
TF_BUILTIN(ArrayForEach, ArrayBuiltinsAssembler) {
TNode<IntPtrT> argc =
ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
CodeStubArguments args(this, argc);
TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
TNode<Object> receiver = args.GetReceiver();
Node* callbackfn = args.GetOptionalArgumentValue(0);
Node* this_arg = args.GetOptionalArgumentValue(1);
InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
GenerateIteratingArrayBuiltinBody(
"Array.prototype.forEach",
&ArrayBuiltinsAssembler::ForEachResultGenerator,
&ArrayBuiltinsAssembler::ForEachProcessor,
&ArrayBuiltinsAssembler::NullPostLoopAction,
Builtins::CallableFor(isolate(), Builtins::kArrayForEachLoopContinuation),
MissingPropertyMode::kSkip);
}
TF_BUILTIN(TypedArrayPrototypeForEach, ArrayBuiltinsAssembler) {
TNode<IntPtrT> argc =
ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));

View File

@ -278,14 +278,6 @@ namespace internal {
/* Support for Array.from and other array-copying idioms */ \
TFS(CloneFastJSArray, kSource) \
TFS(ExtractFastJSArray, kSource, kBegin, kCount) \
/* ES6 #sec-array.prototype.foreach */ \
TFS(ArrayForEachLoopContinuation, kReceiver, kCallbackFn, kThisArg, kArray, \
kObject, kInitialK, kLength, kTo) \
TFJ(ArrayForEachLoopEagerDeoptContinuation, 4, kCallbackFn, kThisArg, \
kInitialK, kLength) \
TFJ(ArrayForEachLoopLazyDeoptContinuation, 5, kCallbackFn, kThisArg, \
kInitialK, kLength, kResult) \
TFJ(ArrayForEach, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-array.prototype.every */ \
TFS(ArrayEveryLoopContinuation, kReceiver, kCallbackFn, kThisArg, kArray, \
kObject, kInitialK, kLength, kTo) \

View File

@ -857,6 +857,12 @@ void CodeStubAssembler::Bind(Label* label, AssemblerDebugInfo debug_info) {
void CodeStubAssembler::Bind(Label* label) { CodeAssembler::Bind(label); }
#endif // DEBUG
TNode<Float64T> CodeStubAssembler::LoadDoubleWithHoleCheck(
TNode<FixedDoubleArray> array, TNode<Smi> index, Label* if_hole) {
return TNode<Float64T>::UncheckedCast(LoadFixedDoubleArrayElement(
array, index, MachineType::Float64(), 0, SMI_PARAMETERS, if_hole));
}
void CodeStubAssembler::BranchIfPrototypesHaveNoElements(
Node* receiver_map, Label* definitely_no_elements,
Label* possibly_elements) {

View File

@ -777,6 +777,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
ParameterMode parameter_mode = INTPTR_PARAMETERS);
TNode<IntPtrT> LoadFeedbackVectorLength(TNode<FeedbackVector>);
TNode<Float64T> LoadDoubleWithHoleCheck(TNode<FixedDoubleArray> array,
TNode<Smi> index,
Label* if_hole = nullptr);
// Load Float64 value by |base| + |offset| address. If the value is a double
// hole then jump to |if_hole|. If |machine_type| is None then only the hole

View File

@ -24,3 +24,15 @@
}
a.forEach(poison);
})();
// Same, but for a double array.
(()=>{
let a = [1, 2.5, 3,,,, 7];
function poison(v, i) {
if (i === 2) {
[].__proto__[4] = 3;
}
return v*v;
}
a.forEach(poison);
})();