Add -fno-strict-float-cast-overflow complation flag

LuaJIT checks whether a Lua number can be represented as an integer by
casting the number (`double`) to an `int32_t` and comparing the result
to the original. While this approach works in practice, it is
technically UB according to the C standard. Because it's UB, compilers
are free to make optimizations that may not preserve the original intent
of the code. Clang/LLVM, in particular, takes advantage of this.

Consider the following code:

```c
int32_t k = lj_num2int(n);
if (n == (lua_Number)k)
  ...
```

Clang short-circuit this to do basically

```c
if (n == trunc(n))
  ...
```

It can do this because it assumes that `n` is either representable as an
`int32_t` or the behavior is undefined, which allows it to compare the
truncated `n` directly without needing to convert `k` back to a
`double`.

We could implement a C-compliant saturating cast from floating point to
integer ourselves, but that awkward, and more useless code, especially
since most FPUs already handle such conversions natively with
saturation.

Alternatively, we could use intrinsics or inline asm to ensure the
correct saturating conversion is used.

But there's a simpler option: we can ask the compiler to handle this
correctly for us.

This commit adds the `-fno-strict-float-cast-overflow` compilation flag, which:
> With the ‘no-strict’ option, Clang will saturate towards the smallest
and largest representable integer values instead. NaNs will be converted
to zero.

GCC does not currently exploit this UB as aggressively, so it's not an
issue there. MSVC also appears unaffected, though it provides built-in
functions for saturating conversions, if someone wants to use them
directly
https://learn.microsoft.com/cpp/intrinsics/saturation-conversion-functions

This commit fixes assumptions that LuaJIT makes in the code.

Fixes: #1351
This commit is contained in:
Kacper Michajłow 2025-04-07 21:47:27 +02:00
parent e0a7ea8a92
commit 6c91ac9cec

View File

@ -302,6 +302,9 @@ endif
ifneq (,$(INSTALL_LJLIBD))
TARGET_XCFLAGS+= -DLUA_LJDIR=\"$(INSTALL_LJLIBD)\"
endif
ifeq (,$(shell $(TARGET_CC) -o /dev/null -c -x c /dev/null -fno-strict-float-cast-overflow 2>/dev/null || echo 1))
TARGET_XCFLAGS+= -fno-strict-float-cast-overflow
endif
##############################################################################
# Target system detection.