-
Notifications
You must be signed in to change notification settings - Fork 253
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__ashldi317h573dd44357b45ab3EHowever, 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 #__ashldi3Notice 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
intwhere this crate uses au32and adding conditional compilation seems invasive. - On the other, other hand, I don't want to go the
build-std-featuresroute to disablecompiler_builtinsbecause I'm trying to remove nightly features from the msp430 backend, not add them. __ashldi3does 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
intargs) are inlibgccfor MSP430. I'd rather seecompiler-builtinscorrectly support 16-bit int input arguments rather than hardcode a 32-bit one. Maybe adding anmsp430_expandwrapper 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?