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 =
115 TruncateTaggedToWord32(context, LoadTypedArrayLength(CAST(array)));
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(this,
131 Uint32LessThan(index_word,
132 TruncateTaggedToWord32(
133 context, LoadTypedArrayLength(CAST(array)))));
134 }
135 #endif
136
TF_BUILTIN(AtomicsLoad,SharedArrayBufferBuiltinsAssembler)137 TF_BUILTIN(AtomicsLoad, SharedArrayBufferBuiltinsAssembler) {
138 Node* array = Parameter(Descriptor::kArray);
139 Node* index = Parameter(Descriptor::kIndex);
140 Node* context = Parameter(Descriptor::kContext);
141
142 Node* instance_type;
143 Node* backing_store;
144 ValidateSharedTypedArray(array, context, &instance_type, &backing_store);
145
146 Node* index_integer;
147 Node* index_word32 =
148 ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
149 ValidateAtomicIndex(array, index_word32, context);
150 Node* index_word = ChangeUint32ToWord(index_word32);
151
152 Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
153 other(this);
154 int32_t case_values[] = {
155 FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
156 FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
157 };
158 Label* case_labels[] = {
159 &i8, &u8, &i16, &u16, &i32, &u32,
160 };
161 Switch(instance_type, &other, case_values, case_labels,
162 arraysize(case_labels));
163
164 BIND(&i8);
165 Return(
166 SmiFromInt32(AtomicLoad(MachineType::Int8(), backing_store, index_word)));
167
168 BIND(&u8);
169 Return(SmiFromInt32(
170 AtomicLoad(MachineType::Uint8(), backing_store, index_word)));
171
172 BIND(&i16);
173 Return(SmiFromInt32(
174 AtomicLoad(MachineType::Int16(), backing_store, WordShl(index_word, 1))));
175
176 BIND(&u16);
177 Return(SmiFromInt32(AtomicLoad(MachineType::Uint16(), backing_store,
178 WordShl(index_word, 1))));
179
180 BIND(&i32);
181 Return(ChangeInt32ToTagged(
182 AtomicLoad(MachineType::Int32(), backing_store, WordShl(index_word, 2))));
183
184 BIND(&u32);
185 Return(ChangeUint32ToTagged(AtomicLoad(MachineType::Uint32(), backing_store,
186 WordShl(index_word, 2))));
187
188 // This shouldn't happen, we've already validated the type.
189 BIND(&other);
190 Unreachable();
191 }
192
TF_BUILTIN(AtomicsStore,SharedArrayBufferBuiltinsAssembler)193 TF_BUILTIN(AtomicsStore, SharedArrayBufferBuiltinsAssembler) {
194 Node* array = Parameter(Descriptor::kArray);
195 Node* index = Parameter(Descriptor::kIndex);
196 Node* value = Parameter(Descriptor::kValue);
197 Node* context = Parameter(Descriptor::kContext);
198
199 Node* instance_type;
200 Node* backing_store;
201 ValidateSharedTypedArray(array, context, &instance_type, &backing_store);
202
203 Node* index_integer;
204 Node* index_word32 =
205 ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
206 ValidateAtomicIndex(array, index_word32, context);
207 Node* index_word = ChangeUint32ToWord(index_word32);
208
209 Node* value_integer = ToInteger_Inline(CAST(context), CAST(value));
210 Node* value_word32 = TruncateTaggedToWord32(context, value_integer);
211
212 #if DEBUG
213 DebugSanityCheckAtomicIndex(array, index_word32, context);
214 #endif
215
216 Label u8(this), u16(this), u32(this), other(this);
217 int32_t case_values[] = {
218 FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
219 FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
220 };
221 Label* case_labels[] = {
222 &u8, &u8, &u16, &u16, &u32, &u32,
223 };
224 Switch(instance_type, &other, case_values, case_labels,
225 arraysize(case_labels));
226
227 BIND(&u8);
228 AtomicStore(MachineRepresentation::kWord8, backing_store, index_word,
229 value_word32);
230 Return(value_integer);
231
232 BIND(&u16);
233 AtomicStore(MachineRepresentation::kWord16, backing_store,
234 WordShl(index_word, 1), value_word32);
235 Return(value_integer);
236
237 BIND(&u32);
238 AtomicStore(MachineRepresentation::kWord32, backing_store,
239 WordShl(index_word, 2), value_word32);
240 Return(value_integer);
241
242 // This shouldn't happen, we've already validated the type.
243 BIND(&other);
244 Unreachable();
245 }
246
TF_BUILTIN(AtomicsExchange,SharedArrayBufferBuiltinsAssembler)247 TF_BUILTIN(AtomicsExchange, SharedArrayBufferBuiltinsAssembler) {
248 Node* array = Parameter(Descriptor::kArray);
249 Node* index = Parameter(Descriptor::kIndex);
250 Node* value = Parameter(Descriptor::kValue);
251 Node* context = Parameter(Descriptor::kContext);
252
253 Node* instance_type;
254 Node* backing_store;
255 ValidateSharedTypedArray(array, context, &instance_type, &backing_store);
256
257 Node* index_integer;
258 Node* index_word32 =
259 ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
260 ValidateAtomicIndex(array, index_word32, context);
261
262 Node* value_integer = ToInteger_Inline(CAST(context), CAST(value));
263
264 #if DEBUG
265 DebugSanityCheckAtomicIndex(array, index_word32, context);
266 #endif
267
268 #if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
269 Return(CallRuntime(Runtime::kAtomicsExchange, context, array, index_integer,
270 value_integer));
271 #else
272 Node* index_word = ChangeUint32ToWord(index_word32);
273
274 Node* value_word32 = TruncateTaggedToWord32(context, value_integer);
275
276 Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
277 other(this);
278 int32_t case_values[] = {
279 FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
280 FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
281 };
282 Label* case_labels[] = {
283 &i8, &u8, &i16, &u16, &i32, &u32,
284 };
285 Switch(instance_type, &other, case_values, case_labels,
286 arraysize(case_labels));
287
288 BIND(&i8);
289 Return(SmiFromInt32(AtomicExchange(MachineType::Int8(), backing_store,
290 index_word, value_word32)));
291
292 BIND(&u8);
293 Return(SmiFromInt32(AtomicExchange(MachineType::Uint8(), backing_store,
294 index_word, value_word32)));
295
296 BIND(&i16);
297 Return(SmiFromInt32(AtomicExchange(MachineType::Int16(), backing_store,
298 WordShl(index_word, 1), value_word32)));
299
300 BIND(&u16);
301 Return(SmiFromInt32(AtomicExchange(MachineType::Uint16(), backing_store,
302 WordShl(index_word, 1), value_word32)));
303
304 BIND(&i32);
305 Return(ChangeInt32ToTagged(AtomicExchange(MachineType::Int32(), backing_store,
306 WordShl(index_word, 2),
307 value_word32)));
308
309 BIND(&u32);
310 Return(ChangeUint32ToTagged(
311 AtomicExchange(MachineType::Uint32(), backing_store,
312 WordShl(index_word, 2), value_word32)));
313
314 // This shouldn't happen, we've already validated the type.
315 BIND(&other);
316 Unreachable();
317 #endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
318 }
319
TF_BUILTIN(AtomicsCompareExchange,SharedArrayBufferBuiltinsAssembler)320 TF_BUILTIN(AtomicsCompareExchange, SharedArrayBufferBuiltinsAssembler) {
321 Node* array = Parameter(Descriptor::kArray);
322 Node* index = Parameter(Descriptor::kIndex);
323 Node* old_value = Parameter(Descriptor::kOldValue);
324 Node* new_value = Parameter(Descriptor::kNewValue);
325 Node* context = Parameter(Descriptor::kContext);
326
327 Node* instance_type;
328 Node* backing_store;
329 ValidateSharedTypedArray(array, context, &instance_type, &backing_store);
330
331 Node* index_integer;
332 Node* index_word32 =
333 ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
334 ValidateAtomicIndex(array, index_word32, context);
335
336 Node* old_value_integer = ToInteger_Inline(CAST(context), CAST(old_value));
337 Node* new_value_integer = ToInteger_Inline(CAST(context), CAST(new_value));
338
339 #if DEBUG
340 DebugSanityCheckAtomicIndex(array, index_word32, context);
341 #endif
342
343 #if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \
344 V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
345 Return(CallRuntime(Runtime::kAtomicsCompareExchange, context, array,
346 index_integer, old_value_integer, new_value_integer));
347 #else
348 Node* index_word = ChangeUint32ToWord(index_word32);
349
350 Node* old_value_word32 = TruncateTaggedToWord32(context, old_value_integer);
351
352 Node* new_value_word32 = TruncateTaggedToWord32(context, new_value_integer);
353
354 Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
355 other(this);
356 int32_t case_values[] = {
357 FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
358 FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
359 };
360 Label* case_labels[] = {
361 &i8, &u8, &i16, &u16, &i32, &u32,
362 };
363 Switch(instance_type, &other, case_values, case_labels,
364 arraysize(case_labels));
365
366 BIND(&i8);
367 Return(SmiFromInt32(AtomicCompareExchange(MachineType::Int8(), backing_store,
368 index_word, old_value_word32,
369 new_value_word32)));
370
371 BIND(&u8);
372 Return(SmiFromInt32(AtomicCompareExchange(MachineType::Uint8(), backing_store,
373 index_word, old_value_word32,
374 new_value_word32)));
375
376 BIND(&i16);
377 Return(SmiFromInt32(AtomicCompareExchange(
378 MachineType::Int16(), backing_store, WordShl(index_word, 1),
379 old_value_word32, new_value_word32)));
380
381 BIND(&u16);
382 Return(SmiFromInt32(AtomicCompareExchange(
383 MachineType::Uint16(), backing_store, WordShl(index_word, 1),
384 old_value_word32, new_value_word32)));
385
386 BIND(&i32);
387 Return(ChangeInt32ToTagged(AtomicCompareExchange(
388 MachineType::Int32(), backing_store, WordShl(index_word, 2),
389 old_value_word32, new_value_word32)));
390
391 BIND(&u32);
392 Return(ChangeUint32ToTagged(AtomicCompareExchange(
393 MachineType::Uint32(), backing_store, WordShl(index_word, 2),
394 old_value_word32, new_value_word32)));
395
396 // This shouldn't happen, we've already validated the type.
397 BIND(&other);
398 Unreachable();
399 #endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64
400 // || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
401 }
402
403 #define BINOP_BUILTIN(op) \
404 TF_BUILTIN(Atomics##op, SharedArrayBufferBuiltinsAssembler) { \
405 Node* array = Parameter(Descriptor::kArray); \
406 Node* index = Parameter(Descriptor::kIndex); \
407 Node* value = Parameter(Descriptor::kValue); \
408 Node* context = Parameter(Descriptor::kContext); \
409 AtomicBinopBuiltinCommon(array, index, value, context, \
410 &CodeAssembler::Atomic##op, \
411 Runtime::kAtomics##op); \
412 }
413 BINOP_BUILTIN(Add)
BINOP_BUILTIN(Sub)414 BINOP_BUILTIN(Sub)
415 BINOP_BUILTIN(And)
416 BINOP_BUILTIN(Or)
417 BINOP_BUILTIN(Xor)
418 #undef BINOP_BUILTIN
419
420 void SharedArrayBufferBuiltinsAssembler::AtomicBinopBuiltinCommon(
421 Node* array, Node* index, Node* value, Node* context,
422 AssemblerFunction function, Runtime::FunctionId runtime_function) {
423 Node* instance_type;
424 Node* backing_store;
425 ValidateSharedTypedArray(array, context, &instance_type, &backing_store);
426
427 Node* index_integer;
428 Node* index_word32 =
429 ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
430 ValidateAtomicIndex(array, index_word32, context);
431
432 Node* value_integer = ToInteger_Inline(CAST(context), CAST(value));
433
434 #if DEBUG
435 // In Debug mode, we re-validate the index as a sanity check because
436 // ToInteger above calls out to JavaScript. A SharedArrayBuffer can't be
437 // neutered and the TypedArray length can't change either, so skipping this
438 // check in Release mode is safe.
439 ValidateAtomicIndex(array, index_word32, context);
440 #endif
441
442 #if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \
443 V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
444 Return(CallRuntime(runtime_function, context, array, index_integer,
445 value_integer));
446 #else
447 Node* index_word = ChangeUint32ToWord(index_word32);
448
449 Node* value_word32 = TruncateTaggedToWord32(context, value_integer);
450
451 Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
452 other(this);
453 int32_t case_values[] = {
454 FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
455 FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
456 };
457 Label* case_labels[] = {
458 &i8, &u8, &i16, &u16, &i32, &u32,
459 };
460 Switch(instance_type, &other, case_values, case_labels,
461 arraysize(case_labels));
462
463 BIND(&i8);
464 Return(SmiFromInt32((this->*function)(MachineType::Int8(), backing_store,
465 index_word, value_word32)));
466
467 BIND(&u8);
468 Return(SmiFromInt32((this->*function)(MachineType::Uint8(), backing_store,
469 index_word, value_word32)));
470
471 BIND(&i16);
472 Return(SmiFromInt32((this->*function)(MachineType::Int16(), backing_store,
473 WordShl(index_word, 1), value_word32)));
474
475 BIND(&u16);
476 Return(SmiFromInt32((this->*function)(MachineType::Uint16(), backing_store,
477 WordShl(index_word, 1), value_word32)));
478
479 BIND(&i32);
480 Return(ChangeInt32ToTagged(
481 (this->*function)(MachineType::Int32(), backing_store,
482 WordShl(index_word, 2), value_word32)));
483
484 BIND(&u32);
485 Return(ChangeUint32ToTagged(
486 (this->*function)(MachineType::Uint32(), backing_store,
487 WordShl(index_word, 2), value_word32)));
488
489 // This shouldn't happen, we've already validated the type.
490 BIND(&other);
491 Unreachable();
492 #endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64
493 // || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
494 }
495
496 } // namespace internal
497 } // namespace v8
498