Open gefjon opened 1 year ago
Using clang without wasi-sdk, compiling C to wasm, I got the same problems.
After changing the return type to int32_t, and & 0b1, it works temporarily as expected.
WASM_IMPORT("btnp")
int32_t btnp(int32_t index, int32_t hold, int32_t period);
// somewhere else
if (btnp(BUTTON_CODE_P1_LEFT, TIC80_PARAM_IGNORE, TIC80_PARAM_IGNORE) & 0b1)
{
// ...
}
(maybe I should read the GAMEPAD memory directly, it is more stable)
I'm running TIC-80 1.0.2164 on an m1 MacBook Air, and looking at the sources for the most recent commit. I checked
git blame
, and it doesn't look like any relevant functions have changed between my build and the source I'm looking at.I'm building a game in Rust WASM. I'm using the Rust template from the repository, but I cranked the optimizations up to
opt-level = 3
, because I don't intend to access VRAM directly.I noticed some unexpected and unpredictable behavior when calling
btnp
. (I was always calling exactlybtnp(7, -1, -1)
, but I don't think it matters.) Depending on surrounding code,debug
vsrelease
build, and the phase of the moon, branches onbtnp
would sometimes be always-taken, whether or not the button in question was pressed. Printing the result of thebtnp
withtrace
would always print the result I expected. That screamed undefined behavior.Digging into rustc/llvm's codegen, I found that it was representing
bool
asi32
, since WASM doesn't have 8-bit integers, and that it was sometimes emitting several different encodings of branches on the result ofbtnp
, which had inconsistent handling of values other than0
or1
. Sometimes it would&
the low bit with1
, sometimes it would usei32.eqz
, and sometimes it would just use the return value directly. This is very much within rustc's purview; it reserves the right to assume that all booleans are either0
or1
.I changed the result type of
tic80::sys::btnp
tou32
, and printed its result, like:This printed:
I looked into the definition of
wasmtic_btnp
, which is:That got me suspicious, since WASM doesn't have
bool
, so how can WASM3 have them?The relevant macros are defined:
Note that
_sp
is auint64_t *
.So, as far as I can tell, what's happening is that
wasmtic_btnp
:uint64_t
on the stack to hold its return value.i32
, so it reads 4 bytes out of the reserved space, one of which is the proper boolean return value, and three of which are (locally) uninitialized.TL;DR:
WASM API functions which want to return a boolean should use
int32_t
as their return type, notbool
. Care must also be taken that the returned value is exactly either0
or1
, assuming consumer libraries want to treat the functions as returningbool
the way the Rust template definesbtnp
.