//! OE / XER[OV] / XER[SO] handling for integer arithmetic. //! //! PPC integer ops with the OE bit set update XER[OV] (overflow) and sticky-set //! XER[SO]. When OE is clear the instruction leaves XER untouched. Signed //! overflow is predicated on the operation width and operand signs per the //! PowerISA pseudocode. For 32-bit-word operations (`addw`, `mullw`, `divw`, //! `neg`, etc. — on PPC these all have `w` in the mnemonic in spec //! descriptions even when the assembler spells them without) the predicate //! uses the low 32 bits. For 64-bit operations (`add`, `mulld`, `divd`) the //! predicate uses the full 64 bits. use crate::context::PpcContext; #[inline] pub fn apply(ctx: &mut PpcContext, overflowed: bool) { if overflowed { ctx.xer_ov = 1; ctx.xer_so = 1; } else { ctx.xer_ov = 0; } } /// Signed addition overflow at width-64 (plain `add`, `addc`, `subf`, `subfc`). /// /// Predicate: same-sign inputs with opposite-sign result. /// For sub callers, rewrite as `a + b'` first (see `_sub`). #[inline] pub fn add_ov_64(a: u64, b: u64, result: u64) -> bool { ((!(a ^ b)) & (a ^ result)) >> 63 != 0 } /// Universal signed-overflow predicate for 64-bit arithmetic. /// /// Caller computes the mathematical (infinite-precision) signed sum as i128, /// plus the stored 64-bit result. Overflow iff the two disagree — i.e. the /// true value doesn't fit in i64. /// /// Use this for multi-term chains (`adde`, `addme`, `addze`, `subfe`, `subfme`, /// `subfze`) where the carry-in makes the bit-predicate above awkward. #[inline] pub fn sum_overflow_64(true_sum: i128, result: u64) -> bool { true_sum != (result as i64) as i128 } /// Signed subtraction: RT = b - a. Overflow iff opposite-sign inputs with /// result sign != b's sign. Equivalently, reduce to addition with `!a + 1`. #[inline] pub fn sub_ov_64(a: u64, b: u64, result: u64) -> bool { ((a ^ b) & (b ^ result)) >> 63 != 0 } /// Signed `addc`/`adde` chain overflow. Same rule as `add_ov_64` — the carry /// in doesn't alter the sign predicate directly because it's already folded /// into the stored result. #[inline] pub fn adde_ov_64(a: u64, b: u64, result: u64) -> bool { add_ov_64(a, b, result) } /// Signed 32-bit multiply overflow (`mullwo`): result fits in 32 bits signed /// iff bit 32 equals bits 33..63 of the 64-bit product. #[inline] pub fn mullw_ov(product: i64) -> bool { let lo = product as i32 as i64; lo != product } /// Signed 64-bit multiply overflow (`mulldo`). Detected via checked_mul. #[inline] pub fn mulld_ov(a: i64, b: i64) -> bool { a.checked_mul(b).is_none() } /// `divwo` / `divwuo` / `divdo` / `divduo` raise OV in two cases: /// * divisor is zero, or /// * signed division of `INT_MIN / -1` (quotient doesn't fit). #[inline] pub fn divw_ov_signed(ra: i32, rb: i32) -> bool { rb == 0 || (ra == i32::MIN && rb == -1) } #[inline] pub fn divw_ov_unsigned(rb: u32) -> bool { rb == 0 } #[inline] pub fn divd_ov_signed(ra: i64, rb: i64) -> bool { rb == 0 || (ra == i64::MIN && rb == -1) } #[inline] pub fn divd_ov_unsigned(rb: u64) -> bool { rb == 0 } /// `negx`: RT = -(RA). Overflow only when RA = INT_MIN (the negation doesn't fit). #[inline] pub fn neg_ov_64(ra: u64) -> bool { ra == 0x8000_0000_0000_0000 } #[cfg(test)] mod tests { use super::*; #[test] fn add_no_overflow() { assert!(!add_ov_64(1, 2, 3)); assert!(!add_ov_64(u64::MAX, 0, u64::MAX)); } #[test] fn add_positive_overflow() { // INT64_MAX + 1 = INT64_MIN — signed overflow let a = i64::MAX as u64; let b = 1u64; let r = a.wrapping_add(b); assert!(add_ov_64(a, b, r)); } #[test] fn add_negative_overflow() { // INT64_MIN + -1 = INT64_MAX — signed overflow let a = i64::MIN as u64; let b = (-1i64) as u64; let r = a.wrapping_add(b); assert!(add_ov_64(a, b, r)); } #[test] fn sub_overflow_min_minus_pos() { // INT64_MIN - 1 overflows let b = i64::MIN as u64; let a = 1u64; let r = b.wrapping_sub(a); assert!(sub_ov_64(a, b, r)); } #[test] fn sub_no_overflow() { let b = 5u64; let a = 2u64; let r = b.wrapping_sub(a); assert!(!sub_ov_64(a, b, r)); } #[test] fn mullw_fits_32_bits() { assert!(!mullw_ov((i32::MAX as i64) * 1)); assert!(!mullw_ov(-1i64)); } #[test] fn mullw_overflows_32_bits() { let p = (i32::MAX as i64) * 2; assert!(mullw_ov(p)); } #[test] fn mulld_overflows() { assert!(mulld_ov(i64::MAX, 2)); assert!(!mulld_ov(i64::MAX, 1)); // PPCBUG-022: INT_MIN * -1 overflows (=-INT_MIN > INT_MAX). // checked_mul correctly returns None for this case. assert!(mulld_ov(i64::MIN, -1), "INT_MIN * -1 overflows i64"); assert!(!mulld_ov(i64::MIN, 1)); assert!(!mulld_ov(i64::MIN + 1, -1), "INT_MIN+1 * -1 = INT_MAX, no overflow"); } #[test] fn neg_ov_only_at_min() { assert!(neg_ov_64(i64::MIN as u64)); assert!(!neg_ov_64(0)); assert!(!neg_ov_64(1)); } }