Skip to content

avr: implement unsigned division intrinsics #816

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

Merged
merged 1 commit into from
Apr 16, 2025

Conversation

tones111
Copy link
Contributor

This PR adds a missing AVR intrinsic for 8-bit unsigned division (see #711) and is based on the popular [long division](https://en.wikipedia.org/wiki/Divisio n_algorithm#Long_division) algorithm.

GCC uses a special calling convention for multiplication and division primitives, requiring a naked_asm implementation.

This implementation attempts to be an assembly port of the following Rust implementation of the long division algorithm.

pub fn long_div_u8(mut num_quo: u8, divisor: u8) -> (u8, u8) {
    let mut remainder = 0u8;
    for _ in 0..8u8 {
        let overflow;
        (num_quo, overflow) = num_quo.overflowing_add(num_quo);
        (remainder, _) = remainder.carrying_add(remainder, overflow);

        if let Some(rem) = remainder.checked_sub(divisor) {
            remainder = rem;
            num_quo += 1;
        }
    }
    (num_quo, remainder)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_long_div_u8() {
        for n in 0..=u8::MAX {
            for d in 1..=u8::MAX {
                assert_eq!(long_div_u8(n, d), (n / d, n % d));
            }
        }
    }

@tones111
Copy link
Contributor Author

Pinging @Patryk27 for testing assistance. As indicated in the description, I've validated the algorithm with cargo test on x86-64, however, I haven't executed the code on AVR hardware or equivalent simulator. Hopefully you can run it through your AVR math fuzzer.

@tones111 tones111 changed the title avr: __udivmodqi4 avr: implement unsigned division intrinsics Apr 13, 2025
@Patryk27
Copy link
Contributor

Ok, I've checked the code under avr-tester's testsuite and it seems to work, nice job!

One thing left to check is whether all AVRs support the instructions here (don't know by heart which ones belong to the base instruction set).

@tones111
Copy link
Contributor Author

Thanks for testing @Patryk27 .

I referenced Microchip's AVR ISA for the opcodes. I think we should be good since none of the instructions used are listed as "N/A" in the chapter 5 tables.

The gcc implementation uses macros to conditionally switch between mov and movw. They also switch between call and rcall in the signed functions. I would hope this could be implemented via an optimization pass, but that doesn't appear to be the case with current rustc.

If you're willing to mentor a bit I'd be interested in trying to help improve the AVR codegen (but don't have any LLVM-specific dev experience).

@Patryk27
Copy link
Contributor

Patryk27 commented Apr 16, 2025

If you're willing to mentor a bit I'd be interested in trying to help improve the AVR codegen (but don't have any LLVM-specific dev experience).

That'd be nice, but I think it's sort of gray area in this specific case - I'm pretty sure that asm!() blocks are supposed to be placed verbatim into the output binary, without performing any optimizations on them (otherwise the optimizer could just elide all asm!("nop"); for instance).

So even if there was an AVR peephole optimization pass within LLVM, I'm pretty sure it wouldn't / couldn't be activated in this case; if we want to handle it, we should probably do it explicitly in here (using #[cfg]).

Copy link
Contributor

@tgross35 tgross35 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I double checked the clobbers and they seem to line up for both the default calling convention and for reduced tiny. I didn't check the routines in depth but they look reasonable to me.

Thanks for getting this started!

@tgross35 tgross35 merged commit 614ab5e into rust-lang:master Apr 16, 2025
27 checks passed
@tones111 tones111 deleted the avr_udivmodqi4 branch April 17, 2025 15:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants