Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/mir/eval.rs')
| -rw-r--r-- | crates/hir-ty/src/mir/eval.rs | 164 |
1 files changed, 144 insertions, 20 deletions
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index e3072d6ee7..dcae6877ba 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -1211,7 +1211,9 @@ impl Evaluator<'_> { } lc = &lc[..self.ptr_size()]; rc = &rc[..self.ptr_size()]; - ls + lc = self.read_memory(Address::from_bytes(lc)?, ls)?; + rc = self.read_memory(Address::from_bytes(rc)?, ls)?; + break 'binary_op Owned(vec![u8::from(lc == rc)]); } else { self.size_of_sized(&ty, locals, "operand of binary op")? }; @@ -1340,18 +1342,8 @@ impl Evaluator<'_> { } } else { let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_))); - let l128 = i128::from_le_bytes(pad16(lc, is_signed)); - let r128 = i128::from_le_bytes(pad16(rc, is_signed)); - let check_overflow = |r: i128| { - // FIXME: this is not very correct, and only catches the basic cases. - let r = r.to_le_bytes(); - for &k in &r[lc.len()..] { - if k != 0 && (k != 255 || !is_signed) { - return Err(MirEvalError::Panic(format!("Overflow in {op:?}"))); - } - } - Ok(Owned(r[0..lc.len()].into())) - }; + let l128 = IntValue::from_bytes(lc, is_signed); + let r128 = IntValue::from_bytes(rc, is_signed); match op { BinOp::Ge | BinOp::Gt | BinOp::Le | BinOp::Lt | BinOp::Eq | BinOp::Ne => { let r = op.run_compare(l128, r128) as u8; @@ -1366,25 +1358,31 @@ impl Evaluator<'_> { | BinOp::Rem | BinOp::Sub => { let r = match op { - BinOp::Add => l128.overflowing_add(r128).0, - BinOp::Mul => l128.overflowing_mul(r128).0, + BinOp::Add => l128.checked_add(r128).ok_or_else(|| { + MirEvalError::Panic(format!("Overflow in {op:?}")) + })?, + BinOp::Mul => l128.checked_mul(r128).ok_or_else(|| { + MirEvalError::Panic(format!("Overflow in {op:?}")) + })?, BinOp::Div => l128.checked_div(r128).ok_or_else(|| { MirEvalError::Panic(format!("Overflow in {op:?}")) })?, BinOp::Rem => l128.checked_rem(r128).ok_or_else(|| { MirEvalError::Panic(format!("Overflow in {op:?}")) })?, - BinOp::Sub => l128.overflowing_sub(r128).0, + BinOp::Sub => l128.checked_sub(r128).ok_or_else(|| { + MirEvalError::Panic(format!("Overflow in {op:?}")) + })?, BinOp::BitAnd => l128 & r128, BinOp::BitOr => l128 | r128, BinOp::BitXor => l128 ^ r128, _ => unreachable!(), }; - check_overflow(r)? + Owned(r.to_bytes()) } BinOp::Shl | BinOp::Shr => { let r = 'b: { - if let Ok(shift_amount) = u32::try_from(r128) { + if let Some(shift_amount) = r128.as_u32() { let r = match op { BinOp::Shl => l128.checked_shl(shift_amount), BinOp::Shr => l128.checked_shr(shift_amount), @@ -1401,7 +1399,7 @@ impl Evaluator<'_> { }; return Err(MirEvalError::Panic(format!("Overflow in {op:?}"))); }; - Owned(r.to_le_bytes()[..lc.len()].to_vec()) + Owned(r.to_bytes()) } BinOp::Offset => not_supported!("offset binop"), } @@ -2115,7 +2113,7 @@ impl Evaluator<'_> { while self.heap.len() % align != 0 { self.heap.push(0); } - if size.checked_add(self.heap.len()).map_or(true, |x| x > self.memory_limit) { + if size.checked_add(self.heap.len()).is_none_or(|x| x > self.memory_limit) { return Err(MirEvalError::Panic(format!("Memory allocation of {size} bytes failed"))); } let pos = self.heap.len(); @@ -2974,3 +2972,129 @@ pub fn pad16(it: &[u8], is_signed: bool) -> [u8; 16] { res[..it.len()].copy_from_slice(it); res } + +macro_rules! for_each_int_type { + ($call_macro:path, $args:tt) => { + $call_macro! { + $args + I8 + U8 + I16 + U16 + I32 + U32 + I64 + U64 + I128 + U128 + } + }; +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +enum IntValue { + I8(i8), + U8(u8), + I16(i16), + U16(u16), + I32(i32), + U32(u32), + I64(i64), + U64(u64), + I128(i128), + U128(u128), +} + +macro_rules! checked_int_op { + ( [ $op:ident ] $( $int_ty:ident )+ ) => { + fn $op(self, other: Self) -> Option<Self> { + match (self, other) { + $( (Self::$int_ty(a), Self::$int_ty(b)) => a.$op(b).map(Self::$int_ty), )+ + _ => panic!("incompatible integer types"), + } + } + }; +} + +macro_rules! int_bit_shifts { + ( [ $op:ident ] $( $int_ty:ident )+ ) => { + fn $op(self, amount: u32) -> Option<Self> { + match self { + $( Self::$int_ty(this) => this.$op(amount).map(Self::$int_ty), )+ + } + } + }; +} + +macro_rules! unchecked_int_op { + ( [ $name:ident, $op:tt ] $( $int_ty:ident )+ ) => { + fn $name(self, other: Self) -> Self { + match (self, other) { + $( (Self::$int_ty(a), Self::$int_ty(b)) => Self::$int_ty(a $op b), )+ + _ => panic!("incompatible integer types"), + } + } + }; +} + +impl IntValue { + fn from_bytes(bytes: &[u8], is_signed: bool) -> Self { + match (bytes.len(), is_signed) { + (1, false) => Self::U8(u8::from_le_bytes(bytes.try_into().unwrap())), + (1, true) => Self::I8(i8::from_le_bytes(bytes.try_into().unwrap())), + (2, false) => Self::U16(u16::from_le_bytes(bytes.try_into().unwrap())), + (2, true) => Self::I16(i16::from_le_bytes(bytes.try_into().unwrap())), + (4, false) => Self::U32(u32::from_le_bytes(bytes.try_into().unwrap())), + (4, true) => Self::I32(i32::from_le_bytes(bytes.try_into().unwrap())), + (8, false) => Self::U64(u64::from_le_bytes(bytes.try_into().unwrap())), + (8, true) => Self::I64(i64::from_le_bytes(bytes.try_into().unwrap())), + (16, false) => Self::U128(u128::from_le_bytes(bytes.try_into().unwrap())), + (16, true) => Self::I128(i128::from_le_bytes(bytes.try_into().unwrap())), + _ => panic!("invalid integer size"), + } + } + + fn to_bytes(self) -> Vec<u8> { + macro_rules! m { + ( [] $( $int_ty:ident )+ ) => { + match self { + $( Self::$int_ty(v) => v.to_le_bytes().to_vec() ),+ + } + }; + } + for_each_int_type! { m, [] } + } + + fn as_u32(self) -> Option<u32> { + macro_rules! m { + ( [] $( $int_ty:ident )+ ) => { + match self { + $( Self::$int_ty(v) => v.try_into().ok() ),+ + } + }; + } + for_each_int_type! { m, [] } + } + + for_each_int_type!(checked_int_op, [checked_add]); + for_each_int_type!(checked_int_op, [checked_sub]); + for_each_int_type!(checked_int_op, [checked_div]); + for_each_int_type!(checked_int_op, [checked_rem]); + for_each_int_type!(checked_int_op, [checked_mul]); + + for_each_int_type!(int_bit_shifts, [checked_shl]); + for_each_int_type!(int_bit_shifts, [checked_shr]); +} + +impl std::ops::BitAnd for IntValue { + type Output = Self; + for_each_int_type!(unchecked_int_op, [bitand, &]); +} +impl std::ops::BitOr for IntValue { + type Output = Self; + for_each_int_type!(unchecked_int_op, [bitor, |]); +} +impl std::ops::BitXor for IntValue { + type Output = Self; + for_each_int_type!(unchecked_int_op, [bitxor, ^]); +} |