Rerumu / Wasynth

WebAssembly to Lua translation library and tool
https://discord.gg/JuekZtYmxx
GNU General Public License v3.0
139 stars 19 forks source link

Undefined behavior regarding bit32 truncation causes differences amongst x86 and other platforms #25

Closed schmatz closed 1 year ago

schmatz commented 2 years ago

We were noticing behavioral differences with our program transpiled with Wasynth running on Roblox on Windows and other platforms.

@vegorov-rbx investigated this; thanks a ton! His (paraphrased) findings are below:

In the Luau Wasynth runtime, to_u32 is defined as bit32.band.

https://github.com/Rerumu/Wasynth/blob/0394aa8895e7f0429342adb2a88b3b3d6cbcf67c/codegen/luau/runtime/runtime.lua#L3

In div.i32 and div.u32, the results of division is passed directly to to_u32.

https://github.com/Rerumu/Wasynth/blob/0394aa8895e7f0429342adb2a88b3b3d6cbcf67c/codegen/luau/runtime/runtime.lua#L78-L91

Luau 5.2, from which Luau was forked, specifies the following:

Unless otherwise stated, all functions accept numeric arguments in the range (-251,+251); each argument is normalized to the remainder of its division by 232 and truncated to an integer (in some unspecified way), so that its final value falls in the range [0,232 - 1].

The behavior varies based on platform:

Some differences can be observed in the Luau codebase. For example, here is how numbers are converted to unsigned integers on Windows x86.

This can be fixed by changing the runtime to truncate the numbers before passing to bit32.band. One potential way of doing this is using modf:

local i, _ = math.modf(lhs / rhs)
return to_u32(i)

The LuaJIT runtime also seems to have some truncation logic baked in which could be adopted for Luau as well.

I can confirm adding the math.modf fix resolves the behavioral differences on the Windows Roblox client for our transpiled program. I was unable to get a minimal CLI repro as Luau Windows CLI appears to be compiled to an x64 target and the bug affects x86. It should be pretty straightforward to repro this in Roblox if necessary by observing the rounding/truncation on a published place on the Windows Roblox client (note that the bug doesn't repro in Studio.)