mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-07 08:08:50 +00:00
Begin work on Atomics for wasm32 (wait and notify intrinsics)
This commit is contained in:
@@ -190,6 +190,15 @@ constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
|
|||||||
wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
|
wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
|
||||||
wasm_memory_size :: proc(index: 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
|
// Darwin targets only
|
||||||
objc_object :: struct{}
|
objc_object :: struct{}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
//+private
|
||||||
|
//+build wasm32
|
||||||
|
package sync
|
||||||
|
|
||||||
|
_current_thread_id :: proc "contextless" () -> int {
|
||||||
|
// TODO(bill): _current_thread_id for wasm32
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -4473,6 +4473,99 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
}
|
}
|
||||||
break;
|
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;
|
return true;
|
||||||
|
|||||||
@@ -218,6 +218,8 @@ BuiltinProc__type_end,
|
|||||||
|
|
||||||
BuiltinProc_wasm_memory_grow,
|
BuiltinProc_wasm_memory_grow,
|
||||||
BuiltinProc_wasm_memory_size,
|
BuiltinProc_wasm_memory_size,
|
||||||
|
BuiltinProc_wasm_memory_atomic_wait32,
|
||||||
|
BuiltinProc_wasm_memory_atomic_notify32,
|
||||||
|
|
||||||
BuiltinProc_COUNT,
|
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_grow"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
{STR_LIT("wasm_memory_size"), 1, 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},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2187,6 +2187,51 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
|
|||||||
return res;
|
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));
|
GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));
|
||||||
|
|||||||
Vendored
+29
-9
@@ -1176,10 +1176,31 @@ class WebGLInterface {
|
|||||||
function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) {
|
function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) {
|
||||||
const MAX_INFO_CONSOLE_LINES = 512;
|
const MAX_INFO_CONSOLE_LINES = 512;
|
||||||
let infoConsoleLines = new Array();
|
let infoConsoleLines = new Array();
|
||||||
|
let currentLine = "";
|
||||||
|
|
||||||
const addConsoleLine = (line) => {
|
const addConsoleLine = (line) => {
|
||||||
if (!line) {
|
if (!line) {
|
||||||
return;
|
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")) {
|
if (line.endsWith("\n")) {
|
||||||
line = line.substring(0, line.length-1);
|
line = line.substring(0, line.length-1);
|
||||||
} else if (infoConsoleLines.length > 0) {
|
} else if (infoConsoleLines.length > 0) {
|
||||||
@@ -1191,16 +1212,15 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) {
|
|||||||
if (infoConsoleLines.length > MAX_INFO_CONSOLE_LINES) {
|
if (infoConsoleLines.length > MAX_INFO_CONSOLE_LINES) {
|
||||||
infoConsoleLines.shift();
|
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) {
|
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;
|
let info = consoleElement;
|
||||||
info.innerHTML = data;
|
info.innerHTML = data;
|
||||||
info.scrollTop = info.scrollHeight;
|
info.scrollTop = info.scrollHeight;
|
||||||
|
|||||||
Reference in New Issue
Block a user