-
Notifications
You must be signed in to change notification settings - Fork 214
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
fix: Add #[avr_skip]
for bit shifts
#513
Conversation
This commit follows the same logic as: - rust-lang#462 - rust-lang#466 I've tested the changes by preparing a simple program: ```rust fn calc() -> ... { let x = hint::black_box(4u...); // 4u8, 4u16, 4u32, 4u64, 4u128 + signed let y = hint::black_box(1u32); // x >> y // x << y } fn main() -> ! { let dp = arduino_hal::Peripherals::take().unwrap(); let pins = arduino_hal::pins!(dp); let mut serial = arduino_hal::default_serial!(dp, pins, 57600); for b in calc().to_le_bytes() { _ = ufmt::uwrite!(&mut serial, "{} ", b); } _ = ufmt::uwriteln!(&mut serial, ""); loop { // } } ``` ... switching types & operators in `calc()`, and observing the results; what I ended up with was: ``` u32 << u32 - ok u64 << u32 - ok u128 << u32 - error (undefined reference to `__ashlti3') i32 >> u32 - ok i64 >> u32 - ok i128 >> u32 - error (undefined reference to `__ashrti3') u32 >> u32 - ok u64 >> u32 - ok u128 >> u32 - error (undefined reference to `__lshrti3') (where "ok" = compiles and returns correct results) ``` As with multiplication and division, so do in here 128-bit operations not work, because avr-gcc's standard library doesn't provide them (at the same time, requiring that specific calling convention, making it pretty difficult for compiler-builtins to jump in). I think 128-bit operations non-working on an 8-bit controller is an acceptable trade-off - :innocent: - and so the entire fix in here is just about skipping those functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds sensible to me.
I wrote AVRShiftExpand so you can ask me about it :) Regarding the bug here, I'm pretty sure this line is not correct: __ashldi3(a: u64, b: u32) -> u64 { For AVR, |
Somewhat unrelated, but is there any reason why Rust uses the Rust clone of compiler-rt instead of the original project for AVR? It would avoid a lot of duplicate effort: @benshi001 and me have been working on getting compiler-rt fully supported on AVR. |
I might be not up to par in terminology -- what in this case would be |
So compiler-builtins is essentially a Rust clone of compiler-rt (a LLVM project). I wonder whether it wouldn't be better to use compiler-rt (instead of compiler-builtins) for AVR?
I guess compiler-butiltins is generally preferred in Rust because it is also written in Rust. My personal opinion is that it doesn't make much sense to rewrite this kind of thing in Rust because it's so low level and ABI heavy. But clearly at least some people disagree otherwise compiler-builtins wouldn't exist. For AVR in particular I wonder whether it wouldn't be better to use compiler-rt instead and focus on improving it for everybody instead of duplicating the effort in Rust. For context: I maintain TinyGo and I've switched away from the GNU toolchain (libgcc, etc) entirely. Most parts of compiler-rt work fine in my testing - including float32 and float64 builtins (but with the notable exception of 32-bit multiplication/division functions that are not terribly hard to implement in Go/Rust/C/whatever). I think the Rust project could do the same to avoid bugs like the one that this PR fixes. |
I see - yeah, I'd say that for AVR it would make more sense to go with LLVM's compiler-rt instead of compiler-builtins; on the top of my head I see no reason why that wouldn't work 🙂 |
Of the top of my head I know of several reasons why we are not using compiler-rt directly:
With that said, we do have a build-time option to use implementations from compiler-rt for some builtin functions where compiler-rt provides optimized assembly implementations. The only downside is that this is not used when building with |
Me too. This seems to work fine for me though? It may be needed to use the generic files instead of the optimized assembly in some cases though, but something like
This could nowadays be supported though Anyway, it's really just a suggestion. I'm not really working with Rust. |
Same story as always, i.e. ABI mismatch: - rust-lang#462 - rust-lang#466 - rust-lang#513 I've made sure the changes work by rendering a Mandelbrot fractal: ```rust #[arduino_hal::entry] fn main() -> ! { let dp = arduino_hal::Peripherals::take().unwrap(); let pins = arduino_hal::pins!(dp); let mut serial = arduino_hal::default_serial!(dp, pins, 57600); mandelbrot(&mut serial, 60, 40, -2.05, -1.12, 0.47, 1.12, 100); loop { // } } fn mandelbrot<T>( output: &mut T, viewport_width: i64, viewport_height: i64, x1: f32, y1: f32, x2: f32, y2: f32, max_iterations: i64, ) where T: uWrite, { for viewport_y in 0..viewport_height { let y0 = y1 + (y2 - y1) * ((viewport_y as f32) / (viewport_height as f32)); for viewport_x in 0..viewport_width { let x0 = x1 + (x2 - x1) * ((viewport_x as f32) / (viewport_width as f32)); let mut x = 0.0; let mut y = 0.0; let mut iterations = max_iterations; while x * x + y * y <= 4.0 && iterations > 0 { let xtemp = x * x - y * y + x0; y = 2.0 * x * y + y0; x = xtemp; iterations -= 1; } let ch = "#%=-:,. " .chars() .nth((8.0 * ((iterations as f32) / (max_iterations as f32))) as _) .unwrap(); _ = ufmt::uwrite!(output, "{}", ch); } _ = ufmt::uwriteln!(output, ""); } } ``` ... where without avr_skips, the code printed an image full of only `#`. Note that because libgcc doesn't provide implementations for f64, using those (e.g. swapping f32 to f64 in the code above) will cause linking to fail: ``` undefined reference to `__divdf3' undefined reference to `__muldf3' undefined reference to `__gedf2' undefined reference to `__fixunsdfsi' undefined reference to `__gtdf2' ``` Ideally compiler-builtins could jump right in and provide those, but f64 also require a special calling convention which hasn't been yet exposed through LLVM. Note that because using 64-bit floats on an 8-bit target is a pretty niche thing to do, and because f64 floats don't work correctly anyway at the moment (due to this ABI mismatch), we're not actually breaking anything by skipping those functions, since any code that currently uses f64 on AVR works by accident. Closes rust-lang/rust#108489.
This commit follows the same logic as:
I've tested the changes by preparing a simple program:
... switching types & operators in
calc()
, and observing the results; what I ended up with was:As with multiplication and division, so do in here 128-bit operations not work, because avr-gcc's standard library doesn't provide them (at the same time, requiring that specific calling convention, making it pretty difficult for compiler-builtins to jump in).
I think 128-bit operations non-working on an 8-bit controller is an acceptable trade-off - 😇 - and so the entire fix in here is just about skipping those functions.
Related to: rust-lang/rust#106135