Skip to content

Commit 6962221

Browse files
sygCommit Bot
authored andcommitted
[atomics] Relax Atomics methods to work on ArrayBuffers
This reached consensus in the March 2020 TC39. tc39/ecma262#1908 This aligns JS with wasm, which allows atomics operations on non-shared linear memory. Bug: v8:10687, v8:9921 Change-Id: I7b60473b271cee6bccb342e97a4fd3781aedddb4 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2330802 Commit-Queue: Shu-yu Guo <syg@chromium.org> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Cr-Commit-Position: refs/heads/master@{#69392}
1 parent c572264 commit 6962221

18 files changed

+463
-189
lines changed

src/builtins/builtins-sharedarraybuffer-gen.cc

Lines changed: 190 additions & 36 deletions
Large diffs are not rendered by default.

src/builtins/builtins-sharedarraybuffer.cc

Lines changed: 62 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ namespace v8 {
2020
namespace internal {
2121

2222
// See builtins-arraybuffer.cc for implementations of
23-
// SharedArrayBuffer.prototye.byteLength and SharedArrayBuffer.prototype.slice
23+
// SharedArrayBuffer.prototype.byteLength and SharedArrayBuffer.prototype.slice
2424

25-
// #sec-atomics.islockfree
25+
// https://tc39.es/ecma262/#sec-atomics.islockfree
2626
inline bool AtomicIsLockFree(double size) {
2727
// According to the standard, 1, 2, and 4 byte atomics are supposed to be
2828
// 'lock free' on every platform. 'Lock free' means that all possible uses of
@@ -39,7 +39,7 @@ inline bool AtomicIsLockFree(double size) {
3939
return size == 1 || size == 2 || size == 4 || size == 8;
4040
}
4141

42-
// ES #sec-atomics.islockfree
42+
// https://tc39.es/ecma262/#sec-atomics.islockfree
4343
BUILTIN(AtomicsIsLockFree) {
4444
HandleScope scope(isolate);
4545
Handle<Object> size = args.atOrUndefined(isolate, 1);
@@ -48,37 +48,45 @@ BUILTIN(AtomicsIsLockFree) {
4848
return *isolate->factory()->ToBoolean(AtomicIsLockFree(size->Number()));
4949
}
5050

51-
// ES #sec-validatesharedintegertypedarray
52-
V8_WARN_UNUSED_RESULT MaybeHandle<JSTypedArray> ValidateSharedIntegerTypedArray(
53-
Isolate* isolate, Handle<Object> object,
51+
// https://tc39.es/ecma262/#sec-validatesharedintegertypedarray
52+
V8_WARN_UNUSED_RESULT MaybeHandle<JSTypedArray> ValidateIntegerTypedArray(
53+
Isolate* isolate, Handle<Object> object, const char* method_name,
5454
bool only_int32_and_big_int64 = false) {
5555
if (object->IsJSTypedArray()) {
5656
Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(object);
57-
if (typed_array->GetBuffer()->is_shared()) {
58-
if (only_int32_and_big_int64) {
59-
if (typed_array->type() == kExternalInt32Array ||
60-
typed_array->type() == kExternalBigInt64Array) {
61-
return typed_array;
62-
}
63-
} else {
64-
if (typed_array->type() != kExternalFloat32Array &&
65-
typed_array->type() != kExternalFloat64Array &&
66-
typed_array->type() != kExternalUint8ClampedArray)
67-
return typed_array;
57+
58+
if (typed_array->WasDetached()) {
59+
THROW_NEW_ERROR(
60+
isolate,
61+
NewTypeError(
62+
MessageTemplate::kDetachedOperation,
63+
isolate->factory()->NewStringFromAsciiChecked(method_name)),
64+
JSTypedArray);
65+
}
66+
67+
if (only_int32_and_big_int64) {
68+
if (typed_array->type() == kExternalInt32Array ||
69+
typed_array->type() == kExternalBigInt64Array) {
70+
return typed_array;
6871
}
72+
} else {
73+
if (typed_array->type() != kExternalFloat32Array &&
74+
typed_array->type() != kExternalFloat64Array &&
75+
typed_array->type() != kExternalUint8ClampedArray)
76+
return typed_array;
6977
}
7078
}
7179

7280
THROW_NEW_ERROR(
7381
isolate,
7482
NewTypeError(only_int32_and_big_int64
75-
? MessageTemplate::kNotInt32OrBigInt64SharedTypedArray
76-
: MessageTemplate::kNotIntegerSharedTypedArray,
83+
? MessageTemplate::kNotInt32OrBigInt64TypedArray
84+
: MessageTemplate::kNotIntegerTypedArray,
7785
object),
7886
JSTypedArray);
7987
}
8088

81-
// ES #sec-validateatomicaccess
89+
// https://tc39.es/ecma262/#sec-validateatomicaccess
8290
// ValidateAtomicAccess( typedArray, requestIndex )
8391
V8_WARN_UNUSED_RESULT Maybe<size_t> ValidateAtomicAccess(
8492
Isolate* isolate, Handle<JSTypedArray> typed_array,
@@ -91,8 +99,9 @@ V8_WARN_UNUSED_RESULT Maybe<size_t> ValidateAtomicAccess(
9199
Nothing<size_t>());
92100

93101
size_t access_index;
102+
size_t typed_array_length = typed_array->length();
94103
if (!TryNumberToSize(*access_index_obj, &access_index) ||
95-
typed_array->WasDetached() || access_index >= typed_array->length()) {
104+
access_index >= typed_array_length) {
96105
isolate->Throw(*isolate->factory()->NewRangeError(
97106
MessageTemplate::kInvalidAtomicAccessIndex));
98107
return Nothing<size_t>();
@@ -122,12 +131,18 @@ BUILTIN(AtomicsNotify) {
122131

123132
Handle<JSTypedArray> sta;
124133
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
125-
isolate, sta, ValidateSharedIntegerTypedArray(isolate, array, true));
134+
isolate, sta,
135+
ValidateIntegerTypedArray(isolate, array, "Atomics.notify", true));
126136

137+
// 2. Let i be ? ValidateAtomicAccess(typedArray, index).
127138
Maybe<size_t> maybe_index = ValidateAtomicAccess(isolate, sta, index);
128139
if (maybe_index.IsNothing()) return ReadOnlyRoots(isolate).exception();
129140
size_t i = maybe_index.FromJust();
130141

142+
// 3. If count is undefined, let c be +∞.
143+
// 4. Else,
144+
// a. Let intCount be ? ToInteger(count).
145+
// b. Let c be max(intCount, 0).
131146
uint32_t c;
132147
if (count->IsUndefined(isolate)) {
133148
c = kMaxUInt32;
@@ -143,9 +158,17 @@ BUILTIN(AtomicsNotify) {
143158
c = static_cast<uint32_t>(count_double);
144159
}
145160

161+
// Steps 5-9 performed in FutexEmulation::Wake.
162+
163+
// 10. If IsSharedArrayBuffer(buffer) is false, return 0.
146164
Handle<JSArrayBuffer> array_buffer = sta->GetBuffer();
147165
size_t wake_addr;
148166

167+
if (V8_UNLIKELY(!sta->GetBuffer()->is_shared())) {
168+
return Smi::FromInt(0);
169+
}
170+
171+
// Steps 11-17 performed in FutexEmulation::Wake.
149172
if (sta->type() == kExternalBigInt64Array) {
150173
wake_addr = GetAddress64(i, sta->byte_offset());
151174
} else {
@@ -158,19 +181,26 @@ BUILTIN(AtomicsNotify) {
158181
Object DoWait(Isolate* isolate, FutexEmulation::WaitMode mode,
159182
Handle<Object> array, Handle<Object> index, Handle<Object> value,
160183
Handle<Object> timeout) {
161-
// 1. Let buffer be ? ValidateSharedIntegerTypedArray(typedArray, true).
184+
// 1. Let buffer be ? ValidateIntegerTypedArray(typedArray, true).
162185
Handle<JSTypedArray> sta;
163186
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
164-
isolate, sta, ValidateSharedIntegerTypedArray(isolate, array, true));
187+
isolate, sta,
188+
ValidateIntegerTypedArray(isolate, array, "Atomics.wait", true));
165189

166-
// 2. Let i be ? ValidateAtomicAccess(typedArray, index).
190+
// 2. If IsSharedArrayBuffer(buffer) is false, throw a TypeError exception.
191+
if (V8_UNLIKELY(!sta->GetBuffer()->is_shared())) {
192+
THROW_NEW_ERROR_RETURN_FAILURE(
193+
isolate, NewTypeError(MessageTemplate::kNotSharedTypedArray, array));
194+
}
195+
196+
// 3. Let i be ? ValidateAtomicAccess(typedArray, index).
167197
Maybe<size_t> maybe_index = ValidateAtomicAccess(isolate, sta, index);
168198
if (maybe_index.IsNothing()) return ReadOnlyRoots(isolate).exception();
169199
size_t i = maybe_index.FromJust();
170200

171-
// 3. Let arrayTypeName be typedArray.[[TypedArrayName]].
172-
// 4. If arrayTypeName is "BigInt64Array", let v be ? ToBigInt64(value).
173-
// 5. Otherwise, let v be ? ToInt32(value).
201+
// 4. Let arrayTypeName be typedArray.[[TypedArrayName]].
202+
// 5. If arrayTypeName is "BigInt64Array", let v be ? ToBigInt64(value).
203+
// 6. Otherwise, let v be ? ToInt32(value).
174204
if (sta->type() == kExternalBigInt64Array) {
175205
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value,
176206
BigInt::FromObject(isolate, value));
@@ -180,8 +210,8 @@ Object DoWait(Isolate* isolate, FutexEmulation::WaitMode mode,
180210
Object::ToInt32(isolate, value));
181211
}
182212

183-
// 6. Let q be ? ToNumber(timeout).
184-
// 7. If q is NaN, let t be +∞, else let t be max(q, 0).
213+
// 7. Let q be ? ToNumber(timeout).
214+
// 8. If q is NaN, let t be +∞, else let t be max(q, 0).
185215
double timeout_number;
186216
if (timeout->IsUndefined(isolate)) {
187217
timeout_number = ReadOnlyRoots(isolate).infinity_value().Number();
@@ -195,7 +225,7 @@ Object DoWait(Isolate* isolate, FutexEmulation::WaitMode mode,
195225
timeout_number = 0;
196226
}
197227

198-
// 8. If mode is sync, then
228+
// 9. If mode is sync, then
199229
// a. Let B be AgentCanSuspend().
200230
// b. If B is false, throw a TypeError exception.
201231
if (mode == FutexEmulation::WaitMode::kSync &&
@@ -218,7 +248,7 @@ Object DoWait(Isolate* isolate, FutexEmulation::WaitMode mode,
218248
}
219249
}
220250

221-
// ES #sec-atomics.wait
251+
// https://tc39.es/ecma262/#sec-atomics.wait
222252
// Atomics.wait( typedArray, index, value, timeout )
223253
BUILTIN(AtomicsWait) {
224254
HandleScope scope(isolate);

src/builtins/builtins-typed-array-gen.cc

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -231,28 +231,6 @@ TNode<JSFunction> TypedArrayBuiltinsAssembler::GetDefaultConstructor(
231231
LoadContextElement(LoadNativeContext(context), context_slot.value()));
232232
}
233233

234-
TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::GetBuffer(
235-
TNode<Context> context, TNode<JSTypedArray> array) {
236-
Label call_runtime(this), done(this);
237-
TVARIABLE(Object, var_result);
238-
239-
TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(array);
240-
GotoIf(IsDetachedBuffer(buffer), &call_runtime);
241-
TNode<RawPtrT> backing_store = LoadJSArrayBufferBackingStorePtr(buffer);
242-
GotoIf(WordEqual(backing_store, IntPtrConstant(0)), &call_runtime);
243-
var_result = buffer;
244-
Goto(&done);
245-
246-
BIND(&call_runtime);
247-
{
248-
var_result = CallRuntime(Runtime::kTypedArrayGetBuffer, context, array);
249-
Goto(&done);
250-
}
251-
252-
BIND(&done);
253-
return CAST(var_result.value());
254-
}
255-
256234
TNode<JSTypedArray> TypedArrayBuiltinsAssembler::ValidateTypedArray(
257235
TNode<Context> context, TNode<Object> obj, const char* method_name) {
258236
// If it is not a typed array, throw

src/builtins/builtins-typed-array-gen.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,6 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
4545
TNode<JSFunction> GetDefaultConstructor(TNode<Context> context,
4646
TNode<JSTypedArray> exemplar);
4747

48-
TNode<JSArrayBuffer> GetBuffer(TNode<Context> context,
49-
TNode<JSTypedArray> array);
50-
5148
TNode<JSTypedArray> ValidateTypedArray(TNode<Context> context,
5249
TNode<Object> obj,
5350
const char* method_name);

src/builtins/typed-array-subarray.tq

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ transitioning javascript builtin TypedArrayPrototypeSubArray(
1717
MessageTemplate::kIncompatibleMethodReceiver, methodName);
1818

1919
// 5. Let buffer be O.[[ViewedArrayBuffer]].
20-
const buffer = typed_array::GetBuffer(source);
20+
const buffer = typed_array::GetTypedArrayBuffer(source);
2121

2222
// 6. Let srcLength be O.[[ArrayLength]].
2323
const srcLength: uintptr = source.length;

src/builtins/typed-array.tq

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ extern macro TypedArrayBuiltinsAssembler::CallCMemmove(
6363
RawPtr, RawPtr, uintptr): void;
6464
extern macro TypedArrayBuiltinsAssembler::CallCMemset(
6565
RawPtr, intptr, uintptr): void;
66-
extern macro TypedArrayBuiltinsAssembler::GetBuffer(implicit context: Context)(
67-
JSTypedArray): JSArrayBuffer;
66+
extern macro GetTypedArrayBuffer(implicit context: Context)(JSTypedArray):
67+
JSArrayBuffer;
6868
extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(
6969
JSTypedArray): TypedArrayElementsInfo;
7070
extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(Map):

src/codegen/code-stub-assembler.cc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12528,6 +12528,28 @@ TNode<UintPtrT> CodeStubAssembler::LoadJSTypedArrayLength(
1252812528
return LoadObjectField<UintPtrT>(typed_array, JSTypedArray::kLengthOffset);
1252912529
}
1253012530

12531+
TNode<JSArrayBuffer> CodeStubAssembler::GetTypedArrayBuffer(
12532+
TNode<Context> context, TNode<JSTypedArray> array) {
12533+
Label call_runtime(this), done(this);
12534+
TVARIABLE(Object, var_result);
12535+
12536+
TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(array);
12537+
GotoIf(IsDetachedBuffer(buffer), &call_runtime);
12538+
TNode<RawPtrT> backing_store = LoadJSArrayBufferBackingStorePtr(buffer);
12539+
GotoIf(WordEqual(backing_store, IntPtrConstant(0)), &call_runtime);
12540+
var_result = buffer;
12541+
Goto(&done);
12542+
12543+
BIND(&call_runtime);
12544+
{
12545+
var_result = CallRuntime(Runtime::kTypedArrayGetBuffer, context, array);
12546+
Goto(&done);
12547+
}
12548+
12549+
BIND(&done);
12550+
return CAST(var_result.value());
12551+
}
12552+
1253112553
CodeStubArguments::CodeStubArguments(CodeStubAssembler* assembler,
1253212554
TNode<IntPtrT> argc, TNode<RawPtrT> fp)
1253312555
: assembler_(assembler),

src/codegen/code-stub-assembler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3442,6 +3442,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
34423442
// JSTypedArray helpers
34433443
TNode<UintPtrT> LoadJSTypedArrayLength(TNode<JSTypedArray> typed_array);
34443444
TNode<RawPtrT> LoadJSTypedArrayDataPtr(TNode<JSTypedArray> typed_array);
3445+
TNode<JSArrayBuffer> GetTypedArrayBuffer(TNode<Context> context,
3446+
TNode<JSTypedArray> array);
34453447

34463448
template <typename TIndex>
34473449
TNode<IntPtrT> ElementOffsetFromIndex(TNode<TIndex> index, ElementsKind kind,

src/common/message-template.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,10 @@ namespace internal {
144144
T(NotSuperConstructor, "Super constructor % of % is not a constructor") \
145145
T(NotSuperConstructorAnonymousClass, \
146146
"Super constructor % of anonymous class is not a constructor") \
147-
T(NotIntegerSharedTypedArray, "% is not an integer shared typed array.") \
148-
T(NotInt32OrBigInt64SharedTypedArray, \
149-
"% is not an int32 or BigInt64 shared typed array.") \
147+
T(NotIntegerTypedArray, "% is not an integer typed array.") \
148+
T(NotInt32OrBigInt64TypedArray, \
149+
"% is not an int32 or BigInt64 typed array.") \
150+
T(NotSharedTypedArray, "% is not a shared typed array.") \
150151
T(ObjectGetterExpectingFunction, \
151152
"Object.prototype.__defineGetter__: Expecting function") \
152153
T(ObjectGetterCallable, "Getter must be a function: %") \

0 commit comments

Comments
 (0)