• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  // Copyright 2016 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.h"
6  #include "src/builtins/builtins.h"
7  #include "src/code-factory.h"
8  #include "src/code-stub-assembler.h"
9  
10  namespace v8 {
11  namespace internal {
12  
13  // ES7 sharedmem 6.3.4.1 get SharedArrayBuffer.prototype.byteLength
BUILTIN(SharedArrayBufferPrototypeGetByteLength)14  BUILTIN(SharedArrayBufferPrototypeGetByteLength) {
15    HandleScope scope(isolate);
16    CHECK_RECEIVER(JSArrayBuffer, array_buffer,
17                   "get SharedArrayBuffer.prototype.byteLength");
18    if (!array_buffer->is_shared()) {
19      THROW_NEW_ERROR_RETURN_FAILURE(
20          isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
21                                isolate->factory()->NewStringFromAsciiChecked(
22                                    "get SharedArrayBuffer.prototype.byteLength"),
23                                args.receiver()));
24    }
25    return array_buffer->byte_length();
26  }
27  
28  namespace {
29  
ValidateSharedTypedArray(CodeStubAssembler * a,compiler::Node * tagged,compiler::Node * context,compiler::Node ** out_instance_type,compiler::Node ** out_backing_store)30  void ValidateSharedTypedArray(CodeStubAssembler* a, compiler::Node* tagged,
31                                compiler::Node* context,
32                                compiler::Node** out_instance_type,
33                                compiler::Node** out_backing_store) {
34    using compiler::Node;
35    CodeStubAssembler::Label is_smi(a), not_smi(a), is_typed_array(a),
36        not_typed_array(a), is_shared(a), not_shared(a), is_float_or_clamped(a),
37        not_float_or_clamped(a), invalid(a);
38  
39    // Fail if it is not a heap object.
40    a->Branch(a->TaggedIsSmi(tagged), &is_smi, &not_smi);
41    a->Bind(&is_smi);
42    a->Goto(&invalid);
43  
44    // Fail if the array's instance type is not JSTypedArray.
45    a->Bind(&not_smi);
46    a->Branch(a->Word32Equal(a->LoadInstanceType(tagged),
47                             a->Int32Constant(JS_TYPED_ARRAY_TYPE)),
48              &is_typed_array, &not_typed_array);
49    a->Bind(&not_typed_array);
50    a->Goto(&invalid);
51  
52    // Fail if the array's JSArrayBuffer is not shared.
53    a->Bind(&is_typed_array);
54    Node* array_buffer = a->LoadObjectField(tagged, JSTypedArray::kBufferOffset);
55    Node* is_buffer_shared =
56        a->IsSetWord32<JSArrayBuffer::IsShared>(a->LoadObjectField(
57            array_buffer, JSArrayBuffer::kBitFieldOffset, MachineType::Uint32()));
58    a->Branch(is_buffer_shared, &is_shared, &not_shared);
59    a->Bind(&not_shared);
60    a->Goto(&invalid);
61  
62    // Fail if the array's element type is float32, float64 or clamped.
63    a->Bind(&is_shared);
64    Node* elements_instance_type = a->LoadInstanceType(
65        a->LoadObjectField(tagged, JSObject::kElementsOffset));
66    STATIC_ASSERT(FIXED_INT8_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
67    STATIC_ASSERT(FIXED_INT16_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
68    STATIC_ASSERT(FIXED_INT32_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
69    STATIC_ASSERT(FIXED_UINT8_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
70    STATIC_ASSERT(FIXED_UINT16_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
71    STATIC_ASSERT(FIXED_UINT32_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
72    a->Branch(a->Int32LessThan(elements_instance_type,
73                               a->Int32Constant(FIXED_FLOAT32_ARRAY_TYPE)),
74              &not_float_or_clamped, &is_float_or_clamped);
75    a->Bind(&is_float_or_clamped);
76    a->Goto(&invalid);
77  
78    a->Bind(&invalid);
79    a->CallRuntime(Runtime::kThrowNotIntegerSharedTypedArrayError, context,
80                   tagged);
81    a->Unreachable();
82  
83    a->Bind(&not_float_or_clamped);
84    *out_instance_type = elements_instance_type;
85  
86    Node* backing_store =
87        a->LoadObjectField(array_buffer, JSArrayBuffer::kBackingStoreOffset);
88    Node* byte_offset = a->ChangeUint32ToWord(a->TruncateTaggedToWord32(
89        context,
90        a->LoadObjectField(tagged, JSArrayBufferView::kByteOffsetOffset)));
91    *out_backing_store =
92        a->IntPtrAdd(a->BitcastTaggedToWord(backing_store), byte_offset);
93  }
94  
95  // https://tc39.github.io/ecmascript_sharedmem/shmem.html#Atomics.ValidateAtomicAccess
ConvertTaggedAtomicIndexToWord32(CodeStubAssembler * a,compiler::Node * tagged,compiler::Node * context)96  compiler::Node* ConvertTaggedAtomicIndexToWord32(CodeStubAssembler* a,
97                                                   compiler::Node* tagged,
98                                                   compiler::Node* context) {
99    using compiler::Node;
100    CodeStubAssembler::Variable var_result(a, MachineRepresentation::kWord32);
101  
102    Callable to_number = CodeFactory::ToNumber(a->isolate());
103    Node* number_index = a->CallStub(to_number, context, tagged);
104    CodeStubAssembler::Label done(a, &var_result);
105  
106    CodeStubAssembler::Label if_numberissmi(a), if_numberisnotsmi(a);
107    a->Branch(a->TaggedIsSmi(number_index), &if_numberissmi, &if_numberisnotsmi);
108  
109    a->Bind(&if_numberissmi);
110    {
111      var_result.Bind(a->SmiToWord32(number_index));
112      a->Goto(&done);
113    }
114  
115    a->Bind(&if_numberisnotsmi);
116    {
117      Node* number_index_value = a->LoadHeapNumberValue(number_index);
118      Node* access_index = a->TruncateFloat64ToWord32(number_index_value);
119      Node* test_index = a->ChangeInt32ToFloat64(access_index);
120  
121      CodeStubAssembler::Label if_indexesareequal(a), if_indexesarenotequal(a);
122      a->Branch(a->Float64Equal(number_index_value, test_index),
123                &if_indexesareequal, &if_indexesarenotequal);
124  
125      a->Bind(&if_indexesareequal);
126      {
127        var_result.Bind(access_index);
128        a->Goto(&done);
129      }
130  
131      a->Bind(&if_indexesarenotequal);
132      a->CallRuntime(Runtime::kThrowInvalidAtomicAccessIndexError, context);
133      a->Unreachable();
134    }
135  
136    a->Bind(&done);
137    return var_result.value();
138  }
139  
ValidateAtomicIndex(CodeStubAssembler * a,compiler::Node * index_word,compiler::Node * array_length_word,compiler::Node * context)140  void ValidateAtomicIndex(CodeStubAssembler* a, compiler::Node* index_word,
141                           compiler::Node* array_length_word,
142                           compiler::Node* context) {
143    using compiler::Node;
144    // Check if the index is in bounds. If not, throw RangeError.
145    CodeStubAssembler::Label if_inbounds(a), if_notinbounds(a);
146    // TODO(jkummerow): Use unsigned comparison instead of "i<0 || i>length".
147    a->Branch(
148        a->Word32Or(a->Int32LessThan(index_word, a->Int32Constant(0)),
149                    a->Int32GreaterThanOrEqual(index_word, array_length_word)),
150        &if_notinbounds, &if_inbounds);
151    a->Bind(&if_notinbounds);
152    a->CallRuntime(Runtime::kThrowInvalidAtomicAccessIndexError, context);
153    a->Unreachable();
154    a->Bind(&if_inbounds);
155  }
156  
157  }  // anonymous namespace
158  
Generate_AtomicsLoad(compiler::CodeAssemblerState * state)159  void Builtins::Generate_AtomicsLoad(compiler::CodeAssemblerState* state) {
160    using compiler::Node;
161    CodeStubAssembler a(state);
162    Node* array = a.Parameter(1);
163    Node* index = a.Parameter(2);
164    Node* context = a.Parameter(3 + 2);
165  
166    Node* instance_type;
167    Node* backing_store;
168    ValidateSharedTypedArray(&a, array, context, &instance_type, &backing_store);
169  
170    Node* index_word32 = ConvertTaggedAtomicIndexToWord32(&a, index, context);
171    Node* array_length_word32 = a.TruncateTaggedToWord32(
172        context, a.LoadObjectField(array, JSTypedArray::kLengthOffset));
173    ValidateAtomicIndex(&a, index_word32, array_length_word32, context);
174    Node* index_word = a.ChangeUint32ToWord(index_word32);
175  
176    CodeStubAssembler::Label i8(&a), u8(&a), i16(&a), u16(&a), i32(&a), u32(&a),
177        other(&a);
178    int32_t case_values[] = {
179        FIXED_INT8_ARRAY_TYPE,   FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
180        FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
181    };
182    CodeStubAssembler::Label* case_labels[] = {
183        &i8, &u8, &i16, &u16, &i32, &u32,
184    };
185    a.Switch(instance_type, &other, case_values, case_labels,
186             arraysize(case_labels));
187  
188    a.Bind(&i8);
189    a.Return(a.SmiFromWord32(
190        a.AtomicLoad(MachineType::Int8(), backing_store, index_word)));
191  
192    a.Bind(&u8);
193    a.Return(a.SmiFromWord32(
194        a.AtomicLoad(MachineType::Uint8(), backing_store, index_word)));
195  
196    a.Bind(&i16);
197    a.Return(a.SmiFromWord32(a.AtomicLoad(MachineType::Int16(), backing_store,
198                                          a.WordShl(index_word, 1))));
199  
200    a.Bind(&u16);
201    a.Return(a.SmiFromWord32(a.AtomicLoad(MachineType::Uint16(), backing_store,
202                                          a.WordShl(index_word, 1))));
203  
204    a.Bind(&i32);
205    a.Return(a.ChangeInt32ToTagged(a.AtomicLoad(
206        MachineType::Int32(), backing_store, a.WordShl(index_word, 2))));
207  
208    a.Bind(&u32);
209    a.Return(a.ChangeUint32ToTagged(a.AtomicLoad(
210        MachineType::Uint32(), backing_store, a.WordShl(index_word, 2))));
211  
212    // This shouldn't happen, we've already validated the type.
213    a.Bind(&other);
214    a.Return(a.SmiConstant(0));
215  }
216  
Generate_AtomicsStore(compiler::CodeAssemblerState * state)217  void Builtins::Generate_AtomicsStore(compiler::CodeAssemblerState* state) {
218    using compiler::Node;
219    CodeStubAssembler a(state);
220    Node* array = a.Parameter(1);
221    Node* index = a.Parameter(2);
222    Node* value = a.Parameter(3);
223    Node* context = a.Parameter(4 + 2);
224  
225    Node* instance_type;
226    Node* backing_store;
227    ValidateSharedTypedArray(&a, array, context, &instance_type, &backing_store);
228  
229    Node* index_word32 = ConvertTaggedAtomicIndexToWord32(&a, index, context);
230    Node* array_length_word32 = a.TruncateTaggedToWord32(
231        context, a.LoadObjectField(array, JSTypedArray::kLengthOffset));
232    ValidateAtomicIndex(&a, index_word32, array_length_word32, context);
233    Node* index_word = a.ChangeUint32ToWord(index_word32);
234  
235    Node* value_integer = a.ToInteger(context, value);
236    Node* value_word32 = a.TruncateTaggedToWord32(context, value_integer);
237  
238    CodeStubAssembler::Label u8(&a), u16(&a), u32(&a), other(&a);
239    int32_t case_values[] = {
240        FIXED_INT8_ARRAY_TYPE,   FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
241        FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
242    };
243    CodeStubAssembler::Label* case_labels[] = {
244        &u8, &u8, &u16, &u16, &u32, &u32,
245    };
246    a.Switch(instance_type, &other, case_values, case_labels,
247             arraysize(case_labels));
248  
249    a.Bind(&u8);
250    a.AtomicStore(MachineRepresentation::kWord8, backing_store, index_word,
251                  value_word32);
252    a.Return(value_integer);
253  
254    a.Bind(&u16);
255    a.AtomicStore(MachineRepresentation::kWord16, backing_store,
256                  a.WordShl(index_word, 1), value_word32);
257    a.Return(value_integer);
258  
259    a.Bind(&u32);
260    a.AtomicStore(MachineRepresentation::kWord32, backing_store,
261                  a.WordShl(index_word, 2), value_word32);
262    a.Return(value_integer);
263  
264    // This shouldn't happen, we've already validated the type.
265    a.Bind(&other);
266    a.Return(a.SmiConstant(0));
267  }
268  
269  }  // namespace internal
270  }  // namespace v8
271