[wasm] Prototype I32AtomicWake implementation
Bug=v8:8075 R=adamk@chromium.org Change-Id: Ib7b18448d59e1f54f664e24908fbd3a8b4607a9f Reviewed-on: https://chromium-review.googlesource.com/c/1332153 Commit-Queue: Aseem Garg <aseemgarg@chromium.org> Reviewed-by: Ben Smith <binji@chromium.org> Cr-Commit-Position: refs/heads/master@{#57623}
This commit is contained in:
parent
50798d6028
commit
de8609aaf5
@ -1231,6 +1231,7 @@ namespace internal {
|
|||||||
/* Wasm */ \
|
/* Wasm */ \
|
||||||
ASM(WasmCompileLazy) \
|
ASM(WasmCompileLazy) \
|
||||||
TFC(WasmAllocateHeapNumber, AllocateHeapNumber, 1) \
|
TFC(WasmAllocateHeapNumber, AllocateHeapNumber, 1) \
|
||||||
|
TFC(WasmAtomicWake, WasmAtomicWake, 1) \
|
||||||
TFC(WasmCallJavaScript, CallTrampoline, 1) \
|
TFC(WasmCallJavaScript, CallTrampoline, 1) \
|
||||||
TFC(WasmMemoryGrow, WasmMemoryGrow, 1) \
|
TFC(WasmMemoryGrow, WasmMemoryGrow, 1) \
|
||||||
TFC(WasmRecordWrite, RecordWrite, 1) \
|
TFC(WasmRecordWrite, RecordWrite, 1) \
|
||||||
@ -1529,6 +1530,7 @@ namespace internal {
|
|||||||
#define WASM_RUNTIME_STUB_LIST(V, VTRAP) \
|
#define WASM_RUNTIME_STUB_LIST(V, VTRAP) \
|
||||||
FOREACH_WASM_TRAPREASON(VTRAP) \
|
FOREACH_WASM_TRAPREASON(VTRAP) \
|
||||||
V(WasmAllocateHeapNumber) \
|
V(WasmAllocateHeapNumber) \
|
||||||
|
V(WasmAtomicWake) \
|
||||||
V(WasmCallJavaScript) \
|
V(WasmCallJavaScript) \
|
||||||
V(WasmMemoryGrow) \
|
V(WasmMemoryGrow) \
|
||||||
V(WasmRecordWrite) \
|
V(WasmRecordWrite) \
|
||||||
|
@ -98,6 +98,31 @@ TF_BUILTIN(WasmThrow, WasmBuiltinsAssembler) {
|
|||||||
TailCallRuntimeWithCEntry(Runtime::kThrow, centry, context, exception);
|
TailCallRuntimeWithCEntry(Runtime::kThrow, centry, context, exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TF_BUILTIN(WasmAtomicWake, WasmBuiltinsAssembler) {
|
||||||
|
TNode<Uint32T> address =
|
||||||
|
UncheckedCast<Uint32T>(Parameter(Descriptor::kAddress));
|
||||||
|
TNode<Uint32T> count = UncheckedCast<Uint32T>(Parameter(Descriptor::kCount));
|
||||||
|
|
||||||
|
TNode<Object> instance = LoadInstanceFromFrame();
|
||||||
|
TNode<Code> centry = LoadCEntryFromInstance(instance);
|
||||||
|
|
||||||
|
TNode<Code> target = LoadBuiltinFromFrame(Builtins::kAllocateHeapNumber);
|
||||||
|
|
||||||
|
// TODO(aseemgarg): Use SMIs if possible for address and count
|
||||||
|
TNode<HeapNumber> address_heap = UncheckedCast<HeapNumber>(
|
||||||
|
CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant()));
|
||||||
|
StoreHeapNumberValue(address_heap, ChangeUint32ToFloat64(address));
|
||||||
|
|
||||||
|
TNode<HeapNumber> count_heap = UncheckedCast<HeapNumber>(
|
||||||
|
CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant()));
|
||||||
|
StoreHeapNumberValue(count_heap, ChangeUint32ToFloat64(count));
|
||||||
|
|
||||||
|
TNode<Smi> result_smi = UncheckedCast<Smi>(CallRuntimeWithCEntry(
|
||||||
|
Runtime::kWasmAtomicWake, centry, NoContextConstant(), instance,
|
||||||
|
address_heap, count_heap));
|
||||||
|
ReturnRaw(SmiToInt32(result_smi));
|
||||||
|
}
|
||||||
|
|
||||||
TF_BUILTIN(WasmMemoryGrow, WasmBuiltinsAssembler) {
|
TF_BUILTIN(WasmMemoryGrow, WasmBuiltinsAssembler) {
|
||||||
TNode<Int32T> num_pages =
|
TNode<Int32T> num_pages =
|
||||||
UncheckedCast<Int32T>(Parameter(Descriptor::kNumPages));
|
UncheckedCast<Int32T>(Parameter(Descriptor::kNumPages));
|
||||||
|
@ -4065,6 +4065,27 @@ Node* WasmGraphBuilder::AtomicOp(wasm::WasmOpcode opcode, Node* const* inputs,
|
|||||||
}
|
}
|
||||||
ATOMIC_STORE_LIST(BUILD_ATOMIC_STORE_OP)
|
ATOMIC_STORE_LIST(BUILD_ATOMIC_STORE_OP)
|
||||||
#undef BUILD_ATOMIC_STORE_OP
|
#undef BUILD_ATOMIC_STORE_OP
|
||||||
|
case wasm::kExprAtomicWake: {
|
||||||
|
Node* index = CheckBoundsAndAlignment(
|
||||||
|
wasm::ValueTypes::MemSize(MachineType::Uint32()), inputs[0], offset,
|
||||||
|
position);
|
||||||
|
// Now that we've bounds-checked, compute the effective address.
|
||||||
|
Node* address = graph()->NewNode(mcgraph()->machine()->Int32Add(),
|
||||||
|
Uint32Constant(offset), index);
|
||||||
|
WasmAtomicWakeDescriptor interface_descriptor;
|
||||||
|
auto call_descriptor = Linkage::GetStubCallDescriptor(
|
||||||
|
mcgraph()->zone(), interface_descriptor,
|
||||||
|
interface_descriptor.GetStackParameterCount(),
|
||||||
|
CallDescriptor::kNoFlags, Operator::kNoProperties,
|
||||||
|
StubCallMode::kCallWasmRuntimeStub);
|
||||||
|
Node* call_target = mcgraph()->RelocatableIntPtrConstant(
|
||||||
|
wasm::WasmCode::kWasmAtomicWake, RelocInfo::WASM_STUB_CALL);
|
||||||
|
node = graph()->NewNode(mcgraph()->common()->Call(call_descriptor),
|
||||||
|
call_target, address, inputs[1], Effect(),
|
||||||
|
Control());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
FATAL_UNSUPPORTED_OPCODE(opcode);
|
FATAL_UNSUPPORTED_OPCODE(opcode);
|
||||||
}
|
}
|
||||||
|
@ -356,6 +356,11 @@ void WasmThrowDescriptor::InitializePlatformSpecific(
|
|||||||
DefaultInitializePlatformSpecific(data, kParameterCount);
|
DefaultInitializePlatformSpecific(data, kParameterCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WasmAtomicWakeDescriptor::InitializePlatformSpecific(
|
||||||
|
CallInterfaceDescriptorData* data) {
|
||||||
|
DefaultInitializePlatformSpecific(data, kParameterCount);
|
||||||
|
}
|
||||||
|
|
||||||
void CloneObjectWithVectorDescriptor::InitializePlatformSpecific(
|
void CloneObjectWithVectorDescriptor::InitializePlatformSpecific(
|
||||||
CallInterfaceDescriptorData* data) {
|
CallInterfaceDescriptorData* data) {
|
||||||
DefaultInitializePlatformSpecific(data, kParameterCount);
|
DefaultInitializePlatformSpecific(data, kParameterCount);
|
||||||
|
@ -76,6 +76,7 @@ namespace internal {
|
|||||||
V(RunMicrotasks) \
|
V(RunMicrotasks) \
|
||||||
V(WasmMemoryGrow) \
|
V(WasmMemoryGrow) \
|
||||||
V(WasmThrow) \
|
V(WasmThrow) \
|
||||||
|
V(WasmAtomicWake) \
|
||||||
V(CloneObjectWithVector) \
|
V(CloneObjectWithVector) \
|
||||||
BUILTIN_LIST_TFS(V)
|
BUILTIN_LIST_TFS(V)
|
||||||
|
|
||||||
@ -1104,6 +1105,15 @@ class WasmThrowDescriptor final : public CallInterfaceDescriptor {
|
|||||||
DECLARE_DESCRIPTOR(WasmThrowDescriptor, CallInterfaceDescriptor)
|
DECLARE_DESCRIPTOR(WasmThrowDescriptor, CallInterfaceDescriptor)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class WasmAtomicWakeDescriptor final : public CallInterfaceDescriptor {
|
||||||
|
public:
|
||||||
|
DEFINE_PARAMETERS_NO_CONTEXT(kAddress, kCount)
|
||||||
|
DEFINE_RESULT_AND_PARAMETER_TYPES(MachineType::Uint32(), // result 1
|
||||||
|
MachineType::Uint32(), // kAddress
|
||||||
|
MachineType::Uint32()) // kCount
|
||||||
|
DECLARE_DESCRIPTOR(WasmAtomicWakeDescriptor, CallInterfaceDescriptor)
|
||||||
|
};
|
||||||
|
|
||||||
class CloneObjectWithVectorDescriptor final : public CallInterfaceDescriptor {
|
class CloneObjectWithVectorDescriptor final : public CallInterfaceDescriptor {
|
||||||
public:
|
public:
|
||||||
DEFINE_PARAMETERS(kSource, kFlags, kSlot, kVector)
|
DEFINE_PARAMETERS(kSource, kFlags, kSlot, kVector)
|
||||||
|
@ -26,7 +26,7 @@ namespace internal {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
Context* GetNativeContextFromWasmInstanceOnStackTop(Isolate* isolate) {
|
WasmInstanceObject* GetWasmInstanceOnStackTop(Isolate* isolate) {
|
||||||
StackFrameIterator it(isolate, isolate->thread_local_top());
|
StackFrameIterator it(isolate, isolate->thread_local_top());
|
||||||
// On top: C entry stub.
|
// On top: C entry stub.
|
||||||
DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
|
DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
|
||||||
@ -34,7 +34,11 @@ Context* GetNativeContextFromWasmInstanceOnStackTop(Isolate* isolate) {
|
|||||||
// Next: the wasm compiled frame.
|
// Next: the wasm compiled frame.
|
||||||
DCHECK(it.frame()->is_wasm_compiled());
|
DCHECK(it.frame()->is_wasm_compiled());
|
||||||
WasmCompiledFrame* frame = WasmCompiledFrame::cast(it.frame());
|
WasmCompiledFrame* frame = WasmCompiledFrame::cast(it.frame());
|
||||||
return frame->wasm_instance()->native_context();
|
return frame->wasm_instance();
|
||||||
|
}
|
||||||
|
|
||||||
|
Context* GetNativeContextFromWasmInstanceOnStackTop(Isolate* isolate) {
|
||||||
|
return GetWasmInstanceOnStackTop(isolate)->native_context();
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClearThreadInWasmScope {
|
class ClearThreadInWasmScope {
|
||||||
@ -253,5 +257,25 @@ RUNTIME_FUNCTION(Runtime_WasmCompileLazy) {
|
|||||||
return reinterpret_cast<Object*>(entrypoint);
|
return reinterpret_cast<Object*>(entrypoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RUNTIME_FUNCTION(Runtime_WasmAtomicWake) {
|
||||||
|
HandleScope scope(isolate);
|
||||||
|
DCHECK_EQ(3, args.length());
|
||||||
|
CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
|
||||||
|
CONVERT_NUMBER_CHECKED(uint32_t, address, Uint32, args[1]);
|
||||||
|
CONVERT_NUMBER_CHECKED(uint32_t, count, Uint32, args[2]);
|
||||||
|
|
||||||
|
DCHECK(instance->has_memory_object());
|
||||||
|
Handle<JSArrayBuffer> array_buffer(instance->memory_object()->array_buffer(),
|
||||||
|
isolate);
|
||||||
|
|
||||||
|
// Validation should have failed if the memory was not shared.
|
||||||
|
DCHECK(array_buffer->is_shared());
|
||||||
|
|
||||||
|
// Should have trapped if address was OOB
|
||||||
|
DCHECK_LT(address, array_buffer->byte_length());
|
||||||
|
|
||||||
|
return FutexEmulation::Wake(array_buffer, address, count);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
@ -531,6 +531,7 @@ namespace internal {
|
|||||||
#define FOR_EACH_INTRINSIC_WASM(F, I) \
|
#define FOR_EACH_INTRINSIC_WASM(F, I) \
|
||||||
F(ThrowWasmError, 1, 1) \
|
F(ThrowWasmError, 1, 1) \
|
||||||
F(ThrowWasmStackOverflow, 0, 1) \
|
F(ThrowWasmStackOverflow, 0, 1) \
|
||||||
|
F(WasmAtomicWake, 3, 1) \
|
||||||
F(WasmExceptionGetValues, 1, 1) \
|
F(WasmExceptionGetValues, 1, 1) \
|
||||||
F(WasmExceptionGetTag, 1, 1) \
|
F(WasmExceptionGetTag, 1, 1) \
|
||||||
F(WasmMemoryGrow, 2, 1) \
|
F(WasmMemoryGrow, 2, 1) \
|
||||||
|
@ -61,6 +61,7 @@ struct WasmException;
|
|||||||
(message)))
|
(message)))
|
||||||
|
|
||||||
#define ATOMIC_OP_LIST(V) \
|
#define ATOMIC_OP_LIST(V) \
|
||||||
|
V(AtomicWake, Uint32) \
|
||||||
V(I32AtomicLoad, Uint32) \
|
V(I32AtomicLoad, Uint32) \
|
||||||
V(I64AtomicLoad, Uint64) \
|
V(I64AtomicLoad, Uint64) \
|
||||||
V(I32AtomicLoad8U, Uint8) \
|
V(I32AtomicLoad8U, Uint8) \
|
||||||
|
@ -263,6 +263,7 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
|
|||||||
CASE_S1x16_OP(AllTrue, "all_true")
|
CASE_S1x16_OP(AllTrue, "all_true")
|
||||||
|
|
||||||
// Atomic operations.
|
// Atomic operations.
|
||||||
|
CASE_OP(AtomicWake, "atomic_wake")
|
||||||
CASE_UNSIGNED_ALL_OP(AtomicLoad, "atomic_load")
|
CASE_UNSIGNED_ALL_OP(AtomicLoad, "atomic_load")
|
||||||
CASE_UNSIGNED_ALL_OP(AtomicStore, "atomic_store")
|
CASE_UNSIGNED_ALL_OP(AtomicStore, "atomic_store")
|
||||||
CASE_UNSIGNED_ALL_OP(AtomicAdd, "atomic_add")
|
CASE_UNSIGNED_ALL_OP(AtomicAdd, "atomic_add")
|
||||||
|
@ -418,6 +418,7 @@ bool IsJSCompatibleSignature(const FunctionSig* sig);
|
|||||||
V(TableCopy, 0xfc0e, v_iii)
|
V(TableCopy, 0xfc0e, v_iii)
|
||||||
|
|
||||||
#define FOREACH_ATOMIC_OPCODE(V) \
|
#define FOREACH_ATOMIC_OPCODE(V) \
|
||||||
|
V(AtomicWake, 0xfe00, i_ii) \
|
||||||
V(I32AtomicLoad, 0xfe10, i_i) \
|
V(I32AtomicLoad, 0xfe10, i_i) \
|
||||||
V(I64AtomicLoad, 0xfe11, l_i) \
|
V(I64AtomicLoad, 0xfe11, l_i) \
|
||||||
V(I32AtomicLoad8U, 0xfe12, i_i) \
|
V(I32AtomicLoad8U, 0xfe12, i_i) \
|
||||||
|
141
test/mjsunit/wasm/futex.js
Normal file
141
test/mjsunit/wasm/futex.js
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
// Copyright 2018 the V8 project authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
// Flags: --allow-natives-syntax --harmony-sharedarraybuffer
|
||||||
|
// Flags: --experimental-wasm-threads
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
load("test/mjsunit/wasm/wasm-constants.js");
|
||||||
|
load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||||
|
|
||||||
|
function WasmAtomicWakeFunction(memory, offset, index, num) {
|
||||||
|
let builder = new WasmModuleBuilder();
|
||||||
|
builder.addImportedMemory("m", "memory", 0, 20, "shared");
|
||||||
|
builder.addFunction("main", kSig_i_ii)
|
||||||
|
.addBody([
|
||||||
|
kExprGetLocal, 0,
|
||||||
|
kExprGetLocal, 1,
|
||||||
|
kAtomicPrefix,
|
||||||
|
kExprAtomicWake, /* alignment */ 0, offset])
|
||||||
|
.exportAs("main");
|
||||||
|
|
||||||
|
// Instantiate module, get function exports
|
||||||
|
let module = new WebAssembly.Module(builder.toBuffer());
|
||||||
|
let instance = new WebAssembly.Instance(module, {m: {memory}});
|
||||||
|
return instance.exports.main(index, num);
|
||||||
|
}
|
||||||
|
|
||||||
|
(function TestInvalidIndex() {
|
||||||
|
let memory = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
|
||||||
|
|
||||||
|
// Valid indexes are 0-65535 (1 page).
|
||||||
|
[-2, 65536, 0xffffffff].forEach(function(invalidIndex) {
|
||||||
|
assertThrows(function() {
|
||||||
|
WasmAtomicWakeFunction(memory, 0, invalidIndex, -1);
|
||||||
|
}, Error);
|
||||||
|
assertThrows(function() {
|
||||||
|
WasmAtomicWakeFunction(memory, invalidIndex, 0, -1);
|
||||||
|
}, Error);
|
||||||
|
assertThrows(function() {
|
||||||
|
WasmAtomicWakeFunction(memory, invalidIndex/2, invalidIndex/2, -1);
|
||||||
|
}, Error);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function TestWakeCounts() {
|
||||||
|
let memory = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
|
||||||
|
|
||||||
|
[-1, 0, 4, 100, 0xffffffff].forEach(function(count) {
|
||||||
|
WasmAtomicWakeFunction(memory, 0, 0, count);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
//// WORKER ONLY TESTS
|
||||||
|
|
||||||
|
if (this.Worker) {
|
||||||
|
let wasm_wake_adapter = (memory, offset, index, count) => {
|
||||||
|
return WasmAtomicWakeFunction(memory, offset, index, count);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wait adapter string that can be passed as a parameter to TestWaitWake to generate
|
||||||
|
// custom worker script
|
||||||
|
let js_wait_adapter = `(memory, offset, index, val) => {
|
||||||
|
let i32a = new Int32Array(memory.buffer, offset);
|
||||||
|
let res = Atomics.wait(i32a, index/4, val);
|
||||||
|
if (res == "ok") return 0;
|
||||||
|
if (res == "not-equal") return 1;
|
||||||
|
return 2;
|
||||||
|
}`
|
||||||
|
|
||||||
|
let TestWaitWake = function(wait_adapter, wake_adapter, num_workers, num_workers_wake) {
|
||||||
|
let memory = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
|
||||||
|
let i32a = new Int32Array(memory.buffer);
|
||||||
|
// SAB values:
|
||||||
|
// memory[id*4], where id in range [0, num_workers]:
|
||||||
|
// 0 => Worker |id| is still waiting on the futex
|
||||||
|
// 1 => Worker |id| is not waiting on futex, but has not be reaped by the
|
||||||
|
// main thread.
|
||||||
|
// 2 => Worker |id| has been reaped.
|
||||||
|
//
|
||||||
|
// memory[num_workers*4]:
|
||||||
|
// always 0. Each worker is waiting on this index.
|
||||||
|
|
||||||
|
let workerScript =
|
||||||
|
`onmessage = function(msg) {
|
||||||
|
let id = msg.id;
|
||||||
|
let memory = msg.memory;
|
||||||
|
let i32a = new Int32Array(memory.buffer);
|
||||||
|
let wait_adapter = eval(msg.wait_adapter);
|
||||||
|
let result = wait_adapter(memory, 0, 4*${num_workers}, 0);
|
||||||
|
// Set i32a[id] to 1 to notify the main thread which workers were
|
||||||
|
// woken up.
|
||||||
|
Atomics.store(i32a, id, 1);
|
||||||
|
postMessage(result);
|
||||||
|
};`;
|
||||||
|
|
||||||
|
let workers = [];
|
||||||
|
for (let id = 0; id < num_workers; id++) {
|
||||||
|
workers[id] = new Worker(workerScript, {type: 'string'});
|
||||||
|
workers[id].postMessage({id, memory, wait_adapter});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spin until all workers are waiting on the futex.
|
||||||
|
while (%AtomicsNumWaitersForTesting(i32a, num_workers) != num_workers) {}
|
||||||
|
|
||||||
|
if (num_workers_wake < num_workers) {
|
||||||
|
assertEquals(num_workers_wake, wake_adapter(memory, 0, 4*num_workers, num_workers_wake));
|
||||||
|
} else {
|
||||||
|
assertEquals(num_workers, wake_adapter(memory, 0, 4*num_workers, num_workers_wake));
|
||||||
|
num_workers_wake = num_workers;
|
||||||
|
}
|
||||||
|
|
||||||
|
let wokenCount = 0;
|
||||||
|
while (wokenCount < num_workers_wake) {
|
||||||
|
for (let id = 0; id < num_workers; id++) {
|
||||||
|
// Look for workers that have not yet been reaped. Set i32a[id] to 2
|
||||||
|
// when they've been processed so we don't look at them again.
|
||||||
|
if (Atomics.compareExchange(i32a, id, 1, 2) == 1) {
|
||||||
|
assertEquals(0, workers[id].getMessage());
|
||||||
|
workers[id].terminate();
|
||||||
|
wokenCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(num_workers - num_workers_wake,
|
||||||
|
%AtomicsNumWaitersForTesting(i32a, num_workers));
|
||||||
|
|
||||||
|
// Finally wake and kill all workers.
|
||||||
|
wake_adapter(memory, 0, 4*num_workers, num_workers_wake)
|
||||||
|
for (let id = 0; id < num_workers; id++) {
|
||||||
|
workers[id].terminate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TestWaitWake(js_wait_adapter, wasm_wake_adapter, 1, 1);
|
||||||
|
TestWaitWake(js_wait_adapter, wasm_wake_adapter, 4, 4);
|
||||||
|
TestWaitWake(js_wait_adapter, wasm_wake_adapter, 4, 3);
|
||||||
|
TestWaitWake(js_wait_adapter, wasm_wake_adapter, 3, 4);
|
||||||
|
}
|
@ -354,6 +354,7 @@ let kExprF64ReinterpretI64 = 0xbf;
|
|||||||
// Prefix opcodes
|
// Prefix opcodes
|
||||||
let kAtomicPrefix = 0xfe;
|
let kAtomicPrefix = 0xfe;
|
||||||
|
|
||||||
|
let kExprAtomicWake = 0x00;
|
||||||
let kExprI32AtomicLoad = 0x10;
|
let kExprI32AtomicLoad = 0x10;
|
||||||
let kExprI32AtomicLoad8U = 0x12;
|
let kExprI32AtomicLoad8U = 0x12;
|
||||||
let kExprI32AtomicLoad16U = 0x13;
|
let kExprI32AtomicLoad16U = 0x13;
|
||||||
|
Loading…
Reference in New Issue
Block a user