Description
I've created a test case that panics on msp430 in debug/release mode:
fn do_shift(val: &u64, amt: &u8) -> u64 {
// compiler_builtins::int::shift::__ashldi3(*val, *amt as u32)
*val << *amt
}
fn shift_test() {
let my_u64: u64 = 1;
let my_shift: u8 = 0;
if black_box(do_shift(black_box(&my_u64), black_box(&my_shift))) != 1 {
panic!()
}
}
If I call compiler_builtins::int::shift::__ashldi3(*val, *amt as u32)
directly, shift_test
does not panic. Relevant assembly code:
.Ltmp6:
.loc 1 46 18 is_stmt 1
mov 6(r12), r15
mov 4(r12), r14
mov 2(r12), r13
mov 0(r12), r12
mov.b 0(r11), r11
.Ltmp7:
.loc 1 38 5
mov r11, 0(r1)
clr 2(r1)
call #_ZN17compiler_builtins3int5shift9__ashldi317h573dd44357b45ab3E
However, if I let LLVM lower the shift to a builtin, shift_test
tends to panic:
.Ltmp7:
.loc 1 46 18 is_stmt 1
mov.b 0(r12), r12
.Ltmp8:
.loc 1 39 5
and #63, r12
mov r12, 0(r1)
.Ltmp9:
.loc 1 46 18
mov 0(r15), r12
mov 2(r15), r13
mov 4(r15), r14
mov 6(r15), r15
.Ltmp10:
.loc 1 39 5
call #__ashldi3
Notice that the call to __ashldi3
only sets up one 64-bit worth and a 16-bit int
's worth of argument. This is correct for __ashldi3
as specified in compiler-rt
. However, we call the compiler-builtins
version in both cases (compiled with -Zbuild-std=core
), which excepts a 32-bit int
's worth of argument. The second code snippet does not do this, and therefore when compiler_builtins::int::shift::__ashldi3
is called it reads garbage for the top 16-bits of the shl
argument. This causes subtle breakage and pain, possibly many instructions away from the call :).
AVR doesn't appear to have this problem because apparently there's an dedicated pass to ensuring that calls to __ashldi3
and friends aren't emitted into assembly files. MSP430 doesn't have such a pass. Which makes me confused as to why #513 was needed in the first place, but that's for another time.
This leaves msp430 as the only platform which would use at the very least the shift builtins where sizeof(int)
isn't 4. I'm not sure how to fix this:
- The code emission for LLVM is correct for C/C++ code.
- Changing MSP430 to a 32-bit int will never be accepted on the LLVM side.
- On the other hand, going through every single function that uses an
int
where this crate uses au32
and adding conditional compilation seems invasive. - On the other, other hand, I don't want to go the
build-std-features
route to disablecompiler_builtins
because I'm trying to remove nightly features from the msp430 backend, not add them. __ashldi3
does exist inlibgcc
:$ nm /opt/toolchains/lib/gcc/msp430-elf/9.3.1/libgcc.a 2> /dev/null | grep __ashldi3 00000000 T __ashldi3
- ... but I can't confirm that all affected builtins (ones using
int
args) are inlibgcc
for MSP430. I'd rather seecompiler-builtins
correctly support 16-bit int input arguments rather than hardcode a 32-bit one. Maybe adding anmsp430_expand
wrapper is possible- i.e. expand 16-bit ints to 32-bit before sending to__ashldi3
? That seems like it could be done in a semver-compat manner somehow?