Skip to content

Commit

Permalink
compiler-rt: alu: add division for i8
Browse files Browse the repository at this point in the history
Implement the following polyfills:
- `__llvm_udiv_i8_i8`
- `__llvm_sdiv_i8_i8`

Signed-off-by: Wojciech Zmuda <zmuda.w@gmail.com>
  • Loading branch information
wzmuda committed Jan 21, 2025
1 parent c51ca21 commit 0834fd7
Show file tree
Hide file tree
Showing 5 changed files with 688 additions and 0 deletions.
2 changes: 2 additions & 0 deletions compiler-rt/src/alu.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,7 @@ pub mod ssub_sat;
pub mod mul;
pub mod umul_with_overflow;
pub mod smul_with_overflow;
pub mod udiv;
pub mod sdiv;

mod test_case;
72 changes: 72 additions & 0 deletions compiler-rt/src/alu/sdiv.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
pub mod sdiv_i8;

use crate::utils::assert_fits_in_type;
use crate::alu::shl::shl;
use core::num::traits::{BitSize, Bounded, WrappingAdd};

// Perform the `sdiv` operation.
//
// This is a generic implementation for every data type. Its specialized versions
// are defined and tested in the sdiv/sdiv_<type>.cairo files.
fn sdiv<
T,
// The trait bounds are chosen so that:
//
// - BitSize<T>: we can determine the length of the data type in bits,
// - Bounded<T>: we can determine min and max value of the type,
// - TryInto<u128, T>, Into<T, u128> - we can convert the type from/to u128,
// - Destruct<T>: the type can be dropped as the result of the downcasting check.
//
// Overall these trait bounds allow any unsigned integer to be used as the concrete type.
impl TBitSize: BitSize<T>,
impl TBounded: Bounded<T>,
impl TTryInto: TryInto<u128, T>,
impl TInto: Into<T, u128>,
impl TDestruct: Destruct<T>,
>(
lhs: u128, rhs: u128,
) -> u128 {
// Make sure the value passed in the u128 arguments can fit in the concrete type.
assert_fits_in_type::<T>(lhs);
assert_fits_in_type::<T>(rhs);

// Check if operands and result are negative
let sign_mask = shl::<u128>(1, BitSize::<T>::bits().into() - 1);
let is_dividend_negative = (lhs & sign_mask) != 0;
let is_divisor_negative = (rhs & sign_mask) != 0;
let is_result_negative = is_dividend_negative ^ is_divisor_negative;

// A helper function to compute two's complement
let twos_complement = |x: u128| -> u128 {
(~x).wrapping_add(1)
};

// Get absolute value of operands
let abs_dividend = if is_dividend_negative {
twos_complement(lhs) & Bounded::<T>::MAX.into()
} else {
lhs
};
let abs_divisor = if is_divisor_negative {
twos_complement(rhs) & Bounded::<T>::MAX.into()
} else {
rhs
};

// Perform unsigned division and get quotient and remainder.
// Adjust quotient for floor division if result is negative and there's a remainder.
let quotient_unsigned = abs_dividend / abs_divisor;
let remainder = abs_dividend % abs_divisor;
let quotient_unsigned = if is_result_negative && remainder != 0 {
quotient_unsigned.wrapping_add(1)
} else {
quotient_unsigned
};

// Apply sign to the quotient
if is_result_negative {
twos_complement(quotient_unsigned)
} else {
quotient_unsigned
} & Bounded::<T>::MAX.into()
}
Loading

0 comments on commit 0834fd7

Please sign in to comment.