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
|