C backend handles signedness incorrectly
In !6934 (closed) I noticed and fixed a few issues in the C backend to do with signedness:
Unsigned operations can sometimes turn into signed operations if given signed arguments
Consider a program like
test() {
bits64 ret, a, b;
a = %neg(43 :: bits64);
b = %neg(0x443c70fa3e465120 :: bits64);
ret = %modu(a, b);
return (ret);
}
In this case both a
and b
will be StgInt
s in the generated C (since
MO_Neg
is a signed operation). However, if we provide these as operands to %
then we
will get signed modulus semantics, despite the user requesting unsigned semantics.
Therefore we must be careful to cast both arguments to StgWord
; currently we do not do this.
Sub-word signed results must be zero-extended
Consider a program like (from #20634):
test() {
bits64 ret;
bits8 a,b;
a = 0xe1 :: bits8; // == -31 signed
b = %quot(a, 3::bits8); // == -10 signed
ret = %zx64(a); // == 0xf6 unsigned
return (ret);
}
This program should return 0xf6 == 246
. However, we need to be very careful
with when dealing with the result of the %quot. For instance, one might be
tempted produce code like:
StgWord8 a = 0xe1U;
StgInt8 b = (StgInt8) a / (StgInt8) 0x3U;
StgWord ret = (W_) b;
However, this would be wrong; by widening b
directly from StgInt8
to
StgWord
we will get sign-extension semantics: rather than 0xf6 we will get
0xfffffffffffffff6
. To avoid this we must first cast b
back to StgWord8
,
ensuring that we get zero-extension semantics when we widen up to StgWord
.