Skip to content
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

compiler-rt: alu: add shl (shift left) and lshr (logical shift right) polyfills #93

Merged
merged 2 commits into from
Nov 21, 2024

Conversation

wzmuda
Copy link
Contributor

@wzmuda wzmuda commented Nov 20, 2024

Summary

Both polyfills have very similar logic so I push them in a single PR. They already follow the pattern I introduce in #92 - there's one generic implementation and the concrete implementations are just aliases existing to comply with our design.

This PR is based on #92 to make the diff show only the 2 commits that this PR actually adds. It's important to change the base back to main before merge and merge this PR after #92.

Details

Please check the logic of lsh and lshr functions in lsh.cairo and lshr.cairo files. Test cases are automatically generated (just like I did with and/or/xor) so they're most likely correct

Checklist

  • Code is formatted by Rustfmt or scarb fmt.
  • [ ] Documentation has been updated if necessary.

@wzmuda wzmuda requested a review from a team as a code owner November 20, 2024 00:12
@wzmuda wzmuda changed the base branch from main to wz/alu-bit-ops-generics November 20, 2024 08:08
Copy link
Collaborator

@iamrecursion iamrecursion left a comment

Choose a reason for hiding this comment

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

Looking good, but a few changes needed. I want to see the shift algorithms documented liberally for those reading.

assert_fits_in_type::<T>(shift);

let mut result = n;
#[cairofmt::skip]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why are we skipping this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because it breaks the loop in an ugly way:

    for _ in 0
        ..shift {
            let mut new_result = 0;
            let mut mask = Bounded::<T>::MAX.into() / 2 + 1;
            for _ in 0
                ..BitSize::<
                    T
                >::bits() {
                    if result & mask != 0 {
                        new_result = new_result | mask / 2; // Shift the mask right
                    }
                    mask = mask / 2;
                };
            result = new_result;
        };

vs

    #[cairofmt::skip]
    for _ in 0..shift {
        let mut new_result = 0;
        let mut mask = Bounded::<T>::MAX.into() / 2 + 1;
        for _ in 0..BitSize::<T>::bits() {
            if result & mask != 0 {
                new_result = new_result | mask / 2; // Shift the mask right
            }
            mask = mask / 2;
        };
        result = new_result;
    };

compiler-rt/src/alu/shl.cairo Show resolved Hide resolved
compiler-rt/src/alu/shl.cairo Show resolved Hide resolved
@wzmuda wzmuda force-pushed the wz/alu-bit-ops-generics branch from bb80742 to ad2ac36 Compare November 20, 2024 20:14
@wzmuda wzmuda force-pushed the wz/alu-bit-ops-shl branch 2 times, most recently from 652458b to 654b792 Compare November 20, 2024 21:30
@wzmuda
Copy link
Contributor Author

wzmuda commented Nov 20, 2024

I want to see the shift algorithms documented liberally for those reading.

Both documented and right shift heavily simplified.

@iamrecursion iamrecursion added the enhancement New feature or request label Nov 20, 2024
Copy link
Collaborator

@iamrecursion iamrecursion left a comment

Choose a reason for hiding this comment

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

Still some changes that need to be made!

compiler-rt/src/alu/lshr.cairo Outdated Show resolved Hide resolved
compiler-rt/src/alu/lshr.cairo Outdated Show resolved Hide resolved
compiler-rt/src/alu/lshr.cairo Outdated Show resolved Hide resolved
compiler-rt/src/alu/lshr.cairo Outdated Show resolved Hide resolved
compiler-rt/src/alu/shl.cairo Outdated Show resolved Hide resolved
compiler-rt/src/alu/shl.cairo Outdated Show resolved Hide resolved
compiler-rt/src/alu/shl.cairo Outdated Show resolved Hide resolved
Comment on lines 39 to 49
for _ in 0..shift {
// Initialize new_result to 0 for the current shift.
let mut new_result = 0;
// Initialize mask to 0b0000..1 (it will move to the left so we can check each bit).
let mut mask = 1;

// Iterate through each bit position of the integer.
for _ in 0..BitSize::<T>::bits() {
if result & mask != 0 {
// If the current bit is set, set the corresponding bit in new_result,
// but shifted one position to the left.
//
// mask.wrapping_add(mask) is essentially mask * 2 or mask << 1
// with the benefit of wrapping back at 0 when we reach the MSB.
new_result = new_result | mask.wrapping_add(mask);
}
mask = mask.wrapping_add(mask);
};
result = new_result;
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is a really convoluted way to do it. Can we not just do multiplications by 2?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If I go with this:

    for _ in 0..shift {
        result = result *2;
        result = result & Bounded::<T>::MAX.into();
    };

It almost works but fails for 128 bit values because of overflow. Shorter values get away with it because the calculations are done on u128 so there's enough room for overflows but in case of u128 there's no extra bits:

running 10 tests
test compiler_rt::alu::shl::shl_i128::tests::test_i128_shifts_zeros_2 ... ok (gas usage est.: 36136631)
test compiler_rt::alu::shl::shl_i128::tests::test_i128_shifts_ones_1 ... fail (gas usage est.: 362082)
test compiler_rt::alu::shl::shl_i128::tests::test_i128_shifts_zeros_1 ... ok (gas usage est.: 49146900)
test compiler_rt::alu::shl::shl_i128::tests::test_i128_shift_mixed ... fail (gas usage est.: 183026)
test compiler_rt::alu::shl::shl_i32::tests::test_i32 ... ok (gas usage est.: 28904173)
test compiler_rt::alu::shl::shl_i128::tests::test_i128_shifts_ones_2 ... fail (gas usage est.: 183026)
test compiler_rt::alu::shl::shl_i8::tests::test_i8 ... ok (gas usage est.: 9936313)
test compiler_rt::alu::shl::shl_i16::tests::test_i16 ... ok (gas usage est.: 15317749)
test compiler_rt::alu::shl::shl_i1::tests::test_i1 ... ok (gas usage est.: 1204618)
test compiler_rt::alu::shl::shl_i64::tests::test_i64 ... ok (gas usage est.: 67371229)
failures:
   compiler_rt::alu::shl::shl_i128::tests::test_i128_shifts_ones_1 - Panicked with 0x753132385f6d756c204f766572666c6f77 ('u128_mul Overflow').
   compiler_rt::alu::shl::shl_i128::tests::test_i128_shift_mixed - Panicked with 0x753132385f6d756c204f766572666c6f77 ('u128_mul Overflow').
   compiler_rt::alu::shl::shl_i128::tests::test_i128_shifts_ones_2 - Panicked with 0x753132385f6d756c204f766572666c6f77 ('u128_mul Overflow').

Copy link
Collaborator

Choose a reason for hiding this comment

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

It might be worth looking at separate impls then, because the mul approach is way more efficient for the cases where it works.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, so I'll rework it so the generic function does multiplication and the i128 does the complex stuff

@wzmuda wzmuda force-pushed the wz/alu-bit-ops-generics branch from 2d4660a to fa1b0bb Compare November 20, 2024 22:46
@wzmuda wzmuda force-pushed the wz/alu-bit-ops-shl branch from 28aad98 to 19e8aaf Compare November 20, 2024 23:00
Copy link
Collaborator

@iamrecursion iamrecursion left a comment

Choose a reason for hiding this comment

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

LGTM. Just the above thread about multiplication-based shifts.

@wzmuda wzmuda force-pushed the wz/alu-bit-ops-generics branch from fa1b0bb to d5f9ec2 Compare November 20, 2024 23:47
@wzmuda wzmuda force-pushed the wz/alu-bit-ops-shl branch from 19e8aaf to dfc2ab3 Compare November 20, 2024 23:54
Base automatically changed from wz/alu-bit-ops-generics to main November 21, 2024 00:00
@wzmuda wzmuda force-pushed the wz/alu-bit-ops-shl branch from dfc2ab3 to c15a151 Compare November 21, 2024 00:01
@wzmuda wzmuda merged commit 730fdb7 into main Nov 21, 2024
10 checks passed
@wzmuda wzmuda deleted the wz/alu-bit-ops-shl branch November 21, 2024 00:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants