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/objects.h"
9
10 namespace v8 {
11 namespace internal {
12
13 using compiler::Node;
14
15 class SharedArrayBufferBuiltinsAssembler : public CodeStubAssembler {
16 public:
SharedArrayBufferBuiltinsAssembler(compiler::CodeAssemblerState * state)17 explicit SharedArrayBufferBuiltinsAssembler(
18 compiler::CodeAssemblerState* state)
19 : CodeStubAssembler(state) {}
20
21 protected:
22 typedef Node* (CodeAssembler::*AssemblerFunction)(MachineType type,
23 Node* base, Node* offset,
24 Node* value);
25 void ValidateSharedTypedArray(Node* tagged, Node* context,
26 Node** out_instance_type,
27 Node** out_backing_store);
28 Node* ConvertTaggedAtomicIndexToWord32(Node* tagged, Node* context,
29 Node** number_index);
30 void ValidateAtomicIndex(Node* array, Node* index_word, Node* context);
31 #if DEBUG
32 void DebugSanityCheckAtomicIndex(Node* array, Node* index_word,
33 Node* context);
34 #endif
35 void AtomicBinopBuiltinCommon(Node* array, Node* index, Node* value,
36 Node* context, AssemblerFunction function,
37 Runtime::FunctionId runtime_function);
38 };
39
ValidateSharedTypedArray(Node * tagged,Node * context,Node ** out_instance_type,Node ** out_backing_store)40 void SharedArrayBufferBuiltinsAssembler::ValidateSharedTypedArray(
41 Node* tagged, Node* context, Node** out_instance_type,
42 Node** out_backing_store) {
43 Label not_float_or_clamped(this), invalid(this);
44
45 // Fail if it is not a heap object.
46 GotoIf(TaggedIsSmi(tagged), &invalid);
47
48 // Fail if the array's instance type is not JSTypedArray.
49 GotoIfNot(InstanceTypeEqual(LoadInstanceType(tagged), JS_TYPED_ARRAY_TYPE),
50 &invalid);
51
52 // Fail if the array's JSArrayBuffer is not shared.
53 Node* array_buffer = LoadObjectField(tagged, JSTypedArray::kBufferOffset);
54 Node* bitfield = LoadObjectField(array_buffer, JSArrayBuffer::kBitFieldOffset,
55 MachineType::Uint32());
56 GotoIfNot(IsSetWord32<JSArrayBuffer::IsShared>(bitfield), &invalid);
57
58 // Fail if the array's element type is float32, float64 or clamped.
59 Node* elements_instance_type = LoadInstanceType(LoadElements(tagged));
60 STATIC_ASSERT(FIXED_INT8_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
61 STATIC_ASSERT(FIXED_INT16_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
62 STATIC_ASSERT(FIXED_INT32_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
63 STATIC_ASSERT(FIXED_UINT8_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
64 STATIC_ASSERT(FIXED_UINT16_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
65 STATIC_ASSERT(FIXED_UINT32_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
66 Branch(Int32LessThan(elements_instance_type,
67 Int32Constant(FIXED_FLOAT32_ARRAY_TYPE)),
68 ¬_float_or_clamped, &invalid);
69
70 BIND(&invalid);
71 {
72 ThrowTypeError(context, MessageTemplate::kNotIntegerSharedTypedArray,
73 tagged);
74 }
75
76 BIND(¬_float_or_clamped);
77 *out_instance_type = elements_instance_type;
78
79 Node* backing_store =
80 LoadObjectField(array_buffer, JSArrayBuffer::kBackingStoreOffset);
81 Node* byte_offset = ChangeUint32ToWord(TruncateTaggedToWord32(
82 context, LoadObjectField(tagged, JSArrayBufferView::kByteOffsetOffset)));
83 *out_backing_store =
84 IntPtrAdd(BitcastTaggedToWord(backing_store), byte_offset);
85 }
86
87 // https://tc39.github.io/ecmascript_sharedmem/shmem.html#Atomics.ValidateAtomicAccess
ConvertTaggedAtomicIndexToWord32(Node * tagged,Node * context,Node ** number_index)88 Node* SharedArrayBufferBuiltinsAssembler::ConvertTaggedAtomicIndexToWord32(
89 Node* tagged, Node* context, Node** number_index) {
90 VARIABLE(var_result, MachineRepresentation::kWord32);
91 Label done(this), range_error(this);
92
93 // Returns word32 since index cannot be longer than a TypedArray length,
94 // which has a uint32 maximum.
95 // The |number_index| output parameter is used only for architectures that
96 // don't currently have a TF implementation and forward to runtime functions
97 // instead; they expect the value has already been coerced to an integer.
98 *number_index = ToSmiIndex(CAST(tagged), CAST(context), &range_error);
99 var_result.Bind(SmiToInt32(*number_index));
100 Goto(&done);
101
102 BIND(&range_error);
103 { ThrowRangeError(context, MessageTemplate::kInvalidAtomicAccessIndex); }
104
105 BIND(&done);
106 return var_result.value();
107 }
108
ValidateAtomicIndex(Node * array,Node * index_word,Node * context)109 void SharedArrayBufferBuiltinsAssembler::ValidateAtomicIndex(Node* array,
110 Node* index_word,
111 Node* context) {
112 // Check if the index is in bounds. If not, throw RangeError.
113 Label check_passed(this);
114 Node* array_length_word32 = TruncateTaggedToWord32(
115 context, LoadObjectField(array, JSTypedArray::kLengthOffset));
116 GotoIf(Uint32LessThan(index_word, array_length_word32), &check_passed);
117
118 ThrowRangeError(context, MessageTemplate::kInvalidAtomicAccessIndex);
119
120 BIND(&check_passed);
121 }
122
123 #if DEBUG
DebugSanityCheckAtomicIndex(Node * array,Node * index_word,Node * context)124 void SharedArrayBufferBuiltinsAssembler::DebugSanityCheckAtomicIndex(
125 Node* array, Node* index_word, Node* context) {
126 // In Debug mode, we re-validate the index as a sanity check because
127 // ToInteger above calls out to JavaScript. A SharedArrayBuffer can't be
128 // neutered and the TypedArray length can't change either, so skipping this
129 // check in Release mode is safe.
130 CSA_ASSERT(
131 this,
132 Uint32LessThan(
133 index_word,
134 TruncateTaggedToWord32(
135 context, LoadObjectField(array, JSTypedArray::kLengthOffset))));
136 }
137 #endif
138
TF_BUILTIN(AtomicsLoad,SharedArrayBufferBuiltinsAssembler)139 TF_BUILTIN(AtomicsLoad, SharedArrayBufferBuiltinsAssembler) {
140 Node* array = Parameter(Descriptor::kArray);
141 Node* index = Parameter(Descriptor::kIndex);
142 Node* context = Parameter(Descriptor::kContext);
143
144 Node* instance_type;
145 Node* backing_store;
146 ValidateSharedTypedArray(array, context, &instance_type, &backing_store);
147
148 Node* index_integer;
149 Node* index_word32 =
150 ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
151 ValidateAtomicIndex(array, index_word32, context);
152 Node* index_word = ChangeUint32ToWord(index_word32);
153
154 Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
155 other(this);
156 int32_t case_values[] = {
157 FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
158 FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
159 };
160 Label* case_labels[] = {
161 &i8, &u8, &i16, &u16, &i32, &u32,
162 };
163 Switch(instance_type, &other, case_values, case_labels,
164 arraysize(case_labels));
165
166 BIND(&i8);
167 Return(
168 SmiFromInt32(AtomicLoad(MachineType::Int8(), backing_store, index_word)));
169
170 BIND(&u8);
171 Return(SmiFromInt32(
172 AtomicLoad(MachineType::Uint8(), backing_store, index_word)));
173
174 BIND(&i16);
175 Return(SmiFromInt32(
176 AtomicLoad(MachineType::Int16(), backing_store, WordShl(index_word, 1))));
177
178 BIND(&u16);
179 Return(SmiFromInt32(AtomicLoad(MachineType::Uint16(), backing_store,
180 WordShl(index_word, 1))));
181
182 BIND(&i32);
183 Return(ChangeInt32ToTagged(
184 AtomicLoad(MachineType::Int32(), backing_store, WordShl(index_word, 2))));
185
186 BIND(&u32);
187 Return(ChangeUint32ToTagged(AtomicLoad(MachineType::Uint32(), backing_store,
188 WordShl(index_word, 2))));
189
190 // This shouldn't happen, we've already validated the type.
191 BIND(&other);
192 Unreachable();
193 }
194
TF_BUILTIN(AtomicsStore,SharedArrayBufferBuiltinsAssembler)195 TF_BUILTIN(AtomicsStore, SharedArrayBufferBuiltinsAssembler) {
196 Node* array = Parameter(Descriptor::kArray);
197 Node* index = Parameter(Descriptor::kIndex);
198 Node* value = Parameter(Descriptor::kValue);
199 Node* context = Parameter(Descriptor::kContext);
200
201 Node* instance_type;
202 Node* backing_store;
203 ValidateSharedTypedArray(array, context, &instance_type, &backing_store);
204
205 Node* index_integer;
206 Node* index_word32 =
207 ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
208 ValidateAtomicIndex(array, index_word32, context);
209 Node* index_word = ChangeUint32ToWord(index_word32);
210
211 Node* value_integer = ToInteger_Inline(CAST(context), CAST(value));
212 Node* value_word32 = TruncateTaggedToWord32(context, value_integer);
213
214 #if DEBUG
215 DebugSanityCheckAtomicIndex(array, index_word32, context);
216 #endif
217
218 Label u8(this), u16(this), u32(this), other(this);
219 int32_t case_values[] = {
220 FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
221 FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
222 };
223 Label* case_labels[] = {
224 &u8, &u8, &u16, &u16, &u32, &u32,
225 };
226 Switch(instance_type, &other, case_values, case_labels,
227 arraysize(case_labels));
228
229 BIND(&u8);
230 AtomicStore(MachineRepresentation::kWord8, backing_store, index_word,
231 value_word32);
232 Return(value_integer);
233
234 BIND(&u16);
235 AtomicStore(MachineRepresentation::kWord16, backing_store,
236 WordShl(index_word, 1), value_word32);
237 Return(value_integer);
238
239 BIND(&u32);
240 AtomicStore(MachineRepresentation::kWord32, backing_store,
241 WordShl(index_word, 2), value_word32);
242 Return(value_integer);
243
244 // This shouldn't happen, we've already validated the type.
245 BIND(&other);
246 Unreachable();
247 }
248
TF_BUILTIN(AtomicsExchange,SharedArrayBufferBuiltinsAssembler)249 TF_BUILTIN(AtomicsExchange, SharedArrayBufferBuiltinsAssembler) {
250 Node* array = Parameter(Descriptor::kArray);
251 Node* index = Parameter(Descriptor::kIndex);
252 Node* value = Parameter(Descriptor::kValue);
253 Node* context = Parameter(Descriptor::kContext);
254
255 Node* instance_type;
256 Node* backing_store;
257 ValidateSharedTypedArray(array, context, &instance_type, &backing_store);
258
259 Node* index_integer;
260 Node* index_word32 =
261 ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
262 ValidateAtomicIndex(array, index_word32, context);
263
264 Node* value_integer = ToInteger_Inline(CAST(context), CAST(value));
265
266 #if DEBUG
267 DebugSanityCheckAtomicIndex(array, index_word32, context);
268 #endif
269
270 #if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
271 Return(CallRuntime(Runtime::kAtomicsExchange, context, array, index_integer,
272 value_integer));
273 #else
274 Node* index_word = ChangeUint32ToWord(index_word32);
275
276 Node* value_word32 = TruncateTaggedToWord32(context, value_integer);
277
278 Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
279 other(this);
280 int32_t case_values[] = {
281 FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
282 FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
283 };
284 Label* case_labels[] = {
285 &i8, &u8, &i16, &u16, &i32, &u32,
286 };
287 Switch(instance_type, &other, case_values, case_labels,
288 arraysize(case_labels));
289
290 BIND(&i8);
291 Return(SmiFromInt32(AtomicExchange(MachineType::Int8(), backing_store,
292 index_word, value_word32)));
293
294 BIND(&u8);
295 Return(SmiFromInt32(AtomicExchange(MachineType::Uint8(), backing_store,
296 index_word, value_word32)));
297
298 BIND(&i16);
299 Return(SmiFromInt32(AtomicExchange(MachineType::Int16(), backing_store,
300 WordShl(index_word, 1), value_word32)));
301
302 BIND(&u16);
303 Return(SmiFromInt32(AtomicExchange(MachineType::Uint16(), backing_store,
304 WordShl(index_word, 1), value_word32)));
305
306 BIND(&i32);
307 Return(ChangeInt32ToTagged(AtomicExchange(MachineType::Int32(), backing_store,
308 WordShl(index_word, 2),
309 value_word32)));
310
311 BIND(&u32);
312 Return(ChangeUint32ToTagged(
313 AtomicExchange(MachineType::Uint32(), backing_store,
314 WordShl(index_word, 2), value_word32)));
315
316 // This shouldn't happen, we've already validated the type.
317 BIND(&other);
318 Unreachable();
319 #endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
320 }
321
TF_BUILTIN(AtomicsCompareExchange,SharedArrayBufferBuiltinsAssembler)322 TF_BUILTIN(AtomicsCompareExchange, SharedArrayBufferBuiltinsAssembler) {
323 Node* array = Parameter(Descriptor::kArray);
324 Node* index = Parameter(Descriptor::kIndex);
325 Node* old_value = Parameter(Descriptor::kOldValue);
326 Node* new_value = Parameter(Descriptor::kNewValue);
327 Node* context = Parameter(Descriptor::kContext);
328
329 Node* instance_type;
330 Node* backing_store;
331 ValidateSharedTypedArray(array, context, &instance_type, &backing_store);
332
333 Node* index_integer;
334 Node* index_word32 =
335 ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
336 ValidateAtomicIndex(array, index_word32, context);
337
338 Node* old_value_integer = ToInteger_Inline(CAST(context), CAST(old_value));
339 Node* new_value_integer = ToInteger_Inline(CAST(context), CAST(new_value));
340
341 #if DEBUG
342 DebugSanityCheckAtomicIndex(array, index_word32, context);
343 #endif
344
345 #if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \
346 V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
347 Return(CallRuntime(Runtime::kAtomicsCompareExchange, context, array,
348 index_integer, old_value_integer, new_value_integer));
349 #else
350 Node* index_word = ChangeUint32ToWord(index_word32);
351
352 Node* old_value_word32 = TruncateTaggedToWord32(context, old_value_integer);
353
354 Node* new_value_word32 = TruncateTaggedToWord32(context, new_value_integer);
355
356 Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
357 other(this);
358 int32_t case_values[] = {
359 FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
360 FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
361 };
362 Label* case_labels[] = {
363 &i8, &u8, &i16, &u16, &i32, &u32,
364 };
365 Switch(instance_type, &other, case_values, case_labels,
366 arraysize(case_labels));
367
368 BIND(&i8);
369 Return(SmiFromInt32(AtomicCompareExchange(MachineType::Int8(), backing_store,
370 index_word, old_value_word32,
371 new_value_word32)));
372
373 BIND(&u8);
374 Return(SmiFromInt32(AtomicCompareExchange(MachineType::Uint8(), backing_store,
375 index_word, old_value_word32,
376 new_value_word32)));
377
378 BIND(&i16);
379 Return(SmiFromInt32(AtomicCompareExchange(
380 MachineType::Int16(), backing_store, WordShl(index_word, 1),
381 old_value_word32, new_value_word32)));
382
383 BIND(&u16);
384 Return(SmiFromInt32(AtomicCompareExchange(
385 MachineType::Uint16(), backing_store, WordShl(index_word, 1),
386 old_value_word32, new_value_word32)));
387
388 BIND(&i32);
389 Return(ChangeInt32ToTagged(AtomicCompareExchange(
390 MachineType::Int32(), backing_store, WordShl(index_word, 2),
391 old_value_word32, new_value_word32)));
392
393 BIND(&u32);
394 Return(ChangeUint32ToTagged(AtomicCompareExchange(
395 MachineType::Uint32(), backing_store, WordShl(index_word, 2),
396 old_value_word32, new_value_word32)));
397
398 // This shouldn't happen, we've already validated the type.
399 BIND(&other);
400 Unreachable();
401 #endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64
402 // || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
403 }
404
405 #define BINOP_BUILTIN(op) \
406 TF_BUILTIN(Atomics##op, SharedArrayBufferBuiltinsAssembler) { \
407 Node* array = Parameter(Descriptor::kArray); \
408 Node* index = Parameter(Descriptor::kIndex); \
409 Node* value = Parameter(Descriptor::kValue); \
410 Node* context = Parameter(Descriptor::kContext); \
411 AtomicBinopBuiltinCommon(array, index, value, context, \
412 &CodeAssembler::Atomic##op, \
413 Runtime::kAtomics##op); \
414 }
415 BINOP_BUILTIN(Add)
BINOP_BUILTIN(Sub)416 BINOP_BUILTIN(Sub)
417 BINOP_BUILTIN(And)
418 BINOP_BUILTIN(Or)
419 BINOP_BUILTIN(Xor)
420 #undef BINOP_BUILTIN
421
422 void SharedArrayBufferBuiltinsAssembler::AtomicBinopBuiltinCommon(
423 Node* array, Node* index, Node* value, Node* context,
424 AssemblerFunction function, Runtime::FunctionId runtime_function) {
425 Node* instance_type;
426 Node* backing_store;
427 ValidateSharedTypedArray(array, context, &instance_type, &backing_store);
428
429 Node* index_integer;
430 Node* index_word32 =
431 ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
432 ValidateAtomicIndex(array, index_word32, context);
433
434 Node* value_integer = ToInteger_Inline(CAST(context), CAST(value));
435
436 #if DEBUG
437 // In Debug mode, we re-validate the index as a sanity check because
438 // ToInteger above calls out to JavaScript. A SharedArrayBuffer can't be
439 // neutered and the TypedArray length can't change either, so skipping this
440 // check in Release mode is safe.
441 ValidateAtomicIndex(array, index_word32, context);
442 #endif
443
444 #if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \
445 V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
446 Return(CallRuntime(runtime_function, context, array, index_integer,
447 value_integer));
448 #else
449 Node* index_word = ChangeUint32ToWord(index_word32);
450
451 Node* value_word32 = TruncateTaggedToWord32(context, value_integer);
452
453 Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
454 other(this);
455 int32_t case_values[] = {
456 FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
457 FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
458 };
459 Label* case_labels[] = {
460 &i8, &u8, &i16, &u16, &i32, &u32,
461 };
462 Switch(instance_type, &other, case_values, case_labels,
463 arraysize(case_labels));
464
465 BIND(&i8);
466 Return(SmiFromInt32((this->*function)(MachineType::Int8(), backing_store,
467 index_word, value_word32)));
468
469 BIND(&u8);
470 Return(SmiFromInt32((this->*function)(MachineType::Uint8(), backing_store,
471 index_word, value_word32)));
472
473 BIND(&i16);
474 Return(SmiFromInt32((this->*function)(MachineType::Int16(), backing_store,
475 WordShl(index_word, 1), value_word32)));
476
477 BIND(&u16);
478 Return(SmiFromInt32((this->*function)(MachineType::Uint16(), backing_store,
479 WordShl(index_word, 1), value_word32)));
480
481 BIND(&i32);
482 Return(ChangeInt32ToTagged(
483 (this->*function)(MachineType::Int32(), backing_store,
484 WordShl(index_word, 2), value_word32)));
485
486 BIND(&u32);
487 Return(ChangeUint32ToTagged(
488 (this->*function)(MachineType::Uint32(), backing_store,
489 WordShl(index_word, 2), value_word32)));
490
491 // This shouldn't happen, we've already validated the type.
492 BIND(&other);
493 Unreachable();
494 #endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64
495 // || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
496 }
497
498 } // namespace internal
499 } // namespace v8
500