Skip to content

Commit

Permalink
Implement atomics wait/notify with C++20 runtime support
Browse files Browse the repository at this point in the history
  • Loading branch information
shravanrn committed Jul 30, 2023
1 parent 319f0c6 commit 165ee1e
Show file tree
Hide file tree
Showing 17 changed files with 739 additions and 581 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ jobs:
env:
USE_NINJA: "1"
CC: "clang" # used by the wasm2c tests
CXX: "clang++" # used by the wasm2c wait/notify runtime
WASM2C_CFLAGS: "-march=x86-64-v2 -fsanitize=address -DWASM_RT_USE_MMAP=0"
WASM2C_CXXFLAGS: "-march=x86-64-v2 -fsanitize=address -DWASM_RT_USE_MMAP=0"
steps:
- uses: actions/setup-python@v1
with:
Expand Down
24 changes: 22 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ project(WABT LANGUAGES C CXX VERSION 1.0.33)

include(GNUInstallDirs)

option(BUILD_WASM2C_THREAD_WAIT_NOTIFY "Include wasm2c runtime support for wasm2c's thread wait/notify API (Requires C++ 20)." OFF)

if (BUILD_WASM2C_THREAD_WAIT_NOTIFY)
set(CMAKE_CXX_STANDARD 20)
else()
set(CMAKE_CXX_STANDARD 17)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
Expand Down Expand Up @@ -224,6 +230,7 @@ function(sanitizer NAME FLAGS)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}" PARENT_SCOPE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}" PARENT_SCOPE)
set(WASM2C_CFLAGS "${WASM2C_CFLAGS} ${FLAGS}" PARENT_SCOPE)
set(WASM2C_CXXFLAGS "${WASM2C_CXXFLAGS} ${FLAGS}" PARENT_SCOPE)
endif ()
endfunction()
sanitizer(USE_ASAN "-fsanitize=address")
Expand Down Expand Up @@ -424,6 +431,11 @@ endif ()

if (HAVE_SETJMP_H)
set(WASM_RT_FILES "wasm2c/wasm-rt-impl.h" "wasm2c/wasm-rt-impl.c" "wasm2c/wasm-rt-exceptions-impl.c")
if (BUILD_WASM2C_THREAD_WAIT_NOTIFY)
set(WASM_RT_FILES ${WASM_RT_FILES} "wasm2c/wasm-rt-threads-impl.cpp")
else()
set(WASM_RT_FILES ${WASM_RT_FILES} "wasm2c/wasm-rt-threads-dummy-impl.c")
endif()

add_library(wasm-rt-impl STATIC ${WASM_RT_FILES})
add_library(wabt::wasm-rt-impl ALIAS wasm-rt-impl)
Expand All @@ -439,7 +451,7 @@ if (HAVE_SETJMP_H)
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
install(
FILES "wasm2c/wasm-rt.h" "wasm2c/wasm-rt-exceptions.h"
FILES "wasm2c/wasm-rt.h" "wasm2c/wasm-rt-threads.h" "wasm2c/wasm-rt-exceptions.h"
TYPE INCLUDE
COMPONENT wabt-development
)
Expand Down Expand Up @@ -651,10 +663,17 @@ if (BUILD_TESTS)
set(WASM2C_CFLAGS "${WASM2C_CFLAGS} -isysroot ${CMAKE_OSX_SYSROOT}")
endif ()

set(WASM2C_CXXFLAGS "${WASM2C_CFLAGS}")

if (DEFINED ENV{WASM2C_CFLAGS})
set(WASM2C_CFLAGS "${WASM2C_CFLAGS} $ENV{WASM2C_CFLAGS}")
endif ()


if (DEFINED ENV{WASM2C_CXXFLAGS})
set(WASM2C_CXXFLAGS "${WASM2C_CXXFLAGS} $ENV{WASM2C_CXXFLAGS}")
endif ()

set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)

Expand Down Expand Up @@ -718,7 +737,8 @@ if (BUILD_TESTS)
set(RUN_TESTS_PY ${WABT_SOURCE_DIR}/test/run-tests.py)

add_custom_target(run-tests
COMMAND ${CMAKE_COMMAND} -E env WASM2C_CC=${CMAKE_C_COMPILER} WASM2C_CFLAGS=${WASM2C_CFLAGS} ${Python3_EXECUTABLE} ${RUN_TESTS_PY} --bindir $<TARGET_FILE_DIR:wat2wasm>

COMMAND ${CMAKE_COMMAND} -E env WASM2C_CC=${CMAKE_C_COMPILER} WASM2C_CXX=${CMAKE_CXX_COMPILER} WASM2C_CFLAGS=${WASM2C_CFLAGS} WASM2C_CXXFLAGS=${WASM2C_CXXFLAGS} ${Python3_EXECUTABLE} ${RUN_TESTS_PY} --bindir $<TARGET_FILE_DIR:wat2wasm>
DEPENDS ${WABT_EXECUTABLES}
WORKING_DIRECTORY ${WABT_SOURCE_DIR}
USES_TERMINAL
Expand Down
50 changes: 48 additions & 2 deletions src/c-writer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,8 @@ class CWriter {
void Write(const AtomicStoreExpr& expr);
void Write(const AtomicRmwExpr& expr);
void Write(const AtomicRmwCmpxchgExpr& expr);
void Write(const AtomicWaitExpr& expr);
void Write(const AtomicNotifyExpr& expr);

size_t BeginTry(const TryExpr& tryexpr);
void WriteTryCatch(const TryExpr& tryexpr);
Expand Down Expand Up @@ -3593,8 +3595,14 @@ void CWriter::Write(const ExprList& exprs) {
break;
}

case ExprType::AtomicWait:
case ExprType::AtomicNotify:
case ExprType::AtomicWait: {
Write(*cast<AtomicWaitExpr>(&expr));
break;
}
case ExprType::AtomicNotify: {
Write(*cast<AtomicNotifyExpr>(&expr));
break;
}
case ExprType::ReturnCall:
case ExprType::ReturnCallIndirect:
case ExprType::CallRef:
Expand Down Expand Up @@ -5350,6 +5358,44 @@ void CWriter::Write(const AtomicRmwCmpxchgExpr& expr) {
PushType(result_type);
}

void CWriter::Write(const AtomicWaitExpr& expr) {
const char* func = nullptr;
// clang-format off
switch(expr.opcode) {
case Opcode::MemoryAtomicWait32: func = "memory_atomic_wait32"; break;
case Opcode::MemoryAtomicWait64: func = "memory_atomic_wait64"; break;
default:
WABT_UNREACHABLE;
}
// clang-format on

Memory* memory = module_->memories[module_->GetMemoryIndex(expr.memidx)];
Type result_type = expr.opcode.GetResultType();

Write(StackVar(2, result_type), " = ", func, "(",
ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", (u64)(",
StackVar(2), ")");
if (expr.offset != 0)
Write(" + ", expr.offset);
Write(", ", StackVar(1), ", ", StackVar(0), ");", Newline());
DropTypes(3);
PushType(result_type);
}

void CWriter::Write(const AtomicNotifyExpr& expr) {
Memory* memory = module_->memories[module_->GetMemoryIndex(expr.memidx)];
Type result_type = expr.opcode.GetResultType();

Write(StackVar(1, result_type), " = memory_atomic_notify(",
ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", (u64)(",
StackVar(1), ")");
if (expr.offset != 0)
Write(" + ", expr.offset);
Write(", ", StackVar(0), ");", Newline());
DropTypes(2);
PushType(result_type);
}

void CWriter::ReserveExportNames() {
for (const Export* export_ : module_->exports) {
ReserveExportName(export_->name);
Expand Down
98 changes: 97 additions & 1 deletion src/prebuilt/wasm2c_atomicops_source_declarations.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
const char* s_atomicops_source_declarations = R"w2c_template(#if defined(_MSC_VER)
const char* s_atomicops_source_declarations = R"w2c_template(#include "wasm-rt-threads.h"
)w2c_template"
R"w2c_template(
#if defined(_MSC_VER)
)w2c_template"
R"w2c_template(
#include <intrin.h>
Expand Down Expand Up @@ -435,4 +438,97 @@ R"w2c_template(DEFINE_ATOMIC_CMP_XCHG(i64_atomic_rmw32_cmpxchg_u, u64, u32);
)w2c_template"
R"w2c_template(DEFINE_ATOMIC_CMP_XCHG(i64_atomic_rmw_cmpxchg, u64, u64);
)w2c_template"
R"w2c_template(
/** total number of instances that are waiting on a notify. */
)w2c_template"
R"w2c_template(static uint32_t wait_instances = 0;
)w2c_template"
R"w2c_template(
static u32 memory_atomic_wait_helper(wasm_rt_memory_t* mem,
)w2c_template"
R"w2c_template( u64 addr,
)w2c_template"
R"w2c_template( s64 timeout) {
)w2c_template"
R"w2c_template( uint32_t prev_wait_instances = atomic_add_u32(&wait_instances, 1);
)w2c_template"
R"w2c_template( if (prev_wait_instances == UINT32_MAX - 1) {
)w2c_template"
R"w2c_template( TRAP(MAX_WAITERS);
)w2c_template"
R"w2c_template( }
)w2c_template"
R"w2c_template(
uint32_t ret = wasm_rt_wait_on_address((uintptr_t)&mem->data[addr], timeout);
)w2c_template"
R"w2c_template( atomic_sub_u32(&wait_instances, 1);
)w2c_template"
R"w2c_template( return ret;
)w2c_template"
R"w2c_template(}
)w2c_template"
R"w2c_template(
static u32 memory_atomic_wait32(wasm_rt_memory_t* mem,
)w2c_template"
R"w2c_template( u64 addr,
)w2c_template"
R"w2c_template( u32 initial,
)w2c_template"
R"w2c_template( s64 timeout) {
)w2c_template"
R"w2c_template( ATOMIC_ALIGNMENT_CHECK(addr, u32);
)w2c_template"
R"w2c_template( MEMCHECK(mem, addr, u32);
)w2c_template"
R"w2c_template(
if (i32_atomic_load(mem, addr) != initial) {
)w2c_template"
R"w2c_template( return 1; // initial value did not match
)w2c_template"
R"w2c_template( }
)w2c_template"
R"w2c_template(
return memory_atomic_wait_helper(mem, addr, timeout);
)w2c_template"
R"w2c_template(}
)w2c_template"
R"w2c_template(
static u32 memory_atomic_wait64(wasm_rt_memory_t* mem,
)w2c_template"
R"w2c_template( u64 addr,
)w2c_template"
R"w2c_template( u64 initial,
)w2c_template"
R"w2c_template( s64 timeout) {
)w2c_template"
R"w2c_template( ATOMIC_ALIGNMENT_CHECK(addr, u64);
)w2c_template"
R"w2c_template( MEMCHECK(mem, addr, u64);
)w2c_template"
R"w2c_template(
if (i32_atomic_load(mem, addr) != initial) {
)w2c_template"
R"w2c_template( return 1; // initial value did not match
)w2c_template"
R"w2c_template( }
)w2c_template"
R"w2c_template(
return memory_atomic_wait_helper(mem, addr, timeout);
)w2c_template"
R"w2c_template(}
)w2c_template"
R"w2c_template(
static u32 memory_atomic_notify(wasm_rt_memory_t* mem, u64 addr, u32 count) {
)w2c_template"
R"w2c_template( ATOMIC_ALIGNMENT_CHECK(addr, u32);
)w2c_template"
R"w2c_template( MEMCHECK(mem, addr, u32);
)w2c_template"
R"w2c_template(
uint32_t ret = wasm_rt_notify_at_address((uintptr_t)&mem->data[addr], count);
)w2c_template"
R"w2c_template( return ret;
)w2c_template"
R"w2c_template(}
)w2c_template"
;
54 changes: 54 additions & 0 deletions src/template/wasm2c_atomicops.declarations.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include "wasm-rt-threads.h"

#if defined(_MSC_VER)

#include <intrin.h>
Expand Down Expand Up @@ -236,3 +238,55 @@ DEFINE_ATOMIC_CMP_XCHG(i64_atomic_rmw8_cmpxchg_u, u64, u8);
DEFINE_ATOMIC_CMP_XCHG(i64_atomic_rmw16_cmpxchg_u, u64, u16);
DEFINE_ATOMIC_CMP_XCHG(i64_atomic_rmw32_cmpxchg_u, u64, u32);
DEFINE_ATOMIC_CMP_XCHG(i64_atomic_rmw_cmpxchg, u64, u64);

/** total number of instances that are waiting on a notify. */
static uint32_t wait_instances = 0;

static u32 memory_atomic_wait_helper(wasm_rt_memory_t* mem,
u64 addr,
s64 timeout) {
uint32_t prev_wait_instances = atomic_add_u32(&wait_instances, 1);
if (prev_wait_instances == UINT32_MAX - 1) {
TRAP(MAX_WAITERS);
}

uint32_t ret = wasm_rt_wait_on_address((uintptr_t)&mem->data[addr], timeout);
atomic_sub_u32(&wait_instances, 1);
return ret;
}

static u32 memory_atomic_wait32(wasm_rt_memory_t* mem,
u64 addr,
u32 initial,
s64 timeout) {
ATOMIC_ALIGNMENT_CHECK(addr, u32);
MEMCHECK(mem, addr, u32);

if (i32_atomic_load(mem, addr) != initial) {
return 1; // initial value did not match
}

return memory_atomic_wait_helper(mem, addr, timeout);
}

static u32 memory_atomic_wait64(wasm_rt_memory_t* mem,
u64 addr,
u64 initial,
s64 timeout) {
ATOMIC_ALIGNMENT_CHECK(addr, u64);
MEMCHECK(mem, addr, u64);

if (i32_atomic_load(mem, addr) != initial) {
return 1; // initial value did not match
}

return memory_atomic_wait_helper(mem, addr, timeout);
}

static u32 memory_atomic_notify(wasm_rt_memory_t* mem, u64 addr, u32 count) {
ATOMIC_ALIGNMENT_CHECK(addr, u32);
MEMCHECK(mem, addr, u32);

uint32_t ret = wasm_rt_notify_at_address((uintptr_t)&mem->data[addr], count);
return ret;
}
Loading

0 comments on commit 165ee1e

Please sign in to comment.