LCOV - code coverage report
Current view: top level - src/builtins - builtins-typedarray-gen.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 286 301 95.0 %
Date: 2017-10-20 Functions: 31 32 96.9 %

          Line data    Source code
       1             : // Copyright 2017 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #include "src/builtins/builtins-utils-gen.h"
       6             : #include "src/builtins/builtins.h"
       7             : #include "src/code-stub-assembler.h"
       8             : #include "src/handles-inl.h"
       9             : 
      10             : namespace v8 {
      11             : namespace internal {
      12             : 
      13             : // This is needed for gc_mole which will compile this file without the full set
      14             : // of GN defined macros.
      15             : #ifndef V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP
      16             : #define V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP 64
      17             : #endif
      18             : 
      19             : // -----------------------------------------------------------------------------
      20             : // ES6 section 22.2 TypedArray Objects
      21             : 
      22             : class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
      23             :  public:
      24             :   explicit TypedArrayBuiltinsAssembler(compiler::CodeAssemblerState* state)
      25         372 :       : CodeStubAssembler(state) {}
      26             : 
      27             :  protected:
      28             :   void GenerateTypedArrayPrototypeGetter(Node* context, Node* receiver,
      29             :                                          const char* method_name,
      30             :                                          int object_offset);
      31             :   void GenerateTypedArrayPrototypeIterationMethod(Node* context, Node* receiver,
      32             :                                                   const char* method_name,
      33             :                                                   IterationKind iteration_kind);
      34             : 
      35             :   void SetupTypedArray(Node* holder, Node* length, Node* byte_offset,
      36             :                        Node* byte_length);
      37             :   void AttachBuffer(Node* holder, Node* buffer, Node* map, Node* length,
      38             :                     Node* byte_offset);
      39             : 
      40             :   Node* LoadMapForType(Node* array);
      41             :   Node* CalculateExternalPointer(Node* backing_store, Node* byte_offset);
      42             :   Node* LoadDataPtr(Node* typed_array);
      43             :   Node* ByteLengthIsValid(Node* byte_length);
      44             : };
      45             : 
      46          62 : compiler::Node* TypedArrayBuiltinsAssembler::LoadMapForType(Node* array) {
      47             :   CSA_ASSERT(this, IsJSTypedArray(array));
      48             : 
      49         124 :   Label unreachable(this), done(this);
      50          62 :   Label uint8_elements(this), uint8_clamped_elements(this), int8_elements(this),
      51          62 :       uint16_elements(this), int16_elements(this), uint32_elements(this),
      52          62 :       int32_elements(this), float32_elements(this), float64_elements(this);
      53             :   Label* elements_kind_labels[] = {
      54             :       &uint8_elements,  &uint8_clamped_elements, &int8_elements,
      55             :       &uint16_elements, &int16_elements,         &uint32_elements,
      56          62 :       &int32_elements,  &float32_elements,       &float64_elements};
      57             :   int32_t elements_kinds[] = {
      58             :       UINT8_ELEMENTS,  UINT8_CLAMPED_ELEMENTS, INT8_ELEMENTS,
      59             :       UINT16_ELEMENTS, INT16_ELEMENTS,         UINT32_ELEMENTS,
      60          62 :       INT32_ELEMENTS,  FLOAT32_ELEMENTS,       FLOAT64_ELEMENTS};
      61             :   const size_t kTypedElementsKindCount = LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
      62             :                                          FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND +
      63             :                                          1;
      64             :   DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds));
      65             :   DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels));
      66             : 
      67         124 :   VARIABLE(var_typed_map, MachineRepresentation::kTagged);
      68             : 
      69         124 :   Node* array_map = LoadMap(array);
      70         124 :   Node* elements_kind = LoadMapElementsKind(array_map);
      71             :   Switch(elements_kind, &unreachable, elements_kinds, elements_kind_labels,
      72          62 :          kTypedElementsKindCount);
      73             : 
      74         620 :   for (int i = 0; i < static_cast<int>(kTypedElementsKindCount); i++) {
      75         558 :     BIND(elements_kind_labels[i]);
      76             :     {
      77         558 :       ElementsKind kind = static_cast<ElementsKind>(elements_kinds[i]);
      78             :       ExternalArrayType type =
      79         558 :           isolate()->factory()->GetArrayTypeFromElementsKind(kind);
      80         558 :       Handle<Map> map(isolate()->heap()->MapForFixedTypedArray(type));
      81         558 :       var_typed_map.Bind(HeapConstant(map));
      82         558 :       Goto(&done);
      83             :     }
      84             :   }
      85             : 
      86          62 :   BIND(&unreachable);
      87          62 :   { Unreachable(); }
      88          62 :   BIND(&done);
      89         124 :   return var_typed_map.value();
      90             : }
      91             : 
      92             : // The byte_offset can be higher than Smi range, in which case to perform the
      93             : // pointer arithmetic necessary to calculate external_pointer, converting
      94             : // byte_offset to an intptr is more difficult. The max byte_offset is 8 * MaxSmi
      95             : // on the particular platform. 32 bit platforms are self-limiting, because we
      96             : // can't allocate an array bigger than our 32-bit arithmetic range anyway. 64
      97             : // bit platforms could theoretically have an offset up to 2^35 - 1, so we may
      98             : // need to convert the float heap number to an intptr.
      99          62 : compiler::Node* TypedArrayBuiltinsAssembler::CalculateExternalPointer(
     100             :     Node* backing_store, Node* byte_offset) {
     101             :   return IntPtrAdd(backing_store,
     102         186 :                    ChangeNonnegativeNumberToUintPtr(byte_offset));
     103             : }
     104             : 
     105             : // Setup the TypedArray which is under construction.
     106             : //  - Set the length.
     107             : //  - Set the byte_offset.
     108             : //  - Set the byte_length.
     109             : //  - Set EmbedderFields to 0.
     110          62 : void TypedArrayBuiltinsAssembler::SetupTypedArray(Node* holder, Node* length,
     111             :                                                   Node* byte_offset,
     112             :                                                   Node* byte_length) {
     113             :   CSA_ASSERT(this, IsJSTypedArray(holder));
     114             :   CSA_ASSERT(this, TaggedIsSmi(length));
     115             :   CSA_ASSERT(this, IsNumber(byte_offset));
     116             :   CSA_ASSERT(this, IsNumber(byte_length));
     117             : 
     118          62 :   StoreObjectField(holder, JSTypedArray::kLengthOffset, length);
     119          62 :   StoreObjectField(holder, JSArrayBufferView::kByteOffsetOffset, byte_offset);
     120          62 :   StoreObjectField(holder, JSArrayBufferView::kByteLengthOffset, byte_length);
     121         186 :   for (int offset = JSTypedArray::kSize;
     122             :        offset < JSTypedArray::kSizeWithEmbedderFields; offset += kPointerSize) {
     123         248 :     StoreObjectField(holder, offset, SmiConstant(0));
     124             :   }
     125          62 : }
     126             : 
     127             : // Attach an off-heap buffer to a TypedArray.
     128          62 : void TypedArrayBuiltinsAssembler::AttachBuffer(Node* holder, Node* buffer,
     129             :                                                Node* map, Node* length,
     130             :                                                Node* byte_offset) {
     131             :   CSA_ASSERT(this, IsJSTypedArray(holder));
     132             :   CSA_ASSERT(this, IsJSArrayBuffer(buffer));
     133             :   CSA_ASSERT(this, IsMap(map));
     134             :   CSA_ASSERT(this, TaggedIsSmi(length));
     135             :   CSA_ASSERT(this, IsNumber(byte_offset));
     136             : 
     137          62 :   StoreObjectField(holder, JSArrayBufferView::kBufferOffset, buffer);
     138             : 
     139          62 :   Node* elements = Allocate(FixedTypedArrayBase::kHeaderSize);
     140          62 :   StoreMapNoWriteBarrier(elements, map);
     141          62 :   StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
     142             :   StoreObjectFieldNoWriteBarrier(
     143         124 :       elements, FixedTypedArrayBase::kBasePointerOffset, SmiConstant(0));
     144             : 
     145             :   Node* backing_store = LoadObjectField(
     146          62 :       buffer, JSArrayBuffer::kBackingStoreOffset, MachineType::Pointer());
     147             : 
     148          62 :   Node* external_pointer = CalculateExternalPointer(backing_store, byte_offset);
     149             :   StoreObjectFieldNoWriteBarrier(
     150             :       elements, FixedTypedArrayBase::kExternalPointerOffset, external_pointer,
     151          62 :       MachineType::PointerRepresentation());
     152             : 
     153          62 :   StoreObjectField(holder, JSObject::kElementsOffset, elements);
     154          62 : }
     155             : 
     156         124 : TF_BUILTIN(TypedArrayInitializeWithBuffer, TypedArrayBuiltinsAssembler) {
     157             :   Node* holder = Parameter(Descriptor::kHolder);
     158             :   Node* length = Parameter(Descriptor::kLength);
     159             :   Node* buffer = Parameter(Descriptor::kBuffer);
     160             :   Node* element_size = Parameter(Descriptor::kElementSize);
     161             :   Node* byte_offset = Parameter(Descriptor::kByteOffset);
     162             : 
     163             :   CSA_ASSERT(this, IsJSTypedArray(holder));
     164             :   CSA_ASSERT(this, TaggedIsSmi(length));
     165             :   CSA_ASSERT(this, IsJSArrayBuffer(buffer));
     166             :   CSA_ASSERT(this, TaggedIsSmi(element_size));
     167             :   CSA_ASSERT(this, IsNumber(byte_offset));
     168             : 
     169          31 :   Node* fixed_typed_map = LoadMapForType(holder);
     170             : 
     171             :   // SmiMul returns a heap number in case of Smi overflow.
     172          31 :   Node* byte_length = SmiMul(length, element_size);
     173             :   CSA_ASSERT(this, IsNumber(byte_length));
     174             : 
     175          31 :   SetupTypedArray(holder, length, byte_offset, byte_length);
     176          31 :   AttachBuffer(holder, buffer, fixed_typed_map, length, byte_offset);
     177          62 :   Return(UndefinedConstant());
     178          31 : }
     179             : 
     180         124 : TF_BUILTIN(TypedArrayInitialize, TypedArrayBuiltinsAssembler) {
     181             :   Node* holder = Parameter(Descriptor::kHolder);
     182             :   Node* length = Parameter(Descriptor::kLength);
     183             :   Node* element_size = Parameter(Descriptor::kElementSize);
     184             :   Node* initialize = Parameter(Descriptor::kInitialize);
     185             :   Node* context = Parameter(Descriptor::kContext);
     186             : 
     187             :   CSA_ASSERT(this, IsJSTypedArray(holder));
     188             :   CSA_ASSERT(this, TaggedIsPositiveSmi(length));
     189             :   CSA_ASSERT(this, TaggedIsPositiveSmi(element_size));
     190             :   CSA_ASSERT(this, IsBoolean(initialize));
     191             : 
     192          62 :   Node* byte_offset = SmiConstant(0);
     193             : 
     194             :   static const int32_t fta_base_data_offset =
     195             :       FixedTypedArrayBase::kDataOffset - kHeapObjectTag;
     196             : 
     197          31 :   Label setup_holder(this), allocate_on_heap(this), aligned(this),
     198          31 :       allocate_elements(this), allocate_off_heap(this),
     199          31 :       allocate_off_heap_no_init(this), attach_buffer(this), done(this);
     200          62 :   VARIABLE(var_total_size, MachineType::PointerRepresentation());
     201             : 
     202             :   // SmiMul returns a heap number in case of Smi overflow.
     203          31 :   Node* byte_length = SmiMul(length, element_size);
     204             :   CSA_ASSERT(this, IsNumber(byte_length));
     205             : 
     206          31 :   SetupTypedArray(holder, length, byte_offset, byte_length);
     207             : 
     208          31 :   Node* fixed_typed_map = LoadMapForType(holder);
     209          62 :   GotoIf(TaggedIsNotSmi(byte_length), &allocate_off_heap);
     210             :   GotoIf(
     211             :       SmiGreaterThan(byte_length, SmiConstant(V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP)),
     212          93 :       &allocate_off_heap);
     213          31 :   Goto(&allocate_on_heap);
     214             : 
     215          31 :   BIND(&allocate_on_heap);
     216             :   {
     217             :     CSA_ASSERT(this, TaggedIsPositiveSmi(byte_length));
     218             :     // Allocate a new ArrayBuffer and initialize it with empty properties and
     219             :     // elements.
     220          62 :     Node* native_context = LoadNativeContext(context);
     221             :     Node* map =
     222          62 :         LoadContextElement(native_context, Context::ARRAY_BUFFER_MAP_INDEX);
     223          62 :     Node* empty_fixed_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex);
     224             : 
     225          31 :     Node* buffer = Allocate(JSArrayBuffer::kSizeWithEmbedderFields);
     226          31 :     StoreMapNoWriteBarrier(buffer, map);
     227             :     StoreObjectFieldNoWriteBarrier(buffer, JSArray::kPropertiesOrHashOffset,
     228          31 :                                    empty_fixed_array);
     229             :     StoreObjectFieldNoWriteBarrier(buffer, JSArray::kElementsOffset,
     230          31 :                                    empty_fixed_array);
     231             :     // Setup the ArrayBuffer.
     232             :     //  - Set BitField to 0.
     233             :     //  - Set IsExternal and IsNeuterable bits of BitFieldSlot.
     234             :     //  - Set the byte_length field to byte_length.
     235             :     //  - Set backing_store to null/Smi(0).
     236             :     //  - Set all embedder fields to Smi(0).
     237             :     StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldSlot,
     238          62 :                                    SmiConstant(0));
     239             :     int32_t bitfield_value = (1 << JSArrayBuffer::IsExternal::kShift) |
     240             :                              (1 << JSArrayBuffer::IsNeuterable::kShift);
     241             :     StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldOffset,
     242             :                                    Int32Constant(bitfield_value),
     243          62 :                                    MachineRepresentation::kWord32);
     244             : 
     245             :     StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kByteLengthOffset,
     246          31 :                                    byte_length);
     247             :     StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBackingStoreOffset,
     248          62 :                                    SmiConstant(0));
     249          93 :     for (int i = 0; i < v8::ArrayBuffer::kEmbedderFieldCount; i++) {
     250          62 :       int offset = JSArrayBuffer::kSize + i * kPointerSize;
     251         124 :       StoreObjectFieldNoWriteBarrier(buffer, offset, SmiConstant(0));
     252             :     }
     253             : 
     254          31 :     StoreObjectField(holder, JSArrayBufferView::kBufferOffset, buffer);
     255             : 
     256             :     // Check the alignment.
     257             :     GotoIf(SmiEqual(SmiMod(element_size, SmiConstant(kObjectAlignment)),
     258             :                     SmiConstant(0)),
     259         124 :            &aligned);
     260             : 
     261             :     // Fix alignment if needed.
     262             :     DCHECK_EQ(0, FixedTypedArrayBase::kHeaderSize & kObjectAlignmentMask);
     263             :     Node* aligned_header_size =
     264          62 :         IntPtrConstant(FixedTypedArrayBase::kHeaderSize + kObjectAlignmentMask);
     265          62 :     Node* size = IntPtrAdd(SmiToWord(byte_length), aligned_header_size);
     266          93 :     var_total_size.Bind(WordAnd(size, IntPtrConstant(~kObjectAlignmentMask)));
     267          31 :     Goto(&allocate_elements);
     268             :   }
     269             : 
     270          31 :   BIND(&aligned);
     271             :   {
     272          62 :     Node* header_size = IntPtrConstant(FixedTypedArrayBase::kHeaderSize);
     273          62 :     var_total_size.Bind(IntPtrAdd(SmiToWord(byte_length), header_size));
     274          31 :     Goto(&allocate_elements);
     275             :   }
     276             : 
     277          31 :   BIND(&allocate_elements);
     278             :   {
     279             :     // Allocate a FixedTypedArray and set the length, base pointer and external
     280             :     // pointer.
     281             :     CSA_ASSERT(this, IsRegularHeapObjectSize(var_total_size.value()));
     282             : 
     283             :     Node* elements;
     284             : 
     285          62 :     if (UnalignedLoadSupported(MachineRepresentation::kFloat64) &&
     286          31 :         UnalignedStoreSupported(MachineRepresentation::kFloat64)) {
     287          31 :       elements = AllocateInNewSpace(var_total_size.value());
     288             :     } else {
     289           0 :       elements = AllocateInNewSpace(var_total_size.value(), kDoubleAlignment);
     290             :     }
     291             : 
     292          31 :     StoreMapNoWriteBarrier(elements, fixed_typed_map);
     293          31 :     StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
     294             :     StoreObjectFieldNoWriteBarrier(
     295          31 :         elements, FixedTypedArrayBase::kBasePointerOffset, elements);
     296             :     StoreObjectFieldNoWriteBarrier(elements,
     297             :                                    FixedTypedArrayBase::kExternalPointerOffset,
     298             :                                    IntPtrConstant(fta_base_data_offset),
     299          62 :                                    MachineType::PointerRepresentation());
     300             : 
     301          31 :     StoreObjectField(holder, JSObject::kElementsOffset, elements);
     302             : 
     303          62 :     GotoIf(IsFalse(initialize), &done);
     304             :     // Initialize the backing store by filling it with 0s.
     305             :     Node* backing_store = IntPtrAdd(BitcastTaggedToWord(elements),
     306          93 :                                     IntPtrConstant(fta_base_data_offset));
     307             :     // Call out to memset to perform initialization.
     308             :     Node* memset =
     309          62 :         ExternalConstant(ExternalReference::libc_memset_function(isolate()));
     310             :     CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
     311             :                    MachineType::IntPtr(), MachineType::UintPtr(), memset,
     312          62 :                    backing_store, IntPtrConstant(0), SmiToWord(byte_length));
     313          31 :     Goto(&done);
     314             :   }
     315             : 
     316          62 :   VARIABLE(var_buffer, MachineRepresentation::kTagged);
     317             : 
     318          31 :   BIND(&allocate_off_heap);
     319             :   {
     320          62 :     GotoIf(IsFalse(initialize), &allocate_off_heap_no_init);
     321             : 
     322             :     Node* buffer_constructor = LoadContextElement(
     323          93 :         LoadNativeContext(context), Context::ARRAY_BUFFER_FUN_INDEX);
     324             :     var_buffer.Bind(ConstructJS(CodeFactory::Construct(isolate()), context,
     325          62 :                                 buffer_constructor, byte_length));
     326          31 :     Goto(&attach_buffer);
     327             :   }
     328             : 
     329          31 :   BIND(&allocate_off_heap_no_init);
     330             :   {
     331             :     Node* buffer_constructor_noinit = LoadContextElement(
     332          93 :         LoadNativeContext(context), Context::ARRAY_BUFFER_NOINIT_FUN_INDEX);
     333             :     var_buffer.Bind(CallJS(CodeFactory::Call(isolate()), context,
     334             :                            buffer_constructor_noinit, UndefinedConstant(),
     335          93 :                            byte_length));
     336          31 :     Goto(&attach_buffer);
     337             :   }
     338             : 
     339          31 :   BIND(&attach_buffer);
     340             :   {
     341             :     AttachBuffer(holder, var_buffer.value(), fixed_typed_map, length,
     342          31 :                  byte_offset);
     343          31 :     Goto(&done);
     344             :   }
     345             : 
     346          31 :   BIND(&done);
     347          93 :   Return(UndefinedConstant());
     348          31 : }
     349             : 
     350             : // ES6 #sec-typedarray-length
     351         124 : TF_BUILTIN(TypedArrayConstructByLength, TypedArrayBuiltinsAssembler) {
     352             :   Node* holder = Parameter(Descriptor::kHolder);
     353             :   Node* length = Parameter(Descriptor::kLength);
     354             :   Node* element_size = Parameter(Descriptor::kElementSize);
     355             :   Node* context = Parameter(Descriptor::kContext);
     356             : 
     357             :   CSA_ASSERT(this, IsJSTypedArray(holder));
     358             :   CSA_ASSERT(this, TaggedIsPositiveSmi(element_size));
     359             : 
     360          62 :   Node* initialize = BooleanConstant(true);
     361             : 
     362             :   Label invalid_length(this);
     363             : 
     364          62 :   length = ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero);
     365             :   // The maximum length of a TypedArray is MaxSmi().
     366             :   // Note: this is not per spec, but rather a constraint of our current
     367             :   // representation (which uses smi's).
     368          62 :   GotoIf(TaggedIsNotSmi(length), &invalid_length);
     369          93 :   GotoIf(SmiLessThan(length, SmiConstant(0)), &invalid_length);
     370             : 
     371             :   CallBuiltin(Builtins::kTypedArrayInitialize, context, holder, length,
     372          31 :               element_size, initialize);
     373          62 :   Return(UndefinedConstant());
     374             : 
     375          31 :   BIND(&invalid_length);
     376             :   {
     377             :     CallRuntime(Runtime::kThrowRangeError, context,
     378          31 :                 SmiConstant(MessageTemplate::kInvalidTypedArrayLength), length);
     379          31 :     Unreachable();
     380          31 :   }
     381          31 : }
     382             : 
     383             : // ES6 #sec-typedarray-buffer-byteoffset-length
     384         124 : TF_BUILTIN(TypedArrayConstructByArrayBuffer, TypedArrayBuiltinsAssembler) {
     385             :   Node* holder = Parameter(Descriptor::kHolder);
     386             :   Node* buffer = Parameter(Descriptor::kBuffer);
     387             :   Node* byte_offset = Parameter(Descriptor::kByteOffset);
     388             :   Node* length = Parameter(Descriptor::kLength);
     389             :   Node* element_size = Parameter(Descriptor::kElementSize);
     390             :   Node* context = Parameter(Descriptor::kContext);
     391             : 
     392             :   CSA_ASSERT(this, IsJSTypedArray(holder));
     393             :   CSA_ASSERT(this, IsJSArrayBuffer(buffer));
     394             :   CSA_ASSERT(this, TaggedIsPositiveSmi(element_size));
     395             : 
     396          62 :   VARIABLE(new_byte_length, MachineRepresentation::kTagged, SmiConstant(0));
     397          93 :   VARIABLE(offset, MachineRepresentation::kTagged, SmiConstant(0));
     398             : 
     399          31 :   Label start_offset_error(this, Label::kDeferred),
     400          31 :       byte_length_error(this, Label::kDeferred),
     401          31 :       invalid_offset_error(this, Label::kDeferred);
     402          31 :   Label offset_is_smi(this), offset_not_smi(this, Label::kDeferred),
     403          31 :       check_length(this), call_init(this), invalid_length(this),
     404          31 :       length_undefined(this), length_defined(this);
     405             : 
     406          62 :   GotoIf(IsUndefined(byte_offset), &check_length);
     407             : 
     408             :   offset.Bind(
     409          62 :       ToInteger(context, byte_offset, CodeStubAssembler::kTruncateMinusZero));
     410          93 :   Branch(TaggedIsSmi(offset.value()), &offset_is_smi, &offset_not_smi);
     411             : 
     412             :   // Check that the offset is a multiple of the element size.
     413          31 :   BIND(&offset_is_smi);
     414             :   {
     415          93 :     GotoIf(SmiEqual(offset.value(), SmiConstant(0)), &check_length);
     416          93 :     GotoIf(SmiLessThan(offset.value(), SmiConstant(0)), &invalid_length);
     417          31 :     Node* remainder = SmiMod(offset.value(), element_size);
     418             :     Branch(SmiEqual(remainder, SmiConstant(0)), &check_length,
     419          93 :            &start_offset_error);
     420             :   }
     421          31 :   BIND(&offset_not_smi);
     422             :   {
     423             :     GotoIf(IsTrue(CallBuiltin(Builtins::kLessThan, context, offset.value(),
     424          62 :                               SmiConstant(0))),
     425          62 :            &invalid_length);
     426             :     Node* remainder =
     427          31 :         CallBuiltin(Builtins::kModulus, context, offset.value(), element_size);
     428             :     // Remainder can be a heap number.
     429             :     Branch(IsTrue(CallBuiltin(Builtins::kEqual, context, remainder,
     430          62 :                               SmiConstant(0))),
     431          62 :            &check_length, &start_offset_error);
     432             :   }
     433             : 
     434          31 :   BIND(&check_length);
     435             :   // TODO(petermarshall): Throw on detached typedArray.
     436          62 :   Branch(IsUndefined(length), &length_undefined, &length_defined);
     437             : 
     438          31 :   BIND(&length_undefined);
     439             :   {
     440             :     Node* buffer_byte_length =
     441             :         LoadObjectField(buffer, JSArrayBuffer::kByteLengthOffset);
     442             : 
     443             :     Node* remainder = CallBuiltin(Builtins::kModulus, context,
     444          31 :                                   buffer_byte_length, element_size);
     445             :     // Remainder can be a heap number.
     446             :     GotoIf(IsFalse(CallBuiltin(Builtins::kEqual, context, remainder,
     447          62 :                                SmiConstant(0))),
     448          62 :            &byte_length_error);
     449             : 
     450             :     new_byte_length.Bind(CallBuiltin(Builtins::kSubtract, context,
     451          31 :                                      buffer_byte_length, offset.value()));
     452             : 
     453             :     Branch(IsTrue(CallBuiltin(Builtins::kLessThan, context,
     454          62 :                               new_byte_length.value(), SmiConstant(0))),
     455          62 :            &invalid_offset_error, &call_init);
     456             :   }
     457             : 
     458          31 :   BIND(&length_defined);
     459             :   {
     460          31 :     Node* new_length = ToSmiIndex(length, context, &invalid_length);
     461          31 :     new_byte_length.Bind(SmiMul(new_length, element_size));
     462             :     // Reading the byte length must come after the ToIndex operation, which
     463             :     // could cause the buffer to become detached.
     464             :     Node* buffer_byte_length =
     465             :         LoadObjectField(buffer, JSArrayBuffer::kByteLengthOffset);
     466             : 
     467             :     Node* end = CallBuiltin(Builtins::kAdd, context, offset.value(),
     468          31 :                             new_byte_length.value());
     469             : 
     470             :     Branch(IsTrue(CallBuiltin(Builtins::kGreaterThan, context, end,
     471          62 :                               buffer_byte_length)),
     472          62 :            &invalid_length, &call_init);
     473             :   }
     474             : 
     475          31 :   BIND(&call_init);
     476             :   {
     477             :     Node* new_length = CallBuiltin(Builtins::kDivide, context,
     478          31 :                                    new_byte_length.value(), element_size);
     479             :     // Force the result into a Smi, or throw a range error if it doesn't fit.
     480          31 :     new_length = ToSmiIndex(new_length, context, &invalid_length);
     481             : 
     482             :     CallBuiltin(Builtins::kTypedArrayInitializeWithBuffer, context, holder,
     483          31 :                 new_length, buffer, element_size, offset.value());
     484          62 :     Return(UndefinedConstant());
     485             :   }
     486             : 
     487          31 :   BIND(&invalid_offset_error);
     488             :   {
     489             :     CallRuntime(Runtime::kThrowRangeError, context,
     490          31 :                 SmiConstant(MessageTemplate::kInvalidOffset), byte_offset);
     491          31 :     Unreachable();
     492             :   }
     493             : 
     494          31 :   BIND(&start_offset_error);
     495             :   {
     496          62 :     Node* holder_map = LoadMap(holder);
     497          62 :     Node* problem_string = StringConstant("start offset");
     498             :     CallRuntime(Runtime::kThrowInvalidTypedArrayAlignment, context, holder_map,
     499             :                 problem_string);
     500             : 
     501          31 :     Unreachable();
     502             :   }
     503             : 
     504          31 :   BIND(&byte_length_error);
     505             :   {
     506          62 :     Node* holder_map = LoadMap(holder);
     507          62 :     Node* problem_string = StringConstant("byte length");
     508             :     CallRuntime(Runtime::kThrowInvalidTypedArrayAlignment, context, holder_map,
     509             :                 problem_string);
     510             : 
     511          31 :     Unreachable();
     512             :   }
     513             : 
     514          31 :   BIND(&invalid_length);
     515             :   {
     516             :     CallRuntime(Runtime::kThrowRangeError, context,
     517          31 :                 SmiConstant(MessageTemplate::kInvalidTypedArrayLength), length);
     518          31 :     Unreachable();
     519          31 :   }
     520          31 : }
     521             : 
     522          62 : compiler::Node* TypedArrayBuiltinsAssembler::LoadDataPtr(Node* typed_array) {
     523             :   CSA_ASSERT(this, IsJSTypedArray(typed_array));
     524         124 :   Node* elements = LoadElements(typed_array);
     525             :   CSA_ASSERT(this, IsFixedTypedArray(elements));
     526             :   Node* base_pointer = BitcastTaggedToWord(
     527         124 :       LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset));
     528             :   Node* external_pointer = BitcastTaggedToWord(
     529         124 :       LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset));
     530         124 :   return IntPtrAdd(base_pointer, external_pointer);
     531             : }
     532             : 
     533           0 : compiler::Node* TypedArrayBuiltinsAssembler::ByteLengthIsValid(
     534             :     Node* byte_length) {
     535           0 :   Label smi(this), done(this);
     536           0 :   VARIABLE(is_valid, MachineRepresentation::kWord32);
     537           0 :   GotoIf(TaggedIsSmi(byte_length), &smi);
     538             : 
     539             :   CSA_ASSERT(this, IsHeapNumber(byte_length));
     540           0 :   Node* float_value = LoadHeapNumberValue(byte_length);
     541             :   Node* max_byte_length_double =
     542           0 :       Float64Constant(FixedTypedArrayBase::kMaxByteLength);
     543           0 :   is_valid.Bind(Float64LessThanOrEqual(float_value, max_byte_length_double));
     544           0 :   Goto(&done);
     545             : 
     546           0 :   BIND(&smi);
     547           0 :   Node* max_byte_length = IntPtrConstant(FixedTypedArrayBase::kMaxByteLength);
     548           0 :   is_valid.Bind(UintPtrLessThanOrEqual(SmiUntag(byte_length), max_byte_length));
     549           0 :   Goto(&done);
     550             : 
     551           0 :   BIND(&done);
     552           0 :   return is_valid.value();
     553             : }
     554             : 
     555         124 : TF_BUILTIN(TypedArrayConstructByArrayLike, TypedArrayBuiltinsAssembler) {
     556             :   Node* holder = Parameter(Descriptor::kHolder);
     557             :   Node* array_like = Parameter(Descriptor::kArrayLike);
     558             :   Node* initial_length = Parameter(Descriptor::kLength);
     559             :   Node* element_size = Parameter(Descriptor::kElementSize);
     560             :   CSA_ASSERT(this, TaggedIsSmi(element_size));
     561             :   Node* context = Parameter(Descriptor::kContext);
     562             : 
     563          62 :   Node* initialize = BooleanConstant(false);
     564             : 
     565          31 :   Label invalid_length(this), fill(this), fast_copy(this);
     566             : 
     567             :   // The caller has looked up length on array_like, which is observable.
     568          31 :   Node* length = ToSmiLength(initial_length, context, &invalid_length);
     569             : 
     570             :   CallBuiltin(Builtins::kTypedArrayInitialize, context, holder, length,
     571          31 :               element_size, initialize);
     572          93 :   GotoIf(SmiNotEqual(length, SmiConstant(0)), &fill);
     573          62 :   Return(UndefinedConstant());
     574             : 
     575          31 :   BIND(&fill);
     576          93 :   Node* holder_kind = LoadMapElementsKind(LoadMap(holder));
     577          93 :   Node* source_kind = LoadMapElementsKind(LoadMap(array_like));
     578          62 :   GotoIf(Word32Equal(holder_kind, source_kind), &fast_copy);
     579             : 
     580             :   // Copy using the elements accessor.
     581             :   CallRuntime(Runtime::kTypedArrayCopyElements, context, holder, array_like,
     582             :               length);
     583          62 :   Return(UndefinedConstant());
     584             : 
     585          31 :   BIND(&fast_copy);
     586             :   {
     587          31 :     Node* holder_data_ptr = LoadDataPtr(holder);
     588          31 :     Node* source_data_ptr = LoadDataPtr(array_like);
     589             : 
     590             :     // Calculate the byte length. We shouldn't be trying to copy if the typed
     591             :     // array was neutered.
     592             :     CSA_ASSERT(this, SmiNotEqual(length, SmiConstant(0)));
     593             :     CSA_ASSERT(this, Word32Equal(IsDetachedBuffer(LoadObjectField(
     594             :                                      array_like, JSTypedArray::kBufferOffset)),
     595             :                                  Int32Constant(0)));
     596             : 
     597          31 :     Node* byte_length = SmiMul(length, element_size);
     598             :     CSA_ASSERT(this, ByteLengthIsValid(byte_length));
     599          62 :     Node* byte_length_intptr = ChangeNonnegativeNumberToUintPtr(byte_length);
     600             :     CSA_ASSERT(this, UintPtrLessThanOrEqual(
     601             :                          byte_length_intptr,
     602             :                          IntPtrConstant(FixedTypedArrayBase::kMaxByteLength)));
     603             : 
     604             :     Node* memcpy =
     605          62 :         ExternalConstant(ExternalReference::libc_memcpy_function(isolate()));
     606             :     CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
     607             :                    MachineType::Pointer(), MachineType::UintPtr(), memcpy,
     608          31 :                    holder_data_ptr, source_data_ptr, byte_length_intptr);
     609          62 :     Return(UndefinedConstant());
     610             :   }
     611             : 
     612          31 :   BIND(&invalid_length);
     613             :   {
     614             :     CallRuntime(Runtime::kThrowRangeError, context,
     615             :                 SmiConstant(MessageTemplate::kInvalidTypedArrayLength),
     616          31 :                 initial_length);
     617          31 :     Unreachable();
     618          31 :   }
     619          31 : }
     620             : 
     621          93 : void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeGetter(
     622             :     Node* context, Node* receiver, const char* method_name, int object_offset) {
     623             :   // Check if the {receiver} is actually a JSTypedArray.
     624          93 :   Label receiver_is_incompatible(this, Label::kDeferred);
     625         186 :   GotoIf(TaggedIsSmi(receiver), &receiver_is_incompatible);
     626             :   GotoIfNot(HasInstanceType(receiver, JS_TYPED_ARRAY_TYPE),
     627         186 :             &receiver_is_incompatible);
     628             : 
     629             :   // Check if the {receiver}'s JSArrayBuffer was neutered.
     630             :   Node* receiver_buffer =
     631             :       LoadObjectField(receiver, JSTypedArray::kBufferOffset);
     632          93 :   Label if_receiverisneutered(this, Label::kDeferred);
     633         186 :   GotoIf(IsDetachedBuffer(receiver_buffer), &if_receiverisneutered);
     634          93 :   Return(LoadObjectField(receiver, object_offset));
     635             : 
     636          93 :   BIND(&if_receiverisneutered);
     637             :   {
     638             :     // The {receiver}s buffer was neutered, default to zero.
     639         186 :     Return(SmiConstant(0));
     640             :   }
     641             : 
     642          93 :   BIND(&receiver_is_incompatible);
     643             :   {
     644             :     // The {receiver} is not a valid JSTypedArray.
     645             :     CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context,
     646          93 :                 StringConstant(method_name), receiver);
     647          93 :     Unreachable();
     648          93 :   }
     649          93 : }
     650             : 
     651             : // ES6 #sec-get-%typedarray%.prototype.bytelength
     652         124 : TF_BUILTIN(TypedArrayPrototypeByteLength, TypedArrayBuiltinsAssembler) {
     653             :   Node* context = Parameter(Descriptor::kContext);
     654             :   Node* receiver = Parameter(Descriptor::kReceiver);
     655             :   GenerateTypedArrayPrototypeGetter(context, receiver,
     656             :                                     "get TypedArray.prototype.byteLength",
     657          31 :                                     JSTypedArray::kByteLengthOffset);
     658          31 : }
     659             : 
     660             : // ES6 #sec-get-%typedarray%.prototype.byteoffset
     661         124 : TF_BUILTIN(TypedArrayPrototypeByteOffset, TypedArrayBuiltinsAssembler) {
     662             :   Node* context = Parameter(Descriptor::kContext);
     663             :   Node* receiver = Parameter(Descriptor::kReceiver);
     664             :   GenerateTypedArrayPrototypeGetter(context, receiver,
     665             :                                     "get TypedArray.prototype.byteOffset",
     666          31 :                                     JSTypedArray::kByteOffsetOffset);
     667          31 : }
     668             : 
     669             : // ES6 #sec-get-%typedarray%.prototype.length
     670         124 : TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) {
     671             :   Node* context = Parameter(Descriptor::kContext);
     672             :   Node* receiver = Parameter(Descriptor::kReceiver);
     673             :   GenerateTypedArrayPrototypeGetter(context, receiver,
     674             :                                     "get TypedArray.prototype.length",
     675          31 :                                     JSTypedArray::kLengthOffset);
     676          31 : }
     677             : 
     678             : // ES #sec-get-%typedarray%.prototype-@@tostringtag
     679         124 : TF_BUILTIN(TypedArrayPrototypeToStringTag, TypedArrayBuiltinsAssembler) {
     680             :   Node* receiver = Parameter(Descriptor::kReceiver);
     681          31 :   Label if_receiverisheapobject(this), return_undefined(this);
     682          62 :   Branch(TaggedIsSmi(receiver), &return_undefined, &if_receiverisheapobject);
     683             : 
     684             :   // Dispatch on the elements kind, offset by
     685             :   // FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND.
     686             :   size_t const kTypedElementsKindCount = LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
     687             :                                          FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND +
     688             :                                          1;
     689             : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
     690             :   Label return_##type##array(this);                     \
     691             :   BIND(&return_##type##array);                          \
     692             :   Return(StringConstant(#Type "Array"));
     693         589 :   TYPED_ARRAYS(TYPED_ARRAY_CASE)
     694             : #undef TYPED_ARRAY_CASE
     695             :   Label* elements_kind_labels[kTypedElementsKindCount] = {
     696             : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) &return_##type##array,
     697             :       TYPED_ARRAYS(TYPED_ARRAY_CASE)
     698             : #undef TYPED_ARRAY_CASE
     699          31 :   };
     700             :   int32_t elements_kinds[kTypedElementsKindCount] = {
     701             : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
     702             :   TYPE##_ELEMENTS - FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND,
     703             :       TYPED_ARRAYS(TYPED_ARRAY_CASE)
     704             : #undef TYPED_ARRAY_CASE
     705          31 :   };
     706             : 
     707             :   // We offset the dispatch by FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND, so
     708             :   // that this can be turned into a non-sparse table switch for ideal
     709             :   // performance.
     710          31 :   BIND(&if_receiverisheapobject);
     711             :   Node* elements_kind =
     712          62 :       Int32Sub(LoadMapElementsKind(LoadMap(receiver)),
     713         155 :                Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND));
     714             :   Switch(elements_kind, &return_undefined, elements_kinds, elements_kind_labels,
     715          31 :          kTypedElementsKindCount);
     716             : 
     717          31 :   BIND(&return_undefined);
     718          93 :   Return(UndefinedConstant());
     719          31 : }
     720             : 
     721          93 : void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeIterationMethod(
     722             :     Node* context, Node* receiver, const char* method_name,
     723             :     IterationKind iteration_kind) {
     724          93 :   Label throw_bad_receiver(this, Label::kDeferred);
     725          93 :   Label throw_typeerror(this, Label::kDeferred);
     726             : 
     727         186 :   GotoIf(TaggedIsSmi(receiver), &throw_bad_receiver);
     728             : 
     729         186 :   Node* map = LoadMap(receiver);
     730         186 :   Node* instance_type = LoadMapInstanceType(map);
     731             :   GotoIfNot(InstanceTypeEqual(instance_type, JS_TYPED_ARRAY_TYPE),
     732         186 :             &throw_bad_receiver);
     733             : 
     734             :   // Check if the {receiver}'s JSArrayBuffer was neutered.
     735             :   Node* receiver_buffer =
     736             :       LoadObjectField(receiver, JSTypedArray::kBufferOffset);
     737          93 :   Label if_receiverisneutered(this, Label::kDeferred);
     738         186 :   GotoIf(IsDetachedBuffer(receiver_buffer), &if_receiverisneutered);
     739             : 
     740             :   Return(CreateArrayIterator(receiver, map, instance_type, context,
     741         186 :                              iteration_kind));
     742             : 
     743         186 :   VARIABLE(var_message, MachineRepresentation::kTagged);
     744          93 :   BIND(&throw_bad_receiver);
     745         186 :   var_message.Bind(SmiConstant(MessageTemplate::kNotTypedArray));
     746          93 :   Goto(&throw_typeerror);
     747             : 
     748          93 :   BIND(&if_receiverisneutered);
     749         186 :   var_message.Bind(SmiConstant(MessageTemplate::kDetachedOperation));
     750          93 :   Goto(&throw_typeerror);
     751             : 
     752          93 :   BIND(&throw_typeerror);
     753             :   {
     754         186 :     Node* method_arg = StringConstant(method_name);
     755             :     Node* result = CallRuntime(Runtime::kThrowTypeError, context,
     756          93 :                                var_message.value(), method_arg);
     757          93 :     Return(result);
     758          93 :   }
     759          93 : }
     760             : 
     761             : // ES6 #sec-%typedarray%.prototype.values
     762         124 : TF_BUILTIN(TypedArrayPrototypeValues, TypedArrayBuiltinsAssembler) {
     763             :   Node* context = Parameter(Descriptor::kContext);
     764             :   Node* receiver = Parameter(Descriptor::kReceiver);
     765             :   GenerateTypedArrayPrototypeIterationMethod(context, receiver,
     766             :                                              "%TypedArray%.prototype.values()",
     767          31 :                                              IterationKind::kValues);
     768          31 : }
     769             : 
     770             : // ES6 #sec-%typedarray%.prototype.entries
     771         124 : TF_BUILTIN(TypedArrayPrototypeEntries, TypedArrayBuiltinsAssembler) {
     772             :   Node* context = Parameter(Descriptor::kContext);
     773             :   Node* receiver = Parameter(Descriptor::kReceiver);
     774             :   GenerateTypedArrayPrototypeIterationMethod(context, receiver,
     775             :                                              "%TypedArray%.prototype.entries()",
     776          31 :                                              IterationKind::kEntries);
     777          31 : }
     778             : 
     779             : // ES6 #sec-%typedarray%.prototype.keys
     780         124 : TF_BUILTIN(TypedArrayPrototypeKeys, TypedArrayBuiltinsAssembler) {
     781             :   Node* context = Parameter(Descriptor::kContext);
     782             :   Node* receiver = Parameter(Descriptor::kReceiver);
     783             :   GenerateTypedArrayPrototypeIterationMethod(
     784          31 :       context, receiver, "%TypedArray%.prototype.keys()", IterationKind::kKeys);
     785          31 : }
     786             : 
     787             : #undef V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP
     788             : 
     789             : }  // namespace internal
     790             : }  // namespace v8

Generated by: LCOV version 1.10