[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: fossil build failure on riscv64



"Barak A. Pearlmutter" <barak@pearlmutter.net> writes:

> Okay, the problem was this code, when called with iVal being the most
> negative int:
> [...]    uVal = iVal * -1;
> [...]    *(--z) = (char)(48+(uVal%10));

Interesting. The multiplication is indeed (commonly understood to be)
UB, since it's producing a signed result outside the range of its type.

I was able to reproduce this with GCC 14.2.0 -O2 on rv64, but I was
curious as to why the fault was only showing up on RISC-V, so I had a
look at how the same code behaves on arm64 and amd64 too...

On all three architectures, GCC uses the same approach -- the % is
implemented by multiplying by a constant then right-shifting the result
to efficiently divide by 10, then subtracting that from the original
value to get the remainder. (For some reason it uses different
approximations of 1/10 on all three architectures, but that's OK.)

It blows up on rv64 because of the invariant that 32-bit values are
always sign-extended in 64-bit registers - the * -1 is implemented using
negw (i.e. subw from zero), which sign-extends its result. The
multiplication then gets a negative value as its input and produces a
negative result. On amd64 and arm64, the negation doesn't sign-extend,
so the result is positive and the loop proceeds as intended.

So it might be worth keeping an eye out for other instances of this bug
on RISC-V -- I bet a lot of number-formatting routines have the same
problem.

Thanks,

-- 
Adam Sampson <ats@offog.org>                         <http://offog.org/>


Reply to: