Skip to content

Commit

Permalink
WIP sign extension working
Browse files Browse the repository at this point in the history
  • Loading branch information
wzmuda committed Jan 13, 2025
1 parent 5dfd9ea commit e193ec6
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 10 deletions.
46 changes: 41 additions & 5 deletions compiler-rt/src/alu/smul_with_overflow.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@ use crate::utils::{assert_fits_in_type};
use crate::alu::shl::shl;
use core::num::traits::{BitSize, Bounded, OverflowingMul};

fn check_bits(num: u128, bit_pos: u128, v: bool) -> bool {
if bit_pos == 0 || bit_pos >= 128 {
return false;
}

let mask = ~(shl::<u128>(1, bit_pos) - 1);
let masked_bits = num & mask;

if v {
masked_bits == mask
} else {
masked_bits == 0
}
}

// Perform the `smul_with_overflow` operation.
//
// This is a generic implementation for every data type. Its specialized version
Expand Down Expand Up @@ -32,22 +47,43 @@ fn smul_with_overflow<
assert_fits_in_type::<T>(lhs);
assert_fits_in_type::<T>(rhs);

let bit_size: u128 = BitSize::<T>::bits().into();
let sign_bit_mask = shl::<u128>(1, bit_size - 1);
let sign_ext_bit_mask = ~(sign_bit_mask - 1);
let lhs_sign_bit = (lhs & sign_bit_mask) != 0;
let lhs = if lhs_sign_bit {
println!("lhs extended from {:x} to {:x}", lhs, sign_ext_bit_mask | lhs);
sign_ext_bit_mask | lhs
} else {
lhs
};
let rhs_sign_bit = (rhs & sign_bit_mask) != 0;
let rhs = if rhs_sign_bit {
println!("rhs extended from {:x} to {:x}", rhs, sign_ext_bit_mask | rhs);
sign_ext_bit_mask | rhs
} else {
rhs
};

// Due to Cairo's limitation in iN<->uN casting and not enough operations implemented on iN,
// we cannot do the multiplication in T. Do the multiplication in u128 and detect overflow
// manually.
// The overflow flag returned by overflowing_mul() done on u128 is irrelevant if T is a shorter
// type.
let (result, overflow) = lhs.overflowing_mul(rhs);
let truncated_result = result & Bounded::<T>::MAX.into();

let result_sign_bit = (result & sign_bit_mask) != 0;
let extra_bits_equal = check_bits(result, bit_size - 1, result_sign_bit);
println!("result {:x} (sign {} extra_bits_equal {}) overflow {}", result, result_sign_bit, extra_bits_equal, overflow);
if overflow && extra_bits_equal {
return (truncated_result, false);
}

// Make sure the result is limited only to the bit width of the concrete type.
let truncated_result = result & Bounded::<T>::MAX.into();
// Manual overflow detection.
#[cairofmt::skip]
let overflow = overflow | {
let bit_size: u128 = BitSize::<T>::bits().into();
let sign_bit_mask = shl::<u128>(1, bit_size - 1);
let lhs_sign_bit = (lhs & sign_bit_mask) != 0;
let rhs_sign_bit = (rhs & sign_bit_mask) != 0;
let result_sign_bit = (result & sign_bit_mask) != 0;

// Overflow occurs if:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ mod tests {
use super::__llvm_smul_with_overflow_i8_i8;
use crate::alu::test_case::TestCaseTwoArgsTwoExpected;
#[cairofmt::skip]
pub const test_cases: [TestCaseTwoArgsTwoExpected; 259] = [
pub const test_cases: [TestCaseTwoArgsTwoExpected; 263] = [
// Due to Cairo's casting limitation, negative numbers are represented as bit patterns.

// Random test cases
Expand Down Expand Up @@ -64,10 +64,10 @@ mod tests {
TestCaseTwoArgsTwoExpected{lhs: 37, rhs: 71, expected: (67, true)},
TestCaseTwoArgsTwoExpected{lhs: 102, rhs: 47, expected: (186, true)},
TestCaseTwoArgsTwoExpected{lhs: 94, rhs: 119, expected: (178, true)},
// TestCaseTwoArgsTwoExpected{lhs: 4, rhs: 0b11111100, expected: (240, false)}, // rhs = -4
TestCaseTwoArgsTwoExpected{lhs: 4, rhs: 0b11111100, expected: (240, false)}, // rhs = -4
TestCaseTwoArgsTwoExpected{lhs: 27, rhs: 82, expected: (166, true)},
TestCaseTwoArgsTwoExpected{lhs: 30, rhs: 69, expected: (22, true)},
// TestCaseTwoArgsTwoExpected{lhs: 0b10001100, rhs: 0b11111111, expected: (116, false)}, // lhs = -116 rhs = -1
TestCaseTwoArgsTwoExpected{lhs: 0b10001100, rhs: 0b11111111, expected: (116, false)}, // lhs = -116 rhs = -1
TestCaseTwoArgsTwoExpected{lhs: 81, rhs: 42, expected: (74, true)},
TestCaseTwoArgsTwoExpected{lhs: 0b10001101, rhs: 0b10101001, expected: (21, true)}, // lhs = -115 rhs = -87
TestCaseTwoArgsTwoExpected{lhs: 124, rhs: 28, expected: (144, true)},
Expand Down Expand Up @@ -106,14 +106,14 @@ mod tests {
TestCaseTwoArgsTwoExpected{lhs: 92, rhs: 0b11010000, expected: (192, true)}, // rhs = -48
TestCaseTwoArgsTwoExpected{lhs: 0b11110100, rhs: 0b10111000, expected: (96, true)}, // lhs = -12 rhs = -72
TestCaseTwoArgsTwoExpected{lhs: 0b10000110, rhs: 64, expected: (128, true)}, // lhs = -122
// TestCaseTwoArgsTwoExpected{lhs: 0b11111100, rhs: 0b11111000, expected: (32, false)}, // lhs = -4 rhs = -8
TestCaseTwoArgsTwoExpected{lhs: 0b11111100, rhs: 0b11111000, expected: (32, false)}, // lhs = -4 rhs = -8
TestCaseTwoArgsTwoExpected{lhs: 79, rhs: 0b11001011, expected: (165, true)}, // rhs = -53
TestCaseTwoArgsTwoExpected{lhs: 0b11110100, rhs: 48, expected: (192, true)}, // lhs = -12
TestCaseTwoArgsTwoExpected{lhs: 24, rhs: 0b10101010, expected: (240, true)}, // rhs = -86
TestCaseTwoArgsTwoExpected{lhs: 33, rhs: 0b11000010, expected: (2, true)}, // rhs = -62
TestCaseTwoArgsTwoExpected{lhs: 0b10101011, rhs: 120, expected: (40, true)}, // lhs = -85
TestCaseTwoArgsTwoExpected{lhs: 0b10000110, rhs: 31, expected: (58, true)}, // lhs = -122
// TestCaseTwoArgsTwoExpected{lhs: 0b11110001, rhs: 6, expected: (166, false)}, // lhs = -15
TestCaseTwoArgsTwoExpected{lhs: 0b11110001, rhs: 6, expected: (166, false)}, // lhs = -15
TestCaseTwoArgsTwoExpected{lhs: 0b11100101, rhs: 103, expected: (35, true)}, // lhs = -27
TestCaseTwoArgsTwoExpected{lhs: 60, rhs: 0b10110111, expected: (228, true)}, // rhs = -73
TestCaseTwoArgsTwoExpected{lhs: 0b10011000, rhs: 23, expected: (168, true)}, // lhs = -104
Expand Down

0 comments on commit e193ec6

Please sign in to comment.