-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[AVR/RegAllocGreedy] Allocator clobbers a live register #81911
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
It seems |
I've got some progress! -- I think this issue might be actually related to If we take a look at the input LLVM IR (the one directly generated by rustc), we'll see: %4 = call addrspace(0) i8 asm sideeffect alignstack "ldi ${0}, 123", "=&r,~{sreg},~{memory}"(), !srcloc !7
store i8 %4, ptr %canary, align 1
/* ... rest of the code ... */
%_7 = load i8, ptr %0, align 1, !noundef !3
%10 = icmp eq i8 %_7, 123 /* the actual comparison */ But for some reason, %5 = tail call addrspace(0) i8 asm sideeffect alignstack "ldi ${0}, 123", "=&r,~{sreg},~{memory}"() #4, !srcloc !3
/* ... rest of the code ... */
store i8 %5, ptr %1, align 1
%_7 = load i8, ptr %1, align 1, !noundef !5
call addrspace(1) void @llvm.lifetime.end.p0(i64 1, ptr nonnull %1)
%15 = icmp eq i8 %_7, 123 ... this might cause the codegen/regalloc/something to go mayhem and confuse the registers in-between. I'm attaching the input LLVM IR here (it's somewhat big, 94 KB, but Seizing the day, I've also simplified the case here - it corresponds to the following Rust code: Show#![feature(asm_experimental_arch)]
#![no_std]
#![no_main]
use core::arch::asm;
use core::hint::black_box;
use panic_halt as _;
#[arduino_hal::entry]
fn main() -> ! {
let canary: u8;
unsafe {
asm!("ldi {reg}, 123", reg = out(reg) canary);
}
black_box(magic(black_box(&[]), black_box(12)));
if black_box(canary) == 123 {
report_ok();
} else {
report_err();
}
loop {
//
}
}
fn magic(buf: &[u8], mut n: u64) -> &[u8] {
let mut i = 0;
loop {
black_box(n % 10);
n /= 10;
if n == 0 {
return if i > 0 { &[] } else { buf };
} else {
i += 1;
}
}
}
#[inline(never)]
fn report_ok() {
let dp = unsafe { arduino_hal::Peripherals::steal() };
let pins = arduino_hal::pins!(dp);
let mut serial = arduino_hal::default_serial!(dp, pins, 57600);
serial.write_byte(b'O');
serial.write_byte(b'\n');
}
#[inline(never)]
fn report_err() {
let dp = unsafe { arduino_hal::Peripherals::steal() };
let pins = arduino_hal::pins!(dp);
let mut serial = arduino_hal::default_serial!(dp, pins, 57600);
serial.write_byte(b'E');
serial.write_byte(b'\n');
} ... which currently prints |
Progress: disabling register coalescing ( |
Progress: found the part where regalloc does the illegal split! If you compile this with:
... you'll see this bit in logs:
In there, LLVM decides to materialize To make it less abstract, that's how the CFG looks like before this split: ... where bb0 contains the After this (almost as if It's even possible to force LLVM to tell
(edit: |
@aykevl, if you don't mind me pinging - you've used to do some llvm-avr stuff, maybe you've got an idea what can be happening in here? It doesn't seem to be AVR-specific this time, but I'm having hard time trying to reproduce it on other architectures -- and regalloc is not the easiest code to follow 👀 |
I've done some LLVM stuff but I haven't touched the register allocator. Looks like you're already further into it now that I have ever been. |
I have not looked deep into this issue, but there is another similar issue #55159. They both
|
I will have a look, maybe several weeks later when I have spare time. |
Thanks, @aykevl - your idea with looking into Following llvm-project/llvm/lib/Target/AVR/AVRInstrInfo.td Line 1390 in b19cfb9
It looks like removing that constraint (i.e. having just I'm yet to understand whether removing that constraint actually makes sense, but it's a step forward 😄 |
My guess would be that it only hides the bug (not actually solves it), but it's useful to know of course. |
I think it might be related - it looks like that's the same reason we've got this 👀 llvm-project/llvm/lib/Target/AVR/AVRInstrInfo.td Line 1421 in a99b912
Having that said, maybe the proper fix would be to create a separate opcode for the |
LDDRdPtrQ was marked as `earlyclobber`, which doesn't play well with GreedyRA (which can generate this instruction through `loadRegFromStackSlot()`). This seems to be the same case as: https://github.com/llvm/llvm-project/blob/a99b912c9b74f6ef91786b4dfbc25160c27d3b41/llvm/lib/Target/AVR/AVRInstrInfo.td#L1421 Closes #81911.
LDDRdPtrQ was marked as `earlyclobber`, which doesn't play well with GreedyRA (which can generate this instruction through `loadRegFromStackSlot()`). This seems to be the same case as: https://github.com/llvm/llvm-project/blob/a99b912c9b74f6ef91786b4dfbc25160c27d3b41/llvm/lib/Target/AVR/AVRInstrInfo.td#L1421 Closes llvm#81911. (cherry picked from commit 328cb9b)
Hi,
I've got some code which works on
-O0
and-O1 -regalloc=basic
, but gets miscompiled with-O1
(and even-O1 -opt-bisect-limit=0
!) - the issue downstream is:Rahix/avr-hal#505 (comment)
... which I've been able to somewhat minimize, down to:
Show LLVM IR
Now, the most important bit is this assignment:
... which doesn't get used up until the end of the program, when it's being tested on:
LLVM (understandably) decides to spill the result of that
in ..., 0x3f
into memory:... to later load back:
... and - even later - test on:
The direct issue is that this
ldd r22, Y+3
instruction is executed only in one code path (out of two) that leads to.LBB0_18
!(that is, in the generated assembly,
.LBB0_18
can be reached from two blocks, butldd
happens in only one of them)Code seems to be compiled correctly with
-O0
and-O1 -regalloc=basic
- the later in particular keepsldd
andtst
together instead of two different basic blocks:The text was updated successfully, but these errors were encountered: