diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 061ad8617893c..54c5b95666c3e 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1406,6 +1406,13 @@ impl WrappingRange { Self { start: 0, end: size.unsigned_int_max() } } + pub fn full_signed(size: Size) -> Self { + Self { + start: (size.signed_int_min() as u128) & size.unsigned_int_max(), + end: size.signed_int_max() as u128, + } + } + /// Returns `true` if `v` is contained in the range. #[inline(always)] pub fn contains(&self, v: u128) -> bool { @@ -1492,6 +1499,11 @@ impl WrappingRange { Ok(start <= end) } } + + #[inline] + pub fn no_wraparound(&self, size: Size, signed: bool) -> Result { + if signed { self.no_signed_wraparound(size) } else { self.no_unsigned_wraparound(size) } + } } impl fmt::Debug for WrappingRange { diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index c3834607e9236..98c2b5f9d95d0 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1486,6 +1486,11 @@ impl<'tcx> Ty<'tcx> { matches!(self.kind(), Int(_)) } + #[inline] + pub fn is_unsigned(self) -> bool { + matches!(self.kind(), Uint(_)) + } + #[inline] pub fn is_ptr_sized_integral(self) -> bool { matches!(self.kind(), Int(ty::IntTy::Isize) | Uint(ty::UintTy::Usize)) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 526d0e96a36d8..8bec40522fc32 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -85,12 +85,16 @@ //! that contain `AllocId`s. use std::borrow::Cow; +use std::cmp::Ordering; use std::hash::{Hash, Hasher}; +use std::iter; use either::Either; use hashbrown::hash_table::{Entry, HashTable}; use itertools::Itertools as _; -use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx}; +use rustc_abi::{ + self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx, WrappingRange, +}; use rustc_arena::DroplessArena; use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ @@ -107,6 +111,7 @@ use rustc_middle::mir::interpret::GlobalAlloc; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::layout::HasTypingEnv; +use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; use smallvec::SmallVec; @@ -1369,10 +1374,10 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } } - if let Some(value) = self.simplify_binary_inner(op, lhs_ty, lhs, rhs) { + let ty = op.ty(self.tcx, lhs_ty, self.ty(rhs)); + if let Some(value) = self.simplify_binary_inner(op, ty, lhs_ty, lhs, rhs) { return Some(value); } - let ty = op.ty(self.tcx, lhs_ty, self.ty(rhs)); let value = Value::BinaryOp(op, lhs, rhs); Some(self.insert(ty, value)) } @@ -1380,6 +1385,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { fn simplify_binary_inner( &mut self, op: BinOp, + ty: Ty<'tcx>, lhs_ty: Ty<'tcx>, lhs: VnIndex, rhs: VnIndex, @@ -1488,7 +1494,13 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { (BinOp::Eq, a, b) if a == b => self.insert_bool(true), (BinOp::Ne, Left(a), Left(b)) => self.insert_bool(a != b), (BinOp::Ne, a, b) if a == b => self.insert_bool(false), - _ => return None, + _ => { + if let Some(result) = self.simplify_binary_range(op, lhs_ty, lhs, rhs) { + self.insert_scalar(ty, result) + } else { + return None; + } + } }; if op.is_overflowing() { @@ -1500,6 +1512,192 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } } + fn collect_range(&mut self, value: VnIndex) -> Option { + let ty = self.ty(value); + let layout = self.ecx.layout_of(ty).ok()?; + if !layout.backend_repr.is_scalar() { + return None; + } + if let Some(constant) = self.eval_to_const(value) { + let scalar = self.ecx.read_scalar(constant).discard_err()?; + let bits = scalar.to_bits(constant.layout.size).discard_err()?; + return Some(WrappingRange { start: bits, end: bits }); + } + match self.get(value) { + Value::Discriminant(discr) => { + let ty::Adt(adt, _) = self.ty(discr).kind() else { + return None; + }; + if !adt.is_enum() { + return None; + } + let discr_ty = adt.repr().discr_type().to_ty(self.tcx); + let discr_layout = self.ecx.layout_of(discr_ty).ok()?; + let discrs: Vec<_> = adt + .discriminants(self.tcx) + .map(|(_, discr)| ImmTy::from_uint(discr.val, discr_layout)) + .sorted_by(|x, y| { + let cmp = self.ecx.binary_op(BinOp::Cmp, x, y).unwrap(); + let cmp = cmp.to_scalar_int().unwrap().to_i8(); + match cmp { + -1 => Ordering::Less, + 0 => Ordering::Equal, + 1 => Ordering::Greater, + _ => unreachable!(), + } + }) + .collect(); + if let [one] = &discrs[..] { + let one = one.to_scalar().to_bits(discr_layout.size).discard_err()?; + Some(WrappingRange { start: one, end: one }) + } else if let [start, .., end] = &discrs[..] + && let Some(start) = start.to_scalar().to_bits(discr_layout.size).discard_err() + && let Some(end) = end.to_scalar().to_bits(discr_layout.size).discard_err() + && let range = (WrappingRange { start, end }) + && range.no_wraparound(discr_layout.size, discr_ty.is_signed()) == Ok(true) + { + // Prefer a non-wrapping range if one exists. + Some(range) + } else { + // Use the minimal wrapping range. + let mut pair = iter::zip(discrs.iter().skip(1), discrs.iter()); + let (mut start, mut end) = pair.next()?; + let range_size = self.ecx.binary_op(BinOp::Sub, &end, &start).discard_err()?; + let mut range_size = + range_size.to_scalar().to_bits(discr_layout.size).discard_err()?; + for (try_start, try_end) in pair { + let try_size = self + .ecx + .binary_op(BinOp::Sub, &try_end, &try_start) + .discard_err()? + .to_scalar() + .to_bits(discr_layout.size) + .discard_err()?; + if try_size < range_size { + range_size = try_size; + start = try_start; + end = try_end; + } + } + let start = start.to_scalar().to_bits(discr_layout.size).discard_err()?; + let end = end.to_scalar().to_bits(discr_layout.size).discard_err()?; + Some(WrappingRange { start, end }) + } + } + Value::Cast { kind, value } => { + if kind == CastKind::IntToInt { + let mut source_range = self.collect_range(value)?; + let source_ty = self.ty(value); + let source_layout = self.ecx.layout_of(source_ty).ok()?; + if layout.size > source_layout.size { + // If the source range wraps around, using the full range may have an optimal + // result for the cast. This is a trade-off, for instance, we don't know which is optimal, + // when cast [0i8, -128i8] to [0u16, 65408u16] (nowrap) or [65408u16, 127u16] (smaller). + if source_ty.is_signed() { + if source_range.no_signed_wraparound(source_layout.size) == Ok(false) { + source_range = WrappingRange::full_signed(source_layout.size); + } + } else if source_range.no_unsigned_wraparound(source_layout.size) + == Ok(false) + { + source_range = WrappingRange::full(source_layout.size); + } + } + + let WrappingRange { start, end } = source_range; + let start = ImmTy::from_uint(start, source_layout); + let end = ImmTy::from_uint(end, source_layout); + + if layout.size < source_layout.size { + let range_size = self + .ecx + .binary_op(BinOp::Sub, &end, &start) + .discard_err()? + .to_scalar() + .to_bits(source_layout.size) + .discard_err()? + + 1; + if range_size >= layout.size.bits() as u128 { + return None; + } + } + + let start = self.ecx.int_to_int_or_float(&start, layout).discard_err().unwrap(); + let start = start.to_scalar().to_bits(layout.size).discard_err()?; + let end = self.ecx.int_to_int_or_float(&end, layout).discard_err().unwrap(); + let end = end.to_scalar().to_bits(layout.size).discard_err()?; + Some(WrappingRange { start, end }) + } else { + None + } + } + Value::Opaque(_) + | Value::Constant { .. } + | Value::Aggregate(_, _) + | Value::Union(_, _) + | Value::RawPtr { .. } + | Value::Repeat(_, _) + | Value::Address { .. } + | Value::Projection(_, _) + | Value::NullaryOp(_) + | Value::UnaryOp(_, _) + | Value::BinaryOp(_, _, _) => None, + } + } + + fn simplify_binary_range( + &mut self, + op: BinOp, + lhs_ty: Ty<'tcx>, + lhs: VnIndex, + rhs: VnIndex, + ) -> Option { + if !lhs_ty.is_integral() { + return None; + } + if !matches!(op, BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge | BinOp::Cmp) { + return None; + } + let layout = self.ecx.layout_of(lhs_ty).ok()?; + let lhs_range = self.collect_range(lhs)?; + let rhs_range = self.collect_range(rhs)?; + // A wrapping or full range cannot be compared. + if lhs_ty.is_signed() + && (lhs_range.no_signed_wraparound(layout.size) != Ok(true) + || rhs_range.no_signed_wraparound(layout.size) != Ok(true)) + { + return None; + } else if lhs_ty.is_unsigned() + && (lhs_range.no_unsigned_wraparound(layout.size) != Ok(true) + || rhs_range.no_unsigned_wraparound(layout.size) != Ok(true)) + { + return None; + } + + let lhs_start = ImmTy::from_uint(lhs_range.start, layout); + let lhs_end = ImmTy::from_uint(lhs_range.end, layout); + let rhs_start = ImmTy::from_uint(rhs_range.start, layout); + let rhs_end = ImmTy::from_uint(rhs_range.end, layout); + + let cmp = self.ecx.binary_op(op, &lhs_start, &rhs_start).discard_err()?.to_scalar(); + if rhs_range.start != rhs_range.end + && cmp != self.ecx.binary_op(op, &lhs_start, &rhs_end).discard_err()?.to_scalar() + { + return None; + } + if lhs_range.start != lhs_range.end { + if cmp != self.ecx.binary_op(op, &lhs_end, &rhs_start).discard_err()?.to_scalar() { + return None; + } + if rhs_range.start != rhs_range.end + && cmp != self.ecx.binary_op(op, &lhs_end, &rhs_end).discard_err()?.to_scalar() + { + return None; + } + } + Some(cmp) + } + fn simplify_cast( &mut self, initial_kind: &mut CastKind, diff --git a/tests/mir-opt/gvn_range.cast_from_signed_wrapping.GVN.diff b/tests/mir-opt/gvn_range.cast_from_signed_wrapping.GVN.diff new file mode 100644 index 0000000000000..7e03ce0918bf4 --- /dev/null +++ b/tests/mir-opt/gvn_range.cast_from_signed_wrapping.GVN.diff @@ -0,0 +1,52 @@ +- // MIR for `cast_from_signed_wrapping` before GVN ++ // MIR for `cast_from_signed_wrapping` after GVN + + fn cast_from_signed_wrapping(_1: SignedWrappingA) -> (bool, bool) { + debug s => _1; + let mut _0: (bool, bool); + let _2: u8; + let _3: SignedWrappingA; + let mut _4: i8; + let mut _5: bool; + let mut _6: u8; + let mut _7: bool; + let mut _8: u8; + scope 1 { + debug s => _2; + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = copy _1; +- _4 = discriminant(_3); ++ _4 = discriminant(_1); + _2 = move _4 as u8 (IntToInt); + StorageDead(_3); + StorageLive(_5); + StorageLive(_6); + _6 = copy _2; +- _5 = Le(move _6, const 128_u8); ++ _5 = const true; + StorageDead(_6); + StorageLive(_7); + StorageLive(_8); + _8 = copy _2; +- _7 = Gt(move _8, const 128_u8); ++ _7 = const false; + StorageDead(_8); +- _0 = (move _5, move _7); ++ _0 = const (true, false); + StorageDead(_7); + StorageDead(_5); +- StorageDead(_2); ++ nop; + return; + } ++ } ++ ++ ALLOC0 (size: 2, align: 1) { ++ 01 00 │ .. + } + diff --git a/tests/mir-opt/gvn_range.cast_from_unsigned_wrapping.GVN.diff b/tests/mir-opt/gvn_range.cast_from_unsigned_wrapping.GVN.diff new file mode 100644 index 0000000000000..abc26085b6ce0 --- /dev/null +++ b/tests/mir-opt/gvn_range.cast_from_unsigned_wrapping.GVN.diff @@ -0,0 +1,54 @@ +- // MIR for `cast_from_unsigned_wrapping` before GVN ++ // MIR for `cast_from_unsigned_wrapping` after GVN + + fn cast_from_unsigned_wrapping(_1: UnsignedWrappingA) -> (bool, bool) { + debug s => _1; + let mut _0: (bool, bool); + let _2: i8; + let _3: UnsignedWrappingA; + let mut _4: u8; + let mut _5: bool; + let mut _6: i8; + let mut _7: bool; + let mut _8: i8; + scope 1 { + debug s => _2; + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = copy _1; +- _4 = discriminant(_3); ++ _4 = discriminant(_1); + _2 = move _4 as i8 (IntToInt); + StorageDead(_3); +- StorageLive(_5); ++ nop; + StorageLive(_6); + _6 = copy _2; +- _5 = Lt(move _6, const -1_i8); ++ _5 = const false; + StorageDead(_6); + StorageLive(_7); + StorageLive(_8); + _8 = copy _2; +- _7 = Gt(move _8, const 1_i8); ++ _7 = const false; + StorageDead(_8); +- _0 = (move _5, move _7); ++ _0 = const (false, false); + StorageDead(_7); +- StorageDead(_5); +- StorageDead(_2); ++ nop; ++ nop; + return; + } ++ } ++ ++ ALLOC0 (size: 2, align: 1) { ++ 00 00 │ .. + } + diff --git a/tests/mir-opt/gvn_range.cast_to_signed_wrapping.GVN.diff b/tests/mir-opt/gvn_range.cast_to_signed_wrapping.GVN.diff new file mode 100644 index 0000000000000..9c2a9c350544d --- /dev/null +++ b/tests/mir-opt/gvn_range.cast_to_signed_wrapping.GVN.diff @@ -0,0 +1,24 @@ +- // MIR for `cast_to_signed_wrapping` before GVN ++ // MIR for `cast_to_signed_wrapping` after GVN + + fn cast_to_signed_wrapping(_1: UnsignedA) -> bool { + debug s => _1; + let mut _0: bool; + let mut _2: i8; + let _3: UnsignedA; + let mut _4: u8; + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = copy _1; +- _4 = discriminant(_3); ++ _4 = discriminant(_1); + _2 = move _4 as i8 (IntToInt); + StorageDead(_3); + _0 = Gt(move _2, const -126_i8); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/gvn_range.cast_to_unsigned_wrapping.GVN.diff b/tests/mir-opt/gvn_range.cast_to_unsigned_wrapping.GVN.diff new file mode 100644 index 0000000000000..cadca53eedcfe --- /dev/null +++ b/tests/mir-opt/gvn_range.cast_to_unsigned_wrapping.GVN.diff @@ -0,0 +1,24 @@ +- // MIR for `cast_to_unsigned_wrapping` before GVN ++ // MIR for `cast_to_unsigned_wrapping` after GVN + + fn cast_to_unsigned_wrapping(_1: SignedA) -> bool { + debug s => _1; + let mut _0: bool; + let mut _2: u8; + let _3: SignedA; + let mut _4: i8; + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = copy _1; +- _4 = discriminant(_3); ++ _4 = discriminant(_1); + _2 = move _4 as u8 (IntToInt); + StorageDead(_3); + _0 = Gt(move _2, const 254_u8); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/gvn_range.rs b/tests/mir-opt/gvn_range.rs new file mode 100644 index 0000000000000..9933fc5543451 --- /dev/null +++ b/tests/mir-opt/gvn_range.rs @@ -0,0 +1,159 @@ +//@ test-mir-pass: GVN + +#![crate_type = "lib"] + +#[repr(i8)] +pub enum SignedWrapping { + Start = i8::MAX, + End = i8::MIN, +} + +#[repr(u8)] +pub enum UnsignedWrapping { + Start = u8::MAX, + End = u8::MIN, +} + +#[repr(u16)] +pub enum UnsignedU16 { + Start = 3, + End = 4, +} + +#[repr(i8)] +#[derive(Copy, Clone)] +pub enum SignedA { + A = -2, + B = -1, + C = 0, + D = 1, +} + +// EMIT_MIR gvn_range.cast_to_unsigned_wrapping.GVN.diff +pub fn cast_to_unsigned_wrapping(s: SignedA) -> bool { + // The source range of i8 is [-2, 1], and the destination range of u8 is [254, 1]. + // This is a wrapping range that contain 255. + // CHECK-LABEL: fn cast_to_unsigned_wrapping( + // CHECK: _0 = Gt( + s as u8 > 254 +} + +#[repr(u8)] +#[derive(Copy, Clone)] +pub enum UnsignedA { + A = 127, + B = 128, // -128, 0x80 + C = 129, // -127, 0x81 + D = 130, // -126, 0x82 +} + +// EMIT_MIR gvn_range.cast_to_signed_wrapping.GVN.diff +pub fn cast_to_signed_wrapping(s: UnsignedA) -> bool { + // The source range of u8 is [127, 130], and the destination range of i8 is [127, -126]. + // This is a wrapping range that contain -127. + // CHECK-LABEL: fn cast_to_signed_wrapping( + // CHECK: _0 = Gt( + s as i8 > -126 +} + +// The size of range [0xff, 1] is smaller than [0, 0xff]. +#[repr(u8)] +#[derive(Copy, Clone)] +pub enum UnsignedWrappingA { + A = u8::MAX, // -1, 0xff + B = u8::MIN, // 0 + C = u8::MIN + 1, // 1 +} + +// EMIT_MIR gvn_range.cast_from_unsigned_wrapping.GVN.diff +pub fn cast_from_unsigned_wrapping(s: UnsignedWrappingA) -> (bool, bool) { + // CHECK-LABEL: fn cast_from_unsigned_wrapping( + // CHECK: _0 = const (false, false); + let s = s as i8; + (s < -1, s > 1) +} + +// The size of range [0, -128] is smaller than [-128, 127]. +#[repr(i8)] +#[derive(Copy, Clone)] +pub enum SignedWrappingA { + A = i8::MAX, // 127, 0x7f + B = i8::MIN, // -128, 0x80 + C = 0, +} + +// EMIT_MIR gvn_range.cast_from_signed_wrapping.GVN.diff +pub fn cast_from_signed_wrapping(s: SignedWrappingA) -> (bool, bool) { + // CHECK-LABEL: fn cast_from_signed_wrapping( + // CHECK: _0 = const (true, false); + let s = s as u8; + (s <= 128, s > 128) +} + +// EMIT_MIR gvn_range.unsigned_extension.GVN.diff +pub fn unsigned_extension(u: UnsignedA) -> (bool, bool, bool, bool) { + // CHECK-LABEL: fn unsigned_extension( + // CHECK: _0 = (const false, const false, const false, const false); + let u2u = u as u16; + let u2s = u as i16; + (u2u < 127, u2u > 130, u2s < 127, u2u > 130) +} + +// EMIT_MIR gvn_range.unsigned_wrapping_extension.GVN.diff +pub fn unsigned_wrapping_extension(u: UnsignedWrappingA) -> (bool, bool) { + // CHECK-LABEL: fn unsigned_wrapping_extension( + // CHECK: _0 = const (true, true); + let u2u = u as u16; + let u2s = u as i16; + (u2u <= 256, u2s <= 256) +} + +// EMIT_MIR gvn_range.signed_extension.GVN.diff +pub fn signed_extension(s: SignedA) -> (bool, bool, bool, bool) { + // CHECK-LABEL: fn signed_extension( + // CHECK: _0 = (move _{{.*}}, move _{{.*}}, const false, const false); + let s2u = s as u16; + let s2s = s as i16; + (s2u > 254, s2u > 65534, s2s < -2, s2s > 1) +} + +// EMIT_MIR gvn_range.signed_wrapping_extension.GVN.diff +pub fn signed_wrapping_extension(s: SignedWrappingA) -> (bool, bool, bool, bool) { + // CHECK-LABEL: fn signed_wrapping_extension( + // CHECK: _0 = (move _{{.*}}, move _{{.*}}, const false, const false); + let s2u = s as u16; + let s2s = s as i16; + (s2u < 128, s2u > 65408, s2s < -128, s2s > 128) +} + +#[repr(i16)] +pub enum SignedWrappingB { + A = 32512, // 0x7f00u16 + B = 32767, // 0x7fffu16 + C = -32768, // 0x8000u16 +} + +#[repr(i16)] +pub enum SignedWrappingC { + A = 256, // 0x100 + B = 511, // 0x1ff + C = i16::MAX, + D = i16::MIN, + X = 0, +} + +#[repr(i16)] +pub enum SignedB { + A = 0, + B = 255, // 0xff + C = 256, // 0x100 +} + +// When cast to i8 or u8, +// the ranges [0x7f00, 0x8000], [0x100, 0] and [0, 0x100] contain the value -1. +// EMIT_MIR gvn_range.truncate.GVN.diff +pub fn truncate(s1: SignedWrappingB, s2: SignedWrappingC, s3: SignedB) -> (bool, bool, bool) { + // CHECK-LABEL: fn truncate( + // CHECK: _0 = (move _{{.*}}, move _{{.*}}, move _{{.*}}); + (s1 as i8 > -1, s2 as i8 > -1, s3 as i8 > -1) +} diff --git a/tests/mir-opt/gvn_range.signed_extension.GVN.diff b/tests/mir-opt/gvn_range.signed_extension.GVN.diff new file mode 100644 index 0000000000000..b0c67ce37818b --- /dev/null +++ b/tests/mir-opt/gvn_range.signed_extension.GVN.diff @@ -0,0 +1,86 @@ +- // MIR for `signed_extension` before GVN ++ // MIR for `signed_extension` after GVN + + fn signed_extension(_1: SignedA) -> (bool, bool, bool, bool) { + debug s => _1; + let mut _0: (bool, bool, bool, bool); + let _2: u16; + let _3: SignedA; + let mut _4: i8; + let _6: SignedA; + let mut _7: i8; + let mut _8: bool; + let mut _9: u16; + let mut _10: bool; + let mut _11: u16; + let mut _12: bool; + let mut _13: i16; + let mut _14: bool; + let mut _15: i16; + scope 1 { + debug s2u => _2; + let _5: i16; + scope 2 { + debug s2s => _5; + } + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = copy _1; +- _4 = discriminant(_3); +- _2 = move _4 as u16 (IntToInt); ++ _4 = discriminant(_1); ++ _2 = copy _4 as u16 (IntToInt); + StorageDead(_3); +- StorageLive(_5); ++ nop; + StorageLive(_6); + _6 = copy _1; +- _7 = discriminant(_6); +- _5 = move _7 as i16 (IntToInt); ++ _7 = copy _4; ++ _5 = copy _4 as i16 (IntToInt); + StorageDead(_6); + StorageLive(_8); + StorageLive(_9); + _9 = copy _2; +- _8 = Gt(move _9, const 254_u16); ++ _8 = Gt(copy _2, const 254_u16); + StorageDead(_9); + StorageLive(_10); + StorageLive(_11); + _11 = copy _2; +- _10 = Gt(move _11, const 65534_u16); ++ _10 = Gt(copy _2, const 65534_u16); + StorageDead(_11); +- StorageLive(_12); ++ nop; + StorageLive(_13); + _13 = copy _5; +- _12 = Lt(move _13, const -2_i16); ++ _12 = const false; + StorageDead(_13); + StorageLive(_14); + StorageLive(_15); + _15 = copy _5; +- _14 = Gt(move _15, const 1_i16); ++ _14 = const false; + StorageDead(_15); +- _0 = (move _8, move _10, move _12, move _14); ++ _0 = (move _8, move _10, const false, const false); + StorageDead(_14); +- StorageDead(_12); ++ nop; + StorageDead(_10); + StorageDead(_8); +- StorageDead(_5); +- StorageDead(_2); ++ nop; ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn_range.signed_wrapping_extension.GVN.diff b/tests/mir-opt/gvn_range.signed_wrapping_extension.GVN.diff new file mode 100644 index 0000000000000..c7689efd5485c --- /dev/null +++ b/tests/mir-opt/gvn_range.signed_wrapping_extension.GVN.diff @@ -0,0 +1,86 @@ +- // MIR for `signed_wrapping_extension` before GVN ++ // MIR for `signed_wrapping_extension` after GVN + + fn signed_wrapping_extension(_1: SignedWrappingA) -> (bool, bool, bool, bool) { + debug s => _1; + let mut _0: (bool, bool, bool, bool); + let _2: u16; + let _3: SignedWrappingA; + let mut _4: i8; + let _6: SignedWrappingA; + let mut _7: i8; + let mut _8: bool; + let mut _9: u16; + let mut _10: bool; + let mut _11: u16; + let mut _12: bool; + let mut _13: i16; + let mut _14: bool; + let mut _15: i16; + scope 1 { + debug s2u => _2; + let _5: i16; + scope 2 { + debug s2s => _5; + } + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = copy _1; +- _4 = discriminant(_3); +- _2 = move _4 as u16 (IntToInt); ++ _4 = discriminant(_1); ++ _2 = copy _4 as u16 (IntToInt); + StorageDead(_3); +- StorageLive(_5); ++ nop; + StorageLive(_6); + _6 = copy _1; +- _7 = discriminant(_6); +- _5 = move _7 as i16 (IntToInt); ++ _7 = copy _4; ++ _5 = copy _4 as i16 (IntToInt); + StorageDead(_6); + StorageLive(_8); + StorageLive(_9); + _9 = copy _2; +- _8 = Lt(move _9, const 128_u16); ++ _8 = Lt(copy _2, const 128_u16); + StorageDead(_9); + StorageLive(_10); + StorageLive(_11); + _11 = copy _2; +- _10 = Gt(move _11, const 65408_u16); ++ _10 = Gt(copy _2, const 65408_u16); + StorageDead(_11); +- StorageLive(_12); ++ nop; + StorageLive(_13); + _13 = copy _5; +- _12 = Lt(move _13, const -128_i16); ++ _12 = const false; + StorageDead(_13); + StorageLive(_14); + StorageLive(_15); + _15 = copy _5; +- _14 = Gt(move _15, const 128_i16); ++ _14 = const false; + StorageDead(_15); +- _0 = (move _8, move _10, move _12, move _14); ++ _0 = (move _8, move _10, const false, const false); + StorageDead(_14); +- StorageDead(_12); ++ nop; + StorageDead(_10); + StorageDead(_8); +- StorageDead(_5); +- StorageDead(_2); ++ nop; ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn_range.truncate.GVN.diff b/tests/mir-opt/gvn_range.truncate.GVN.diff new file mode 100644 index 0000000000000..58e95e3b3c9d6 --- /dev/null +++ b/tests/mir-opt/gvn_range.truncate.GVN.diff @@ -0,0 +1,63 @@ +- // MIR for `truncate` before GVN ++ // MIR for `truncate` after GVN + + fn truncate(_1: SignedWrappingB, _2: SignedWrappingC, _3: SignedB) -> (bool, bool, bool) { + debug s1 => _1; + debug s2 => _2; + debug s3 => _3; + let mut _0: (bool, bool, bool); + let mut _4: bool; + let mut _5: i8; + let _6: SignedWrappingB; + let mut _7: i16; + let mut _8: bool; + let mut _9: i8; + let _10: SignedWrappingC; + let mut _11: i16; + let mut _12: bool; + let mut _13: i8; + let _14: SignedB; + let mut _15: i16; + + bb0: { + StorageLive(_4); + StorageLive(_5); + StorageLive(_6); +- _6 = move _1; +- _7 = discriminant(_6); ++ _6 = copy _1; ++ _7 = discriminant(_1); + _5 = move _7 as i8 (IntToInt); + StorageDead(_6); + _4 = Gt(move _5, const -1_i8); + StorageDead(_5); + StorageLive(_8); + StorageLive(_9); + StorageLive(_10); +- _10 = move _2; +- _11 = discriminant(_10); ++ _10 = copy _2; ++ _11 = discriminant(_2); + _9 = move _11 as i8 (IntToInt); + StorageDead(_10); + _8 = Gt(move _9, const -1_i8); + StorageDead(_9); + StorageLive(_12); + StorageLive(_13); + StorageLive(_14); +- _14 = move _3; +- _15 = discriminant(_14); ++ _14 = copy _3; ++ _15 = discriminant(_3); + _13 = move _15 as i8 (IntToInt); + StorageDead(_14); + _12 = Gt(move _13, const -1_i8); + StorageDead(_13); + _0 = (move _4, move _8, move _12); + StorageDead(_12); + StorageDead(_8); + StorageDead(_4); + return; + } + } + diff --git a/tests/mir-opt/gvn_range.unsigned_extension.GVN.diff b/tests/mir-opt/gvn_range.unsigned_extension.GVN.diff new file mode 100644 index 0000000000000..954fa0ed7bc47 --- /dev/null +++ b/tests/mir-opt/gvn_range.unsigned_extension.GVN.diff @@ -0,0 +1,86 @@ +- // MIR for `unsigned_extension` before GVN ++ // MIR for `unsigned_extension` after GVN + + fn unsigned_extension(_1: UnsignedA) -> (bool, bool, bool, bool) { + debug u => _1; + let mut _0: (bool, bool, bool, bool); + let _2: u16; + let _3: UnsignedA; + let mut _4: u8; + let _6: UnsignedA; + let mut _7: u8; + let mut _8: bool; + let mut _9: u16; + let mut _10: bool; + let mut _11: u16; + let mut _12: bool; + let mut _13: i16; + let mut _14: bool; + let mut _15: u16; + scope 1 { + debug u2u => _2; + let _5: i16; + scope 2 { + debug u2s => _5; + } + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = copy _1; +- _4 = discriminant(_3); +- _2 = move _4 as u16 (IntToInt); ++ _4 = discriminant(_1); ++ _2 = copy _4 as u16 (IntToInt); + StorageDead(_3); +- StorageLive(_5); ++ nop; + StorageLive(_6); + _6 = copy _1; +- _7 = discriminant(_6); +- _5 = move _7 as i16 (IntToInt); ++ _7 = copy _4; ++ _5 = copy _4 as i16 (IntToInt); + StorageDead(_6); +- StorageLive(_8); ++ nop; + StorageLive(_9); + _9 = copy _2; +- _8 = Lt(move _9, const 127_u16); ++ _8 = const false; + StorageDead(_9); + StorageLive(_10); + StorageLive(_11); + _11 = copy _2; +- _10 = Gt(move _11, const 130_u16); ++ _10 = const false; + StorageDead(_11); + StorageLive(_12); + StorageLive(_13); + _13 = copy _5; +- _12 = Lt(move _13, const 127_i16); ++ _12 = const false; + StorageDead(_13); + StorageLive(_14); + StorageLive(_15); + _15 = copy _2; +- _14 = Gt(move _15, const 130_u16); ++ _14 = const false; + StorageDead(_15); +- _0 = (move _8, move _10, move _12, move _14); ++ _0 = (const false, const false, const false, const false); + StorageDead(_14); + StorageDead(_12); + StorageDead(_10); +- StorageDead(_8); +- StorageDead(_5); +- StorageDead(_2); ++ nop; ++ nop; ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn_range.unsigned_wrapping_extension.GVN.diff b/tests/mir-opt/gvn_range.unsigned_wrapping_extension.GVN.diff new file mode 100644 index 0000000000000..ef4f30e41d966 --- /dev/null +++ b/tests/mir-opt/gvn_range.unsigned_wrapping_extension.GVN.diff @@ -0,0 +1,72 @@ +- // MIR for `unsigned_wrapping_extension` before GVN ++ // MIR for `unsigned_wrapping_extension` after GVN + + fn unsigned_wrapping_extension(_1: UnsignedWrappingA) -> (bool, bool) { + debug u => _1; + let mut _0: (bool, bool); + let _2: u16; + let _3: UnsignedWrappingA; + let mut _4: u8; + let _6: UnsignedWrappingA; + let mut _7: u8; + let mut _8: bool; + let mut _9: u16; + let mut _10: bool; + let mut _11: i16; + scope 1 { + debug u2u => _2; + let _5: i16; + scope 2 { + debug u2s => _5; + } + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = copy _1; +- _4 = discriminant(_3); +- _2 = move _4 as u16 (IntToInt); ++ _4 = discriminant(_1); ++ _2 = copy _4 as u16 (IntToInt); + StorageDead(_3); +- StorageLive(_5); ++ nop; + StorageLive(_6); + _6 = copy _1; +- _7 = discriminant(_6); +- _5 = move _7 as i16 (IntToInt); ++ _7 = copy _4; ++ _5 = copy _4 as i16 (IntToInt); + StorageDead(_6); +- StorageLive(_8); ++ nop; + StorageLive(_9); + _9 = copy _2; +- _8 = Le(move _9, const 256_u16); ++ _8 = const true; + StorageDead(_9); + StorageLive(_10); + StorageLive(_11); + _11 = copy _5; +- _10 = Le(move _11, const 256_i16); ++ _10 = const true; + StorageDead(_11); +- _0 = (move _8, move _10); ++ _0 = const (true, true); + StorageDead(_10); +- StorageDead(_8); +- StorageDead(_5); +- StorageDead(_2); ++ nop; ++ nop; ++ nop; + return; + } ++ } ++ ++ ALLOC0 (size: 2, align: 1) { ++ 01 01 │ .. + } +