Commit 7f21e67b authored by caitp's avatar caitp Committed by Commit bot

[builtins] implement JSBuiltinReducer for ArrayIteratorNext()

Adds a protector cell to prevent inlining (which will likely lead to deopt
loops) when a JSArrayIterator's array transitions from a fast JSArray to a
slow JSArray (such as, when the array is touched during iteration in a way
which triggers a map transition).

Also adds TODO comments relating to the spec update proposed by Dan at
https://github.com/tc39/ecma262/pull/724

BUG=v8:5388
R=bmeurer@chromium.org, mstarzinger@chromium.org
TBR=hpayer@chromium.org, ulan@chromium.org

Review-Url: https://codereview.chromium.org/2484003002
Cr-Commit-Position: refs/heads/master@{#40970}
parent 0909e5cc
......@@ -1334,6 +1334,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Handle<JSFunction> next = InstallFunction(
array_iterator_prototype, "next", JS_OBJECT_TYPE, JSObject::kHeaderSize,
MaybeHandle<JSObject>(), Builtins::kArrayIteratorPrototypeNext);
next->shared()->set_builtin_function_id(kArrayIteratorNext);
// Set the expected parameters for %ArrayIteratorPrototype%.next to 0 (not
// including the receiver), as required by the builtin.
......@@ -2187,14 +2188,21 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
kTypedArrayLength);
// Install "keys", "values" and "entries" methods on the {prototype}.
SimpleInstallFunction(prototype, factory->entries_string(),
Builtins::kTypedArrayPrototypeEntries, 0, true);
SimpleInstallFunction(prototype, factory->keys_string(),
Builtins::kTypedArrayPrototypeKeys, 0, true);
Handle<JSFunction> iterator =
Handle<JSFunction> entries =
SimpleInstallFunction(prototype, factory->entries_string(),
Builtins::kTypedArrayPrototypeEntries, 0, true);
entries->shared()->set_builtin_function_id(kTypedArrayEntries);
Handle<JSFunction> keys =
SimpleInstallFunction(prototype, factory->keys_string(),
Builtins::kTypedArrayPrototypeKeys, 0, true);
keys->shared()->set_builtin_function_id(kTypedArrayKeys);
Handle<JSFunction> values =
SimpleInstallFunction(prototype, factory->values_string(),
Builtins::kTypedArrayPrototypeValues, 0, true);
JSObject::AddProperty(prototype, factory->iterator_symbol(), iterator,
values->shared()->set_builtin_function_id(kTypedArrayValues);
JSObject::AddProperty(prototype, factory->iterator_symbol(), values,
DONT_ENUM);
}
......
......@@ -2301,6 +2301,8 @@ void Builtins::Generate_ArrayIteratorPrototypeNext(
assembler->Bind(&if_isgeneric);
{
Label if_wasfastarray(assembler);
Node* length = nullptr;
{
Variable var_length(assembler, MachineRepresentation::kTagged);
......@@ -2314,7 +2316,35 @@ void Builtins::Generate_ArrayIteratorPrototypeNext(
{
var_length.Bind(
assembler->LoadObjectField(array, JSArray::kLengthOffset));
assembler->Goto(&done);
// Invalidate protector cell if needed
assembler->Branch(
assembler->WordNotEqual(orig_map, assembler->UndefinedConstant()),
&if_wasfastarray, &done);
assembler->Bind(&if_wasfastarray);
{
Label if_invalid(assembler, Label::kDeferred);
// A fast array iterator transitioned to a slow iterator during
// iteration. Invalidate fast_array_iteration_prtoector cell to
// prevent potential deopt loops.
assembler->StoreObjectFieldNoWriteBarrier(
iterator, JSArrayIterator::kIteratedObjectMapOffset,
assembler->UndefinedConstant());
assembler->GotoIf(
assembler->Uint32LessThanOrEqual(
instance_type, assembler->Int32Constant(
JS_GENERIC_ARRAY_KEY_ITERATOR_TYPE)),
&done);
Node* invalid = assembler->SmiConstant(
Smi::FromInt(Isolate::kArrayProtectorInvalid));
Node* cell = assembler->LoadRoot(
Heap::kFastArrayIterationProtectorRootIndex);
assembler->StoreObjectFieldNoWriteBarrier(cell, Cell::kValueOffset,
invalid);
assembler->Goto(&done);
}
}
assembler->Bind(&if_isnotarray);
......@@ -2377,6 +2407,8 @@ void Builtins::Generate_ArrayIteratorPrototypeNext(
assembler->Bind(&if_isdetached);
{
// TODO(caitp): If IsDetached(buffer) is true, throw a TypeError, per
// https://github.com/tc39/ecma262/issues/713
var_length.Bind(assembler->SmiConstant(Smi::kZero));
assembler->Goto(&done);
}
......
......@@ -681,16 +681,11 @@ void CodeStubAssembler::BranchIfFastJSArray(Node* object, Node* context,
Node* elements_kind = LoadMapElementsKind(map);
// Bailout if receiver has slow elements.
GotoIf(
Int32GreaterThan(elements_kind, Int32Constant(LAST_FAST_ELEMENTS_KIND)),
if_false);
GotoUnless(IsFastElementsKind(elements_kind), if_false);
// Check prototype chain if receiver does not have packed elements.
STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == (FAST_SMI_ELEMENTS | 1));
STATIC_ASSERT(FAST_HOLEY_ELEMENTS == (FAST_ELEMENTS | 1));
STATIC_ASSERT(FAST_HOLEY_DOUBLE_ELEMENTS == (FAST_DOUBLE_ELEMENTS | 1));
Node* holey_elements = Word32And(elements_kind, Int32Constant(1));
GotoIf(Word32Equal(holey_elements, Int32Constant(0)), if_true);
GotoUnless(IsHoleyFastElementsKind(elements_kind), if_true);
BranchIfPrototypesHaveNoElements(map, if_true, if_false);
}
......@@ -8898,19 +8893,59 @@ compiler::Node* CodeStubAssembler::CreateArrayIterator(
Bind(&if_isfast);
{
Node* map_index =
IntPtrAdd(IntPtrConstant(kBaseMapIndex + kFastIteratorOffset),
LoadMapElementsKind(array_map));
CSA_ASSERT(this, IntPtrGreaterThanOrEqual(
map_index, IntPtrConstant(kBaseMapIndex +
kFastIteratorOffset)));
CSA_ASSERT(this, IntPtrLessThan(map_index,
IntPtrConstant(kBaseMapIndex +
kSlowIteratorOffset)));
Label if_ispacked(this), if_isholey(this);
Node* elements_kind = LoadMapElementsKind(array_map);
Branch(IsHoleyFastElementsKind(elements_kind), &if_isholey,
&if_ispacked);
var_map_index.Bind(map_index);
var_array_map.Bind(array_map);
Goto(&allocate_iterator);
Bind(&if_isholey);
{
// Fast holey JSArrays can treat the hole as undefined if the
// protector cell is valid, and the prototype chain is unchanged from
// its initial state (because the protector cell is only tracked for
// initial the Array and Object prototypes). Check these conditions
// here, and take the slow path if any fail.
Node* protector_cell = LoadRoot(Heap::kArrayProtectorRootIndex);
DCHECK(isolate()->heap()->array_protector()->IsPropertyCell());
GotoUnless(
WordEqual(
LoadObjectField(protector_cell, PropertyCell::kValueOffset),
SmiConstant(Smi::FromInt(Isolate::kArrayProtectorValid))),
&if_isslow);
Node* native_context = LoadNativeContext(context);
Node* prototype = LoadMapPrototype(array_map);
Node* array_prototype = LoadContextElement(
native_context, Context::INITIAL_ARRAY_PROTOTYPE_INDEX);
GotoUnless(WordEqual(prototype, array_prototype), &if_isslow);
Node* map = LoadMap(prototype);
prototype = LoadMapPrototype(map);
Node* object_prototype = LoadContextElement(
native_context, Context::INITIAL_OBJECT_PROTOTYPE_INDEX);
GotoUnless(WordEqual(prototype, object_prototype), &if_isslow);
map = LoadMap(prototype);
prototype = LoadMapPrototype(map);
Branch(IsNull(prototype), &if_ispacked, &if_isslow);
}
Bind(&if_ispacked);
{
Node* map_index =
IntPtrAdd(IntPtrConstant(kBaseMapIndex + kFastIteratorOffset),
LoadMapElementsKind(array_map));
CSA_ASSERT(this, IntPtrGreaterThanOrEqual(
map_index, IntPtrConstant(kBaseMapIndex +
kFastIteratorOffset)));
CSA_ASSERT(this, IntPtrLessThan(map_index,
IntPtrConstant(kBaseMapIndex +
kSlowIteratorOffset)));
var_map_index.Bind(map_index);
var_array_map.Bind(array_map);
Goto(&allocate_iterator);
}
}
Bind(&if_isslow);
......@@ -9051,5 +9086,24 @@ void CodeStubArguments::PopAndReturn(compiler::Node* value) {
value);
}
compiler::Node* CodeStubAssembler::IsFastElementsKind(
compiler::Node* elements_kind) {
return Uint32LessThanOrEqual(elements_kind,
Int32Constant(LAST_FAST_ELEMENTS_KIND));
}
compiler::Node* CodeStubAssembler::IsHoleyFastElementsKind(
compiler::Node* elements_kind) {
CSA_ASSERT(this, IsFastElementsKind(elements_kind));
STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == (FAST_SMI_ELEMENTS | 1));
STATIC_ASSERT(FAST_HOLEY_ELEMENTS == (FAST_ELEMENTS | 1));
STATIC_ASSERT(FAST_HOLEY_DOUBLE_ELEMENTS == (FAST_DOUBLE_ELEMENTS | 1));
// Check prototype chain if receiver does not have packed elements.
Node* holey_elements = Word32And(elements_kind, Int32Constant(1));
return Word32Equal(holey_elements, Int32Constant(1));
}
} // namespace internal
} // namespace v8
......@@ -20,8 +20,6 @@ class StubCache;
enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
enum class IterationKind { kKeys, kValues, kEntries };
#define HEAP_CONSTANT_LIST(V) \
V(BooleanMap, BooleanMap) \
V(CodeMap, CodeMap) \
......@@ -634,6 +632,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
compiler::Node* IsDictionary(compiler::Node* object);
compiler::Node* IsUnseededNumberDictionary(compiler::Node* object);
// ElementsKind helpers:
compiler::Node* IsFastElementsKind(compiler::Node* elements_kind);
compiler::Node* IsHoleyFastElementsKind(compiler::Node* elements_kind);
// String helpers.
// Load a character from a String (might flatten a ConsString).
compiler::Node* StringCharCodeAt(compiler::Node* string,
......
......@@ -570,6 +570,59 @@ FieldAccess AccessBuilder::ForJSGlobalObjectNativeContext() {
return access;
}
// static
FieldAccess AccessBuilder::ForJSArrayIteratorObject() {
FieldAccess access = {kTaggedBase,
JSArrayIterator::kIteratedObjectOffset,
Handle<Name>(),
Type::ReceiverOrUndefined(),
MachineType::TaggedPointer(),
kPointerWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForJSArrayIteratorIndex(InstanceType instance_type,
ElementsKind elements_kind) {
// In generic case, cap to 2^53-1 (per ToLength() in spec) via
// kPositiveSafeInteger
FieldAccess access = {kTaggedBase,
JSArrayIterator::kNextIndexOffset,
Handle<Name>(),
TypeCache::Get().kPositiveSafeInteger,
MachineType::AnyTagged(),
kFullWriteBarrier};
if (instance_type == JS_ARRAY_TYPE) {
if (IsFastDoubleElementsKind(elements_kind)) {
access.type = TypeCache::Get().kFixedDoubleArrayLengthType;
access.machine_type = MachineType::TaggedSigned();
access.write_barrier_kind = kNoWriteBarrier;
} else if (IsFastElementsKind(elements_kind)) {
access.type = TypeCache::Get().kFixedArrayLengthType;
access.machine_type = MachineType::TaggedSigned();
access.write_barrier_kind = kNoWriteBarrier;
} else {
access.type = TypeCache::Get().kJSArrayLengthType;
}
} else if (instance_type == JS_TYPED_ARRAY_TYPE) {
access.type = TypeCache::Get().kJSTypedArrayLengthType;
access.machine_type = MachineType::TaggedSigned();
access.write_barrier_kind = kNoWriteBarrier;
}
return access;
}
// static
FieldAccess AccessBuilder::ForJSArrayIteratorObjectMap() {
FieldAccess access = {kTaggedBase,
JSArrayIterator::kIteratedObjectMapOffset,
Handle<Name>(),
Type::OtherInternal(),
MachineType::TaggedPointer(),
kPointerWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForJSStringIteratorString() {
FieldAccess access = {
......
......@@ -191,6 +191,16 @@ class V8_EXPORT_PRIVATE AccessBuilder final
// Provides access to JSGlobalObject::native_context() field.
static FieldAccess ForJSGlobalObjectNativeContext();
// Provides access to JSArrayIterator::object() field.
static FieldAccess ForJSArrayIteratorObject();
// Provides access to JSArrayIterator::index() field.
static FieldAccess ForJSArrayIteratorIndex(InstanceType type = JS_OBJECT_TYPE,
ElementsKind kind = NO_ELEMENTS);
// Provides access to JSArrayIterator::object_map() field.
static FieldAccess ForJSArrayIteratorObjectMap();
// Provides access to JSStringIterator::string() field.
static FieldAccess ForJSStringIteratorString();
......
This diff is collapsed.
......@@ -45,6 +45,16 @@ class V8_EXPORT_PRIVATE JSBuiltinReducer final
Reduction Reduce(Node* node) final;
private:
Reduction ReduceArrayIterator(Node* node, IterationKind kind);
Reduction ReduceTypedArrayIterator(Node* node, IterationKind kind);
Reduction ReduceArrayIterator(Handle<Map> receiver_map, Node* node,
IterationKind kind,
ArrayIteratorKind iter_kind);
Reduction ReduceArrayIteratorNext(Node* node);
Reduction ReduceFastArrayIteratorNext(Handle<Map> iterator_map, Node* node,
IterationKind kind);
Reduction ReduceTypedArrayIteratorNext(Handle<Map> iterator_map, Node* node,
IterationKind kind);
Reduction ReduceArrayPop(Node* node);
Reduction ReduceArrayPush(Node* node);
Reduction ReduceDateGetTime(Node* node);
......
......@@ -210,6 +210,8 @@ Reduction JSCreateLowering::Reduce(Node* node) {
return ReduceJSCreateClosure(node);
case IrOpcode::kJSCreateIterResultObject:
return ReduceJSCreateIterResultObject(node);
case IrOpcode::kJSCreateKeyValueArray:
return ReduceJSCreateKeyValueArray(node);
case IrOpcode::kJSCreateLiteralArray:
case IrOpcode::kJSCreateLiteralObject:
return ReduceJSCreateLiteral(node);
......@@ -721,6 +723,36 @@ Reduction JSCreateLowering::ReduceJSCreateIterResultObject(Node* node) {
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreateKeyValueArray(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateKeyValueArray, node->opcode());
Node* key = NodeProperties::GetValueInput(node, 0);
Node* value = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* array_map = jsgraph()->HeapConstant(
handle(native_context()->js_array_fast_elements_map_index()));
Node* properties = jsgraph()->EmptyFixedArrayConstant();
Node* length = jsgraph()->Constant(2);
AllocationBuilder aa(jsgraph(), effect, graph()->start());
aa.AllocateArray(2, factory()->fixed_array_map());
aa.Store(AccessBuilder::ForFixedArrayElement(FAST_ELEMENTS),
jsgraph()->Constant(0), key);
aa.Store(AccessBuilder::ForFixedArrayElement(FAST_ELEMENTS),
jsgraph()->Constant(1), value);
Node* elements = aa.Finish();
AllocationBuilder a(jsgraph(), elements, graph()->start());
a.Allocate(JSArray::kSize);
a.Store(AccessBuilder::ForMap(), array_map);
a.Store(AccessBuilder::ForJSObjectProperties(), properties);
a.Store(AccessBuilder::ForJSObjectElements(), elements);
a.Store(AccessBuilder::ForJSArrayLength(FAST_ELEMENTS), length);
STATIC_ASSERT(JSArray::kSize == 4 * kPointerSize);
a.FinishAndChange(node);
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreateLiteral(Node* node) {
DCHECK(node->opcode() == IrOpcode::kJSCreateLiteralArray ||
node->opcode() == IrOpcode::kJSCreateLiteralObject);
......
......@@ -51,6 +51,7 @@ class V8_EXPORT_PRIVATE JSCreateLowering final
Reduction ReduceJSCreateArray(Node* node);
Reduction ReduceJSCreateClosure(Node* node);
Reduction ReduceJSCreateIterResultObject(Node* node);
Reduction ReduceJSCreateKeyValueArray(Node* node);
Reduction ReduceJSCreateLiteral(Node* node);
Reduction ReduceJSCreateFunctionContext(Node* node);
Reduction ReduceJSCreateWithContext(Node* node);
......
......@@ -463,6 +463,9 @@ void JSGenericLowering::LowerJSCreateIterResultObject(Node* node) {
ReplaceWithRuntimeCall(node, Runtime::kCreateIterResultObject);
}
void JSGenericLowering::LowerJSCreateKeyValueArray(Node* node) {
ReplaceWithRuntimeCall(node, Runtime::kCreateKeyValueArray);
}
void JSGenericLowering::LowerJSCreateLiteralArray(Node* node) {
CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op());
......
......@@ -445,6 +445,7 @@ CompareOperationHint CompareOperationHintOf(const Operator* op) {
V(ToString, Operator::kNoProperties, 1, 1) \
V(Create, Operator::kEliminatable, 2, 1) \
V(CreateIterResultObject, Operator::kEliminatable, 2, 1) \
V(CreateKeyValueArray, Operator::kEliminatable, 2, 1) \
V(HasProperty, Operator::kNoProperties, 2, 1) \
V(TypeOf, Operator::kPure, 1, 1) \
V(InstanceOf, Operator::kNoProperties, 2, 1) \
......
......@@ -458,6 +458,7 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* CreateClosure(Handle<SharedFunctionInfo> shared_info,
PretenureFlag pretenure);
const Operator* CreateIterResultObject();
const Operator* CreateKeyValueArray();
const Operator* CreateLiteralArray(Handle<FixedArray> constant_elements,
int literal_flags, int literal_index,
int number_of_elements);
......
......@@ -128,6 +128,7 @@
V(JSCreateArray) \
V(JSCreateClosure) \
V(JSCreateIterResultObject) \
V(JSCreateKeyValueArray) \
V(JSCreateLiteralArray) \
V(JSCreateLiteralObject) \
V(JSCreateLiteralRegExp) \
......
......@@ -1103,6 +1103,9 @@ Type* Typer::Visitor::TypeJSCreateIterResultObject(Node* node) {
return Type::OtherObject();
}
Type* Typer::Visitor::TypeJSCreateKeyValueArray(Node* node) {
return Type::OtherObject();
}
Type* Typer::Visitor::TypeJSCreateLiteralArray(Node* node) {
return Type::OtherObject();
......@@ -1384,6 +1387,15 @@ Type* Typer::Visitor::JSCallFunctionTyper(Type* fun, Typer* t) {
case kStringIteratorNext:
return Type::OtherObject();
case kArrayEntries:
case kArrayKeys:
case kArrayValues:
case kTypedArrayEntries:
case kTypedArrayKeys:
case kTypedArrayValues:
case kArrayIteratorNext:
return Type::OtherObject();
// Array functions.
case kArrayIndexOf:
case kArrayLastIndexOf:
......
......@@ -158,6 +158,7 @@ namespace compiler {
V(DetectableReceiver, kFunction | kOtherObject | kProxy) \
V(Object, kFunction | kOtherObject | kOtherUndetectable) \
V(Receiver, kObject | kProxy) \
V(ReceiverOrUndefined, kReceiver | kUndefined) \
V(StringOrReceiver, kString | kReceiver) \
V(Unique, kBoolean | kUniqueName | kNull | kUndefined | \
kReceiver) \
......
......@@ -579,6 +579,10 @@ void Verifier::Visitor::Check(Node* node) {
// Type is OtherObject.
CheckTypeIs(node, Type::OtherObject());
break;
case IrOpcode::kJSCreateKeyValueArray:
// Type is OtherObject.
CheckTypeIs(node, Type::OtherObject());
break;
case IrOpcode::kJSCreateLiteralArray:
case IrOpcode::kJSCreateLiteralObject:
case IrOpcode::kJSCreateLiteralRegExp:
......
......@@ -1279,9 +1279,28 @@ inline std::ostream& operator<<(std::ostream& os, UnicodeEncoding encoding) {
return os;
}
enum class IterationKind { kKeys, kValues, kEntries };
inline std::ostream& operator<<(std::ostream& os, IterationKind kind) {
switch (kind) {
case IterationKind::kKeys:
return os << "IterationKind::kKeys";
case IterationKind::kValues:
return os << "IterationKind::kValues";
case IterationKind::kEntries:
return os << "IterationKind::kEntries";
}
UNREACHABLE();
return os;
}
} // namespace internal
} // namespace v8
// Used by js-builtin-reducer to identify whether ReduceArrayIterator() is
// reducing a JSArray method, or a JSTypedArray method.
enum class ArrayIteratorKind { kArray, kTypedArray };
namespace i = v8::internal;
#endif // V8_GLOBALS_H_
......@@ -2850,6 +2850,10 @@ void Heap::CreateInitialObjects() {
cell->set_value(Smi::FromInt(Isolate::kArrayProtectorValid));
set_string_length_protector(*cell);
Handle<Cell> fast_array_iteration_cell = factory->NewCell(
handle(Smi::FromInt(Isolate::kArrayProtectorValid), isolate()));
set_fast_array_iteration_protector(*fast_array_iteration_cell);
set_serialized_templates(empty_fixed_array());
set_weak_stack_trace_list(Smi::kZero);
......
......@@ -166,6 +166,7 @@ using v8::MemoryPressureLevel;
V(PropertyCell, has_instance_protector, HasInstanceProtector) \
V(Cell, species_protector, SpeciesProtector) \
V(PropertyCell, string_length_protector, StringLengthProtector) \
V(Cell, fast_array_iteration_protector, FastArrayIterationProtector) \
/* Special numbers */ \
V(HeapNumber, nan_value, NanValue) \
V(HeapNumber, hole_nan_value, HoleNanValue) \
......
......@@ -143,6 +143,11 @@ bool Isolate::IsStringLengthOverflowIntact() {
return has_instance_cell->value() == Smi::FromInt(kArrayProtectorValid);
}
bool Isolate::IsFastArrayIterationIntact() {
Cell* fast_iteration = heap()->fast_array_iteration_protector();
return fast_iteration->value() == Smi::FromInt(kArrayProtectorValid);
}
} // namespace internal
} // namespace v8
......
......@@ -997,6 +997,9 @@ class Isolate {
bool IsIsConcatSpreadableLookupChainIntact(JSReceiver* receiver);
inline bool IsStringLengthOverflowIntact();
// Avoid deopt loops if fast Array Iterators migrate to slow Array Iterators.
inline bool IsFastArrayIterationIntact();
// On intent to set an element in object, make sure that appropriate
// notifications occur if the set is on the elements of the array or
// object prototype. Also ensure that changes to prototype chain between
......
......@@ -20345,5 +20345,47 @@ MaybeHandle<Name> FunctionTemplateInfo::TryGetCachedPropertyName(
return MaybeHandle<Name>();
}
// static
ElementsKind JSArrayIterator::ElementsKindForInstanceType(InstanceType type) {
DCHECK_GE(type, FIRST_ARRAY_ITERATOR_TYPE);
DCHECK_LE(type, LAST_ARRAY_ITERATOR_TYPE);
if (type <= LAST_ARRAY_KEY_ITERATOR_TYPE) {
// Should be ignored for key iterators.
return FAST_ELEMENTS;
} else {
ElementsKind kind;
if (type < FIRST_ARRAY_VALUE_ITERATOR_TYPE) {
// Convert `type` to a value iterator from an entries iterator
type = static_cast<InstanceType>(type +
(FIRST_ARRAY_VALUE_ITERATOR_TYPE -
FIRST_ARRAY_KEY_VALUE_ITERATOR_TYPE));
DCHECK_GE(type, FIRST_ARRAY_VALUE_ITERATOR_TYPE);
DCHECK_LE(type, LAST_ARRAY_ITERATOR_TYPE);
}
if (type <= JS_UINT8_CLAMPED_ARRAY_VALUE_ITERATOR_TYPE) {
kind =
static_cast<ElementsKind>(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND +
(type - FIRST_ARRAY_VALUE_ITERATOR_TYPE));
DCHECK_LE(kind, LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
} else if (type < JS_GENERIC_ARRAY_VALUE_ITERATOR_TYPE) {
kind = static_cast<ElementsKind>(
FIRST_FAST_ELEMENTS_KIND +
(type - JS_FAST_SMI_ARRAY_VALUE_ITERATOR_TYPE));
DCHECK_LE(kind, LAST_FAST_ELEMENTS_KIND);
} else {
// For any slow element cases, the actual elements kind is not known.
// Simply
// return a slow elements kind in this case. Users of this function must
// not
// depend on this.
return DICTIONARY_ELEMENTS;
}
DCHECK_LE(kind, LAST_ELEMENTS_KIND);
return kind;
}
}
} // namespace internal
} // namespace v8
......@@ -7279,6 +7279,10 @@ enum BuiltinFunctionId {
kMathPowHalf,
// These are manually assigned to special getters during bootstrapping.
kArrayBufferByteLength,
kArrayEntries,
kArrayKeys,
kArrayValues,
kArrayIteratorNext,
kDataViewBuffer,
kDataViewByteLength,
kDataViewByteOffset,
......@@ -7292,7 +7296,10 @@ enum BuiltinFunctionId {
kGlobalIsNaN,
kTypedArrayByteLength,
kTypedArrayByteOffset,
kTypedArrayEntries,
kTypedArrayKeys,
kTypedArrayLength,
kTypedArrayValues,
kSharedArrayBufferByteLength,
kStringIterator,
kStringIteratorNext,
......@@ -10709,6 +10716,10 @@ class JSArrayIterator : public JSObject {
// allocated.
DECL_ACCESSORS(object_map, Object)
// Return the ElementsKind that a JSArrayIterator's [[IteratedObject]] is
// expected to have, based on its instance type.
static ElementsKind ElementsKindForInstanceType(InstanceType instance_type);
static const int kIteratedObjectOffset = JSObject::kHeaderSize;
static const int kNextIndexOffset = kIteratedObjectOffset + kPointerSize;
static const int kIteratedObjectMapOffset = kNextIndexOffset + kPointerSize;
......
......@@ -31,8 +31,10 @@ RUNTIME_FUNCTION(Runtime_FinishArrayPrototypeSetup) {
return Smi::kZero;
}
static void InstallCode(Isolate* isolate, Handle<JSObject> holder,
const char* name, Handle<Code> code, int argc = -1) {
static void InstallCode(
Isolate* isolate, Handle<JSObject> holder, const char* name,
Handle<Code> code, int argc = -1,
BuiltinFunctionId id = static_cast<BuiltinFunctionId>(-1)) {
Handle<String> key = isolate->factory()->InternalizeUtf8String(name);
Handle<JSFunction> optimized =
isolate->factory()->NewFunctionWithoutPrototype(key, code);
......@@ -41,15 +43,19 @@ static void InstallCode(Isolate* isolate, Handle<JSObject> holder,
} else {
optimized->shared()->set_internal_formal_parameter_count(argc);
}
if (id >= 0) {
optimized->shared()->set_builtin_function_id(id);
}
JSObject::AddProperty(holder, key, optimized, NONE);
}
static void InstallBuiltin(Isolate* isolate, Handle<JSObject> holder,
const char* name, Builtins::Name builtin_name,
int argc = -1) {
static void InstallBuiltin(
Isolate* isolate, Handle<JSObject> holder, const char* name,
Builtins::Name builtin_name, int argc = -1,
BuiltinFunctionId id = static_cast<BuiltinFunctionId>(-1)) {
InstallCode(isolate, holder, name,
handle(isolate->builtins()->builtin(builtin_name), isolate),
argc);
handle(isolate->builtins()->builtin(builtin_name), isolate), argc,
id);
}
RUNTIME_FUNCTION(Runtime_SpecialArrayFunctions) {
......@@ -71,10 +77,12 @@ RUNTIME_FUNCTION(Runtime_SpecialArrayFunctions) {
InstallBuiltin(isolate, holder, "splice", Builtins::kArraySplice);
InstallBuiltin(isolate, holder, "includes", Builtins::kArrayIncludes, 2);
InstallBuiltin(isolate, holder, "indexOf", Builtins::kArrayIndexOf, 2);
InstallBuiltin(isolate, holder, "keys", Builtins::kArrayPrototypeKeys, 0);
InstallBuiltin(isolate, holder, "values", Builtins::kArrayPrototypeValues, 0);
InstallBuiltin(isolate, holder, "keys", Builtins::kArrayPrototypeKeys, 0,
kArrayKeys);
InstallBuiltin(isolate, holder, "values", Builtins::kArrayPrototypeValues, 0,
kArrayValues);
InstallBuiltin(isolate, holder, "entries", Builtins::kArrayPrototypeEntries,
0);
0, kArrayEntries);
return *holder;
}
......
......@@ -925,6 +925,17 @@ RUNTIME_FUNCTION(Runtime_CreateIterResultObject) {
return *isolate->factory()->NewJSIteratorResult(value, done->BooleanValue());
}
RUNTIME_FUNCTION(Runtime_CreateKeyValueArray) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, key, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, value, 1);
Handle<FixedArray> elements = isolate->factory()->NewFixedArray(2);
elements->set(0, *key);
elements->set(1, *value);
return *isolate->factory()->NewJSArrayWithElements(elements, FAST_ELEMENTS,
2);
}
RUNTIME_FUNCTION(Runtime_IsAccessCheckNeeded) {
SealHandleScope shs(isolate);
......
......@@ -426,6 +426,7 @@ namespace internal {
F(Compare, 3, 1) \
F(HasInPrototypeChain, 2, 1) \
F(CreateIterResultObject, 2, 1) \
F(CreateKeyValueArray, 2, 1) \
F(IsAccessCheckNeeded, 1, 1) \
F(CreateDataProperty, 3, 1)
......
// Copyright 2016 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.
// Flags: --turbo --turbo-escape --allow-natives-syntax
"use strict";
const kDeoptimized = 2;
const kTurbofanned = 7;
const kInterpreted = 8;
function GetOptimizationStatus(fn) {
let status = %GetOptimizationStatus(fn);
switch (status) {
case kInterpreted: // Treat interpreted frames as unoptimized
status = kDeoptimized;
break;
}
return status;
}
let global = this;
let tests = {
FastElementsKind() {
let runners = {
FAST_SMI_ELEMENTS(array) {
let sum = 0;
for (let x of array) sum += x;
return sum;
},
FAST_HOLEY_SMI_ELEMENTS(array) {
let sum = 0;
for (let x of array) {
if (x) sum += x;
}
return sum;
},
FAST_ELEMENTS(array) {
let ret = "";
for (let str of array) ret += `> ${str}`;
return ret;
},
FAST_HOLEY_ELEMENTS(array) {
let ret = "";
for (let str of array) ret += `> ${str}`;
return ret;
},
FAST_DOUBLE_ELEMENTS(array) {
let sum = 0.0;
for (let x of array) sum += x;
return sum;
},
FAST_HOLEY_DOUBLE_ELEMENTS(array) {
let sum = 0.0;
for (let x of array) {
if (x) sum += x;
}
return sum;
}
};
let tests = {
FAST_SMI_ELEMENTS: {
array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
expected: 55,
array2: [1, 2, 3],
expected2: 6
},
FAST_HOLEY_SMI_ELEMENTS: {
array: [1, , 3, , 5, , 7, , 9, ,],
expected: 25,
array2: [1, , 3],
expected2: 4
},
FAST_ELEMENTS: {
array: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"],
expected: "> a> b> c> d> e> f> g> h> i> j",
array2: ["a", "b", "c"],
expected2: "> a> b> c"
},
FAST_HOLEY_ELEMENTS: {
array: ["a", , "c", , "e", , "g", , "i", ,],
expected: "> a> undefined> c> undefined> e> undefined> g" +
"> undefined> i> undefined",
array2: ["a", , "c"],
expected2: "> a> undefined> c"
},
FAST_DOUBLE_ELEMENTS: {
array: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0],
expected: 5.5,
array2: [0.6, 0.4, 0.2],
expected2: 1.2
},
FAST_HOLEY_DOUBLE_ELEMENTS: {
array: [0.1, , 0.3, , 0.5, , 0.7, , 0.9, ,],
expected: 2.5,
array2: [0.1, , 0.3],
expected2: 0.4
}
};
for (let key of Object.keys(runners)) {
let fn = runners[key];
let { array, expected, array2, expected2 } = tests[key];
// Warmup:
fn(array);
fn(array);
%OptimizeFunctionOnNextCall(fn);
fn(array);
// TODO(bmeurer): FAST_HOLEY_DOUBLE_ELEMENTS maps generally deopt when
// a hole is encountered. Test should be fixed once that is corrected.
let status = /HOLEY_DOUBLE/.test(key) ? kDeoptimized : kTurbofanned;
assertEquals(status, GetOptimizationStatus(fn), key);
assertEquals(expected, fn(array), key);
assertEquals(status, GetOptimizationStatus(fn), key);
// Check no deopt when another arra with the same map is used
assertTrue(%HaveSameMap(array, array2), key);
assertEquals(status, GetOptimizationStatus(fn), key);
assertEquals(expected2, fn(array2), key);
// CheckMaps bailout
let newArray = Object.defineProperty(
[1, 2, 3], 2, { enumerable: false, configurable: false,
get() { return 7; } });
fn(newArray);
assertEquals(kDeoptimized, GetOptimizationStatus(fn), key);
}
},
TypedArrays() {
let tests = {
Uint8Array: {
array: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, -1, 256]),
expected: 291,
array2: new Uint8Array([1, 2, 3]),
expected2: 6
},
Int8Array: {
array: new Int8Array([1, 2, 3, 4, 5, 6, 7, 8, -129, 128]),
expected: 35,
array2: new Int8Array([1, 2, 3]),
expected2: 6
},
Uint16Array: {
array: new Uint16Array([1, 2, 3, 4, 5, 6, 7, 8, -1, 0x10000]),
expected: 65571,
array2: new Uint16Array([1, 2, 3]),
expected2: 6
},
Int16Array: {
array: new Int16Array([1, 2, 3, 4, 5, 6, 7, 8, -32769, 0x7FFF]),
expected: 65570,
array2: new Int16Array([1, 2, 3]),
expected2: 6
},
Uint32Array: {
array: new Uint32Array([1, 2, 3, 4, 5, 6, 7, 8, -1, 0x100000000]),
expected: 4294967331,
array2: new Uint32Array([1, 2, 3]),
expected2: 6
},
Int32Array: {
array: new Int32Array([1, 2, 3, 4, 5, 6, 7, 8,
-2147483649, 0x7FFFFFFF]),
expected: 4294967330,
array2: new Int32Array([1, 2, 3]),
expected2: 6
},
Float32Array: {
array: new Float32Array([9.5, 8.0, 7.0, 7.0, 5.0, 4.0, 3.0, 2.0]),
expected: 45.5,
array2: new Float32Array([10.5, 5.5, 1.5]),
expected2: 17.5
},
Float64Array: {
array: new Float64Array([9.5, 8.0, 7.0, 7.0, 5.0, 4.0, 3.0, 2.0]),
expected: 45.5,
array2: new Float64Array([10.5, 5.5, 1.5]),
expected2: 17.5
},
Uint8ClampedArray: {
array: new Uint8ClampedArray([4.3, 7.45632, 3.14, 4.61, 5.0004, 6.493,
7.12, 8, 1.7, 3.6]),
expected: 51,
array2: new Uint8ClampedArray([1, 2, 3]),
expected2: 6
}
};
for (let key of Object.keys(tests)) {
let test = tests[key];
let { array, expected, array2, expected2 } = test;
let sum = function(array) {
let ret = 0;
for (let x of array) ret += x;
return ret;
};
// Warmup
sum(array);
sum(array);
%OptimizeFunctionOnNextCall(sum);
assertEquals(expected, sum(array), key);
assertEquals(kTurbofanned, GetOptimizationStatus(sum), key);
// Not deoptimized when called on typed array of same type / map
assertTrue(%HaveSameMap(array, array2));
assertEquals(expected2, sum(array2), key);
assertEquals(kTurbofanned, GetOptimizationStatus(sum), key);
// Throw when detached
let clone = new array.constructor(array);
%ArrayBufferNeuter(clone.buffer);
assertThrows(() => sum(clone), TypeError);
}
}
};
for (let name of Object.keys(tests)) {
let test = tests[name];
test();
}
......@@ -594,14 +594,22 @@
'regress/regress-353551': [SKIP],
}], # 'arch == ppc64'
##############################################################################
['variant == nocrankshaft', {
'es6/array-iterator-turbo': [SKIP],
}], # variant == nocranshaft
##############################################################################
['variant == stress', {
'es6/array-iterator-turbo': [SKIP],
'ignition/regress-599001-verifyheap': [SKIP],
'unicode-test': [SKIP],
}], # variant == stress
##############################################################################
['variant == turbofan_opt', {
'es6/array-iterator-turbo': [SKIP],
# TODO(jarin/mstarzinger): Investigate debugger issues with TurboFan.
'debug-evaluate-locals': [FAIL],
......
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