 Chromium Code Reviews
 Chromium Code Reviews Issue 2146293003:
  [builtins] implement Array.prototype.includes in TurboFan  (Closed) 
  Base URL: https://chromium.googlesource.com/v8/v8.git@master
    
  
    Issue 2146293003:
  [builtins] implement Array.prototype.includes in TurboFan  (Closed) 
  Base URL: https://chromium.googlesource.com/v8/v8.git@master| Index: src/builtins/builtins.cc | 
| diff --git a/src/builtins/builtins.cc b/src/builtins/builtins.cc | 
| index e57091311a77d97b090c10a92f334340e7915c4f..8c20d81ba18cfb16d1ffcbc2ec9fadf3e2f66c58 100644 | 
| --- a/src/builtins/builtins.cc | 
| +++ b/src/builtins/builtins.cc | 
| @@ -343,6 +343,219 @@ void Builtins::Generate_ArrayIsArray(CodeStubAssembler* assembler) { | 
| assembler->CallRuntime(Runtime::kArrayIsArray, context, object)); | 
| } | 
| +void Builtins::Generate_ArrayIncludes(CodeStubAssembler* assembler) { | 
| + typedef compiler::Node Node; | 
| + typedef CodeStubAssembler::Label Label; | 
| + typedef CodeStubAssembler::Variable Variable; | 
| + | 
| + Node* array = assembler->Parameter(0); | 
| + Node* search_element = assembler->Parameter(1); | 
| + Node* start_from = assembler->Parameter(2); | 
| + Node* context = assembler->Parameter(3 + 2); | 
| + | 
| + Node* int32_zero = assembler->Int32Constant(0); | 
| + Node* int32_one = assembler->Int32Constant(1); | 
| + | 
| + Variable len(assembler, MachineRepresentation::kWord32), | 
| + k(assembler, MachineRepresentation::kWord32), | 
| + n(assembler, MachineRepresentation::kWord32); | 
| + | 
| + Label init_k(assembler), return_true(assembler), return_false(assembler), | 
| + call_runtime(assembler); | 
| + | 
| + { // Prologue | 
| + // 1. Bailout to slow path if `array` is not a JSArray | 
| + assembler->GotoIf(assembler->WordIsSmi(array), &call_runtime); | 
| + Node* instance_type = assembler->LoadInstanceType(array); | 
| + assembler->GotoUnless( | 
| + assembler->Word32Equal(instance_type, | 
| + assembler->Int32Constant(JS_ARRAY_TYPE)), | 
| + &call_runtime); | 
| + | 
| + // 2. Bailout to slow path if elements kind is not fast | 
| + Node* map = assembler->LoadMap(array); | 
| + Node* bit_field2 = assembler->LoadMapBitField2(map); | 
| + Node* elements_kind = | 
| + assembler->BitFieldDecode<Map::ElementsKindBits>(bit_field2); | 
| + assembler->GotoIf( | 
| + assembler->Int32GreaterThan( | 
| + elements_kind, assembler->Int32Constant(LAST_FAST_ELEMENTS_KIND)), | 
| + &call_runtime); | 
| + | 
| + // 3. Bailout to slow path if array protector field invalid | 
| + assembler->GotoUnless( | 
| + assembler->WordEqual( | 
| + assembler->LoadObjectField( | 
| + assembler->LoadRoot(Heap::kArrayProtectorRootIndex), | 
| + PropertyCell::kValueOffset), | 
| + assembler->SmiConstant( | 
| + Smi::FromInt(Isolate::kArrayProtectorValid))), | 
| + &call_runtime); | 
| + | 
| + len.Bind(assembler->SmiToWord( | 
| + assembler->LoadObjectField(array, JSArray::kLengthOffset))); | 
| + assembler->GotoUnless(assembler->Word32Equal(len.value(), int32_zero), | 
| + &init_k); | 
| + assembler->Return(assembler->BooleanConstant(false)); | 
| + } | 
| + | 
| + assembler->Bind(&init_k); | 
| + { | 
| + Variable tagged_n(assembler, MachineRepresentation::kTagged); | 
| + Label done(assembler), if_neg(assembler), init_k_smi(assembler), | 
| + init_k_zero(assembler); | 
| + Callable call_to_integer = CodeFactory::ToInteger(assembler->isolate()); | 
| + tagged_n.Bind(assembler->CallStub(call_to_integer, context, start_from)); | 
| + | 
| + assembler->GotoIf(assembler->WordIsSmi(tagged_n.value()), &init_k_smi); | 
| + | 
| + Node* fp_len = assembler->ChangeInt32ToFloat64(len.value()); | 
| + Node* fp_n = assembler->TruncateTaggedToFloat64(context, tagged_n.value()); | 
| + | 
| + assembler->GotoIf(assembler->Float64GreaterThanOrEqual(fp_n, fp_len), | 
| + &return_false); | 
| + | 
| + tagged_n.Bind( | 
| + assembler->SmiFromWord32(assembler->TruncateFloat64ToWord32(fp_n))); | 
| + | 
| + assembler->Goto(&init_k_smi); | 
| + assembler->Bind(&init_k_smi); | 
| + n.Bind(assembler->SmiToWord32(tagged_n.value())); | 
| + | 
| + assembler->GotoUnless( | 
| + assembler->Int32GreaterThanOrEqual(n.value(), int32_zero), &if_neg); | 
| + k.Bind(n.value()); | 
| + assembler->Goto(&done); | 
| + | 
| + assembler->Bind(&if_neg); | 
| + k.Bind(assembler->Int32Add(len.value(), n.value())); | 
| + assembler->BranchIf(assembler->Int32LessThan(k.value(), int32_zero), | 
| + &init_k_zero, &done); | 
| + | 
| + assembler->Bind(&init_k_zero); | 
| + k.Bind(int32_zero); | 
| + | 
| + assembler->Goto(&done); | 
| + assembler->Bind(&done); | 
| + } | 
| + | 
| + { // Repeat while k < len | 
| + Variable element_k(assembler, MachineRepresentation::kTagged); | 
| + Variable stable(assembler, MachineRepresentation::kWord32); | 
| + | 
| + Variable* loop_body_variables[] = {&k, &stable}; | 
| + | 
| + Label loop_body(assembler, 2, | 
| 
Benedikt Meurer
2016/07/15 17:59:55
You really want one really tight loop per elements
 
caitp
2016/07/15 22:11:17
This is a good idea, but currently this is not sou
 
Benedikt Meurer
2016/07/16 12:40:27
Ah indeed, you also need to check that the prototy
 | 
| + static_cast<Variable **>(loop_body_variables)), | 
| + continue_loop(assembler), if_issmiorobject(assembler), | 
| + if_isdouble(assembler), if_slowpath(assembler), test_element(assembler); | 
| + | 
| + stable.Bind(int32_one); | 
| + Node* map = assembler->LoadMap(array); | 
| + Node* bit_field2 = assembler->LoadMapBitField2(map); | 
| + Node* elements_kind = | 
| + assembler->BitFieldDecode<Map::ElementsKindBits>(bit_field2); | 
| + | 
| + assembler->Goto(&loop_body); | 
| + assembler->Bind(&loop_body); | 
| + | 
| + assembler->GotoUnless(assembler->Uint32LessThan(k.value(), len.value()), | 
| + &return_false); | 
| + | 
| + assembler->GotoIf(assembler->WordEqual(stable.value(), int32_zero), | 
| + &if_slowpath); | 
| + | 
| + assembler->Branch( | 
| + assembler->Int32LessThan( | 
| + elements_kind, assembler->Int32Constant(FAST_DOUBLE_ELEMENTS)), | 
| + &if_issmiorobject, &if_isdouble); | 
| + | 
| + assembler->Bind(&if_issmiorobject); | 
| + { // Fast Smi/Object elements | 
| + Node* elements = assembler->LoadElements(array); | 
| + Node* length = assembler->LoadFixedArrayBaseLength(elements); | 
| + | 
| + assembler->GotoUnless( | 
| + assembler->Int32LessThan(k.value(), assembler->SmiToWord32(length)), | 
| + &if_slowpath); | 
| + | 
| + Node* element = assembler->LoadFixedArrayElement(elements, k.value()); | 
| + Node* the_hole = assembler->TheHoleConstant(); | 
| + | 
| + assembler->GotoUnless(assembler->WordNotEqual(element, the_hole), | 
| + &if_slowpath); | 
| 
Benedikt Meurer
2016/07/15 17:59:55
You can just turn the hole into undefined, since y
 
caitp
2016/07/15 22:11:17
I'm not sure, we still end up on the fast path wit
 
Benedikt Meurer
2016/07/16 12:40:27
As mentioned above, indexed accessors in the proto
 | 
| + element_k.Bind(element); | 
| + assembler->Goto(&test_element); | 
| + } | 
| + | 
| + assembler->Bind(&if_isdouble); | 
| + { // Fast double elements | 
| + Node* elements = assembler->LoadElements(array); | 
| + Node* length = assembler->LoadFixedArrayBaseLength(elements); | 
| + | 
| + assembler->GotoUnless( | 
| + assembler->Int32LessThan(k.value(), assembler->SmiToWord32(length)), | 
| + &if_slowpath); | 
| + | 
| + if (kPointerSize == kDoubleSize) { | 
| + Node* element = assembler->LoadFixedDoubleArrayElement( | 
| + elements, k.value(), MachineType::Uint64()); | 
| + Node* the_hole = assembler->Int64Constant(kHoleNanInt64); | 
| + assembler->GotoIf(assembler->Word64Equal(element, the_hole), | 
| + &if_slowpath); | 
| + } else { | 
| + Node* element_upper = assembler->LoadFixedDoubleArrayElement( | 
| + elements, k.value(), MachineType::Uint32(), | 
| + kIeeeDoubleExponentWordOffset); | 
| + assembler->GotoIf( | 
| + assembler->Word32Equal(element_upper, | 
| + assembler->Int32Constant(kHoleNanUpper32)), | 
| + &if_slowpath); | 
| + } | 
| + | 
| + Node* element = assembler->LoadFixedDoubleArrayElement( | 
| + elements, k.value(), MachineType::Float64()); | 
| + element_k.Bind(assembler->AllocateHeapNumberWithValue(element)); | 
| + assembler->Goto(&test_element); | 
| + } | 
| + | 
| + assembler->Bind(&if_slowpath); | 
| + { | 
| + Node* element = | 
| + assembler->CallRuntime(Runtime::kGetProperty, context, array, | 
| + assembler->SmiFromWord32(k.value())); | 
| + element_k.Bind(element); | 
| + | 
| + // Slow path may have modified elements backing store in some way. | 
| + assembler->GotoIf(assembler->WordEqual(stable.value(), int32_zero), | 
| + &test_element); | 
| + Node* current_map = assembler->LoadMap(array); | 
| + assembler->GotoIf(assembler->WordEqual(map, current_map), &test_element); | 
| + stable.Bind(int32_zero); | 
| + assembler->Goto(&test_element); | 
| + } | 
| + | 
| + assembler->Bind(&test_element); | 
| + assembler->BranchIfSameValueZero(search_element, element_k.value(), context, | 
| + &return_true, &continue_loop); | 
| + | 
| + assembler->Bind(&continue_loop); | 
| + k.Bind(assembler->Int32Add(k.value(), int32_one)); | 
| + assembler->Goto(&loop_body); | 
| + } | 
| + | 
| + assembler->Bind(&return_true); | 
| + assembler->Return(assembler->BooleanConstant(true)); | 
| + | 
| + assembler->Bind(&return_false); | 
| + assembler->Return(assembler->BooleanConstant(false)); | 
| + | 
| + assembler->Bind(&call_runtime); | 
| + assembler->Return(assembler->CallRuntime(Runtime::kArrayIncludes_Slow, | 
| + context, array, search_element, | 
| + start_from)); | 
| +} | 
| + | 
| void Builtins::Generate_ObjectHasOwnProperty(CodeStubAssembler* assembler) { | 
| typedef compiler::Node Node; | 
| typedef CodeStubAssembler::Label Label; |