Skip to content

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 StgInts 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.

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information