-
Notifications
You must be signed in to change notification settings - Fork 226
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
Possible miscompilation in LLVM #573
Comments
Thanks for reporting! I've already got two other AVR miscompilations at hand, so I won't be able to analyze it right now, but I'll get back to you 🙂 |
Little update with an extra finding: turning off LTO (no ldd r14, Y+1 ; 0x01
ldd r15, Y+2 ; 0x02 Not sure if these are harmful or not... edit: in our larger project, from which we derived the minimal example, the instruction is generated even without LTO: ld r19, Y |
Those are most likely stack spills, i.e. when a function uses more data that can be kept within registers, the extra gets spilled onto stack using |
Friendly ping. Any chance of an update? 🙏 Should I perhaps report this elsewhere, too? To Rust or LLVM directly? |
Hi, no progress yet, still stuck on llvm/llvm-project#102936. If you have some spare time to investigate this issue more, you could try narrowing it down to a single *.ll file - that would be helpful for LLVM-people. |
Thanks for the update!
We'll look into it 🙂 |
Status: on newest LLVM, compiling this code crashes with:
That's because when loading data from stack, LLVM uses the I'll see if there's something we can do here - maybe instead of generating: ldd reg, Y+offset ... for larger offsets the codegen could do: addi Y, offset
ldd reg, Y+0
subi Y, offset tl;dr in order to load |
Ah, interesting. Thanks very much for looking into this! Would you say this is something I could/should look into? I've never worked on either LLVM specifically or compiler code in general, but I do like to think that I have a decent grasp on C++. And I suppose until this is fixed in LLVM, there's not much we can do to work around this, is there? |
I haven't worked on LLVM until once day I randomly got around to fixing AVR bugs, so this is a nice opportunity, if you're not prone to banging your head against the wall 😄 The instructions boil down to:
Having that said, yesterday I managed to fix that other bug and now I'm looking into this one, so don't feel forced to play around with LLVM on your own, it's always a mix of fun and frustration.
It looks like the underlying codegen bug is related to the object/stack size going above 62 bytes, so the only solution for now seems to be "avoid huge objects". Fortunately, once a fix lands in LLVM, it can be quickly cherry-picked to rustc's fork of LLVM, so once the fix is ready, the timeline to getting it into rustc is hours/days, not half a year. |
Got progress! The minimal offending *.ll is surprisingly small: define void @main(ptr %self) {
start:
%1 = getelementptr i8, ptr %self, i16 61
%2 = load i32, ptr %1, align 1
store i32 %2, ptr null, align 1
ret void
} (got it via Now, onto discovering what's going on here 🔍 |
Alright, it looks like the problem is within those two checks: Those functions are related to the "if offset is too high, use the alternative route" logic and they both are off by one - the maximum allowed offset should be 62, not 63 (which is what Adjusting the checks to So now lemme just prepare a proper LLVM test, pull request and let's keep the fingers crossed. |
Wow, thank you so much for the great work! |
Status: waiting for llvm/llvm-project#106993. |
Status: fixed in the newest toolchain 🙂 |
You absolute hero, thank you very much!! 🚀 |
I haven't had a chance to test on hardware yet, but using the latest nightly toolchain does still generate
Reading the comment here, I suppose those would be 8-bit loads, and therefore okay? I'll hopefully get a chance to test for real later this week. Just in case:
|
Yes, those are alright - that particular commit you linked is about limiting 16-bit loads, since those are expanded into a pair of: ldd reg1, Y+offset
ldd reg2, Y+offset+1 ... and so a 16-bit load can have a maximum offset of 62 (as that will make the second |
Okay, thanks for clarifying! I remembered my little test case here: https://github.com/tronje/llvm-avr-compiler-bug And this is indeed now fixed with the new toolchain. Thank you again! I'll close this now then. |
Hi, it's me again, the guy with the weird problems. This time, I believe I have a decent grasp on what's going wrong. It's documented with a minimal example and a README here: https://github.com/tronje/llvm-avr-compiler-bug
The short version is that under certain conditions, the
Y
register is used to load struct members into registers, as an optimization. This causes stack corruption (maybe?) and causes incorrect program behavior. I do believe I did a decent job explaining it in the README of the project I linked. The Rust code should be fairly self-explanatory as well.It does include some weird things, like extra booleans to get the struct to be a certain size, without which the bug does not occur.
Reasons I suspect the
Y
register to be the culprit:source
https://github.com/rust-lang/llvm-project/blob/rustc-1.80.0/llvm/lib/Target/AVR/AVRRegisterInfo.cpp#L80-L91
And whenever the example program is changed in a way that does not cause the bug to occur, the offending use of
Y
is also not included in the assembly.I hope it's okay to report this here; I realize this is just avr-hal, not LLVM 🙂 But I was hoping to get an opinion and perhaps some advice on how to proceed here. Thanks!
The text was updated successfully, but these errors were encountered: