Aarch64 NCG doesn't always uphold the invariants mentioned in `Note [Signed arithmetic on AArch64]`
The Note describes this invariant:
-- We handle 16-and 8-bit values by using the 32-bit operations and
-- sign-/zero-extending operands and truncate results as necessary. For
-- simplicity we maintain the invariant that a register containing a
-- sub-word-size value always contains the zero-extended form of that value
-- in between operations.
However the fast path for addition with immediate doesn't uphold this. There we have:
CmmMachOp (MO_Add w) [(CmmReg reg), CmmLit (CmmInt n _)]
| n > 0 && n < 4096 -> return $ Any (intFormat w) (\d -> unitOL $ annExpr expr (ADD (OpReg w d) (OpReg w' r') (OpImm (ImmInteger n))))
-- TODO: 12bits lsl #12; e.g. lower 12 bits of n are 0; shift n >> 12, and set lsl to #12.
where w' = formatToWidth (cmmTypeFormat (cmmRegType reg))
r' = getRegisterReg plat reg
Which means expressions such as reg + 1
can overflow the high bits resulting in a violation of the invariant.
I wasn't (yet) able to make this go wrong on a larger scale, as we don't seem to really rely on this invariant much (and re-zero in most other places). Best I could do was this cmm:
high_bits(I16 x) {
A:
I16 y;
y = x + 1;
call foo(y);
}
Resulting in foo being called with a high bit potentially set depending on the initial value of x:
mov w18, w22
add w18, w18, #1
adrp x17, .Lblock_c4_info
add x17, x17, :lo12:.Lblock_c4_info
str x17, [ x20, -8 ]
mov x22, x18
sub x20, x20, #8
b foo
Maybe this could cause issues in mac where we are responsible for zeroing out the high bits. But I won't spend more time on what issues this can cause I will just fix it.
Edited by Andreas Klebinger