Begin work on Atomics for wasm32 (wait and notify intrinsics)

This commit is contained in:
gingerBill
2022-05-21 12:58:48 +01:00
parent 9eb4cbcbd2
commit e48f41165c
7 changed files with 224 additions and 9 deletions
+9
View File
@@ -190,6 +190,15 @@ constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
wasm_memory_size :: proc(index: uintptr) -> int ---
// `timeout_ns` is maximum number of nanoseconds the calling thread will be blocked for
// A negative value will be blocked forever
// Return value:
// 0 - indicates that the thread blocked and then was woken up
// 1 - the loaded value from `ptr` did not match `expected`, the thread did not block
// 2 - the thread blocked, but the timeout
wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -> u32 ---
wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) ---
// Darwin targets only
objc_object :: struct{}
+36
View File
@@ -0,0 +1,36 @@
//+private
//+build wasm32
package sync
import "core:intrinsics"
import "core:time"
_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1)
return s != 0
}
_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, i64(duration))
return s != 0
}
_futex_signal :: proc(f: ^Futex) {
loop: for {
s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1)
if s >= 1 {
return
}
}
}
_futex_broadcast :: proc(f: ^Futex) {
loop: for {
s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), ~u32(0))
if s >= 0 {
return
}
}
}
+8
View File
@@ -0,0 +1,8 @@
//+private
//+build wasm32
package sync
_current_thread_id :: proc "contextless" () -> int {
// TODO(bill): _current_thread_id for wasm32
return 0
}
+93
View File
@@ -4473,6 +4473,99 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
break;
case BuiltinProc_wasm_memory_atomic_wait32:
{
if (!is_arch_wasm()) {
error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name));
return false;
}
Operand ptr = {};
Operand expected = {};
Operand timeout = {};
check_expr(c, &ptr, ce->args[0]); if (ptr.mode == Addressing_Invalid) return false;
check_expr(c, &expected, ce->args[1]); if (expected.mode == Addressing_Invalid) return false;
check_expr(c, &timeout, ce->args[2]); if (timeout.mode == Addressing_Invalid) return false;
Type *t_u32_ptr = alloc_type_pointer(t_u32);
convert_to_typed(c, &ptr, t_u32_ptr); if (ptr.mode == Addressing_Invalid) return false;
convert_to_typed(c, &expected, t_u32); if (expected.mode == Addressing_Invalid) return false;
convert_to_typed(c, &timeout, t_i64); if (timeout.mode == Addressing_Invalid) return false;
if (!is_operand_value(ptr) || !check_is_assignable_to(c, &ptr, t_u32_ptr)) {
gbString e = expr_to_string(ptr.expr);
gbString t = type_to_string(ptr.type);
error(ptr.expr, "'%.*s' expected ^u32 for the memory pointer, got '%s' of type %s", LIT(builtin_name), e, t);
gb_string_free(t);
gb_string_free(e);
return false;
}
if (!is_operand_value(expected) || !check_is_assignable_to(c, &expected, t_u32)) {
gbString e = expr_to_string(expected.expr);
gbString t = type_to_string(expected.type);
error(expected.expr, "'%.*s' expected u32 for the 'expected' value, got '%s' of type %s", LIT(builtin_name), e, t);
gb_string_free(t);
gb_string_free(e);
return false;
}
if (!is_operand_value(timeout) || !check_is_assignable_to(c, &timeout, t_i64)) {
gbString e = expr_to_string(timeout.expr);
gbString t = type_to_string(timeout.type);
error(timeout.expr, "'%.*s' expected i64 for the timeout, got '%s' of type %s", LIT(builtin_name), e, t);
gb_string_free(t);
gb_string_free(e);
return false;
}
operand->mode = Addressing_Value;
operand->type = t_u32;
operand->value = {};
break;
}
break;
case BuiltinProc_wasm_memory_atomic_notify32:
{
if (!is_arch_wasm()) {
error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name));
return false;
}
Operand ptr = {};
Operand waiters = {};
check_expr(c, &ptr, ce->args[0]); if (ptr.mode == Addressing_Invalid) return false;
check_expr(c, &waiters, ce->args[1]); if (waiters.mode == Addressing_Invalid) return false;
Type *t_u32_ptr = alloc_type_pointer(t_u32);
convert_to_typed(c, &ptr, t_u32_ptr); if (ptr.mode == Addressing_Invalid) return false;
convert_to_typed(c, &waiters, t_u32); if (waiters.mode == Addressing_Invalid) return false;
if (!is_operand_value(ptr) || !check_is_assignable_to(c, &ptr, t_u32_ptr)) {
gbString e = expr_to_string(ptr.expr);
gbString t = type_to_string(ptr.type);
error(ptr.expr, "'%.*s' expected ^u32 for the memory pointer, got '%s' of type %s", LIT(builtin_name), e, t);
gb_string_free(t);
gb_string_free(e);
return false;
}
if (!is_operand_value(waiters) || !check_is_assignable_to(c, &waiters, t_u32)) {
gbString e = expr_to_string(waiters.expr);
gbString t = type_to_string(waiters.type);
error(waiters.expr, "'%.*s' expected u32 for the 'waiters' value, got '%s' of type %s", LIT(builtin_name), e, t);
gb_string_free(t);
gb_string_free(e);
return false;
}
operand->mode = Addressing_Value;
operand->type = t_u32;
operand->value = {};
break;
}
break;
}
return true;
+4
View File
@@ -218,6 +218,8 @@ BuiltinProc__type_end,
BuiltinProc_wasm_memory_grow,
BuiltinProc_wasm_memory_size,
BuiltinProc_wasm_memory_atomic_wait32,
BuiltinProc_wasm_memory_atomic_notify32,
BuiltinProc_COUNT,
};
@@ -438,4 +440,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("wasm_memory_grow"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("wasm_memory_size"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("wasm_memory_atomic_wait32"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("wasm_memory_atomic_notify32"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
};
+45
View File
@@ -2187,6 +2187,51 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
return res;
}
case BuiltinProc_wasm_memory_atomic_wait32:
{
char const *name = "llvm.wasm.memory.atomic.wait32";
LLVMTypeRef types[1] = {
lb_type(p->module, t_u32),
};
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0]));
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
Type *t_u32_ptr = alloc_type_pointer(t_u32);
LLVMValueRef args[3] = {};
args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32_ptr).value;
args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value;
args[2] = lb_emit_conv(p, lb_build_expr(p, ce->args[2]), t_i64).value;
lbValue res = {};
res.type = tv.type;
res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
return res;
}
case BuiltinProc_wasm_memory_atomic_notify32:
{
char const *name = "llvm.wasm.memory.atomic.notify";
LLVMTypeRef types[1] = {
lb_type(p->module, t_u32),
};
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0]));
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
Type *t_u32_ptr = alloc_type_pointer(t_u32);
LLVMValueRef args[2] = {};
args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32_ptr).value;
args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value;
lbValue res = {};
res.type = tv.type;
res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
return res;
}
}
GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));
+29 -9
View File
@@ -1176,10 +1176,31 @@ class WebGLInterface {
function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) {
const MAX_INFO_CONSOLE_LINES = 512;
let infoConsoleLines = new Array();
let currentLine = "";
const addConsoleLine = (line) => {
if (!line) {
return;
}
if (!line.includes("\n")) {
currentLine = currentLine.concat(line);
} else {
let printLast = line.endsWith("\n");
let lines = line.split("\n");
for (let i = 0; i < lines.length-1; i++) {
let theLine = lines[i].trim("\r");
currentLine = currentLine.concat(line);
console.log(currentLine);
currentLine = "";
}
console.log(lines);
if (printLast) {
console.log(lines[lines.length-1]);
} else {
currentLine = currentLine.concat(lines[lines.length-1]);
}
}
if (line.endsWith("\n")) {
line = line.substring(0, line.length-1);
} else if (infoConsoleLines.length > 0) {
@@ -1191,16 +1212,15 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) {
if (infoConsoleLines.length > MAX_INFO_CONSOLE_LINES) {
infoConsoleLines.shift();
}
let data = "";
for (let i = 0; i < infoConsoleLines.length; i++) {
if (i != 0) {
data = data.concat("\n");
}
data = data.concat(infoConsoleLines[i]);
}
if (consoleElement) {
let data = "";
for (let i = 0; i < infoConsoleLines.length; i++) {
if (i != 0) {
data = data.concat("\n");
}
data = data.concat(infoConsoleLines[i]);
}
let info = consoleElement;
info.innerHTML = data;
info.scrollTop = info.scrollHeight;