## Cmm's lack of signedness information is problematic for some platforms

Currently Cmm has a very simple model of data types:

```
data CmmType
= CmmType CmmCat Width
data CmmCat -- "Category" (not exported)
= GcPtrCat -- GC pointer
| BitsCat -- Non-pointer
| FloatCat -- Float
| VecCat Length CmmCat -- Vector
```

Most things are either GC pointers (corresponding to Haskell's `BoxedRep`

) or "bits" (corresponding to the majority of the remaining `RuntimeRep`

s, including `IntRep`

, `Int32Rep`

, etc.). Local registers, literals, and other Cmm expressions are annotated with `CmmType`

s.

Machine operations (`MachOp`

s) are generally polymorphic over the width of its operand. For concreteness, here are the "types" of a few MachOps in a pseudo-Haskell syntax (see https://gitlab.haskell.org/bgamari/test-primops/-/blob/master/src/Expr.hs#L77 for a few more examples):

```
MO_Add :: forall (width :: Width). CmmExpr width -> CmmExpr width -> CmmExpr width
MO_S_Mul :: forall (width :: Width). CmmExpr width -> CmmExpr width -> CmmExpr width
MO_S_Gt :: forall (width :: Width). CmmExpr width -> CmmExpr width -> CmmExpr WordSize
```

The problems come when we start thinking about signed versus unsigned arithmetic. For instance, consider this Cmm program (from #20644) running on a 64-bit machine:

```
test(bits64 buffer)
bits8 a = bits8[buffer];
bits8 b = %quot(a, 2::bits8);
return (b);
}
```

Here we load an 8-bit number, `a`

, from a buffer. We then perform a signed division-by-2 on `a`

and return the result. Assuming the target machine has an 8-bit division operation this program is easy to compile. However, if we do *not* have sub-word arithmetic (like, e.g., AArch64) then things get much hard as we need to start worrying about how we filled the high bits of the value: we must ensure that the value is sign-extended before we perform the division. Under a naive implementation this means that before every arithmetic operation we must sign-extend all operands, growing the program size considerably.

Moreover, in some other cases we must also truncate results. For instance, consider this program:

```
test(bits64 buffer)
bits8 a = 0;
bits8 b = %not(a);
bits8 c = %shrl(b, 4::bits8);
return (c);
}
```

This program should return `0x0f`

. However, if we only have a word-size complement operation we would need to take care to truncate `b`

to 8-bits, lest the `shrl`

inappropriately shift ones into its result (yielding the result `0xff`

).