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          &not_float_or_clamped, &invalid);
69 
70   BIND(&invalid);
71   {
72     ThrowTypeError(context, MessageTemplate::kNotIntegerSharedTypedArray,
73                    tagged);
74   }
75 
76   BIND(&not_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