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 = 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