From 691bbf769b67d1f9f73def30970c7e5e09df50ac Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sun, 23 Nov 2025 20:31:57 -0500 Subject: [PATCH 1/6] derive(PartialEq) via bitwise comparison where possible --- compiler/rustc_abi/src/layout/ty.rs | 23 ++++++ .../src/deriving/cmp/partial_eq.rs | 71 ++++++++++++++----- .../src/deriving/generic/mod.rs | 17 ++++- .../rustc_hir_analysis/src/check/intrinsic.rs | 14 ++++ compiler/rustc_middle/src/ty/sty.rs | 8 +++ .../src/lower_intrinsics.rs | 39 ++++++++++ compiler/rustc_span/src/symbol.rs | 2 + library/core/src/intrinsics/mod.rs | 20 ++++++ tests/assembly-llvm/derive-partialeq.rs | 22 ++++++ tests/ui/deriving/deriving-all-codegen.stdout | 50 +++++++++---- tests/ui/stats/macro-stats.stderr | 2 +- 11 files changed, 235 insertions(+), 33 deletions(-) create mode 100644 tests/assembly-llvm/derive-partialeq.rs diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index ca2753ea2fd98..7418cbf9377a8 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -191,6 +191,29 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { Ty::ty_and_layout_field(self, cx, i) } + pub fn is_pod_layout(self, cx: &C) -> bool + where + Ty: TyAbiInterface<'a, C> + Copy, + { + let Variants::Single { .. } = self.variants else { + return false; + }; + + let mut expected_offset = Size::ZERO; + for i in self.fields.index_by_increasing_offset() { + if self.fields.offset(i) != expected_offset { + return false; + } + expected_offset = self.fields.offset(i) + TyAndLayout::field(self, cx, i).size; + } + + if expected_offset != self.size { + return false; + } + + true + } + pub fn pointee_info_at(self, cx: &C, offset: Size) -> Option where Ty: TyAbiInterface<'a, C>, diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs index 03ee270d66ba1..196234553e302 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs @@ -1,6 +1,6 @@ -use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability, Safety}; +use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, MetaItem, Mutability, Safety}; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::{Span, sym}; +use rustc_span::{Ident, Span, kw, sym}; use thin_vec::thin_vec; use crate::deriving::generic::ty::*; @@ -125,7 +125,7 @@ fn get_substructure_equality_expr( ) -> Box { use SubstructureFields::*; - match substructure.fields { + let field_comparison = match substructure.fields { EnumMatching(.., fields) | Struct(.., fields) => { let combine = move |acc, field| { let rhs = get_field_equality_expr(cx, field); @@ -138,23 +138,35 @@ fn get_substructure_equality_expr( Some(rhs) }; - // First compare scalar fields, then compound fields, combining all - // with logical AND. - return fields - .iter() - .filter(|field| !field.maybe_scalar) - .fold(fields.iter().filter(|field| field.maybe_scalar).fold(None, combine), combine) - // If there are no fields, treat as always equal. - .unwrap_or_else(|| cx.expr_bool(span, true)); + // If there are no fields, return true immediately. + // If there is just one, compare it. + // Otherwise, try to do a bitwise comparison. + match &fields[..] { + [] => return cx.expr_bool(span, true), + [field] => return get_field_equality_expr(cx, field), + _ => { + // First compare scalar fields, then compound fields, combining all + // with logical AND. + fields + .iter() + .filter(|field| !field.maybe_scalar) + .fold( + fields.iter().filter(|field| field.maybe_scalar).fold(None, combine), + combine, + ) + .unwrap() + } + } } EnumDiscr(disc, match_expr) => { let lhs = get_field_equality_expr(cx, disc); - let Some(match_expr) = match_expr else { - return lhs; - }; - // Compare the discriminant first (cheaper), then the rest of the - // fields. - return cx.expr_binary(disc.span, BinOpKind::And, lhs, match_expr.clone()); + if let Some(match_expr) = match_expr { + // Compare the discriminant first (cheaper), then the rest of the + // fields. + cx.expr_binary(disc.span, BinOpKind::And, lhs, match_expr.clone()) + } else { + lhs + } } StaticEnum(..) => cx.dcx().span_bug( span, @@ -168,6 +180,31 @@ fn get_substructure_equality_expr( span, "unexpected all-fieldless enum encountered during `derive(PartialEq)` expansion", ), + }; + + if matches!(substructure.fields, Struct(..)) { + // Construct intrinsics::can_compare_bitwise() + let self_ty = cx.ty_path(cx.path_ident(span, Ident::with_dummy_span(kw::SelfUpper))); + let path = cx.expr_path(cx.path_all( + span, + true, + cx.std_path(&[sym::intrinsics, sym::can_compare_bitwise]), + vec![GenericArg::Type(self_ty)], + )); + let cond = cx.expr_call(span, path, thin_vec![]); + + let [_self, rhs] = &substructure.selflike_args[..] else { + cx.dcx().span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`"); + }; + + // Construct intrinsics::compare_bitwise(self, other) + let compare_bitwise = cx.std_path(&[sym::intrinsics, sym::compare_bitwise]); + let call_compare_bitwise = + cx.expr_call_global(span, compare_bitwise, thin_vec![cx.expr_self(span), rhs.clone()]); + + cx.expr_if(span, cond, call_compare_bitwise, Some(field_comparison)) + } else { + field_comparison } } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index baffc525d95a0..3c041fda3b453 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -278,6 +278,7 @@ pub(crate) struct Substructure<'a> { /// Verbatim access to any non-selflike arguments, i.e. arguments that /// don't have type `&Self`. pub nonselflike_args: &'a [Box], + pub selflike_args: &'a [Box], pub fields: &'a SubstructureFields<'a>, } @@ -879,6 +880,7 @@ impl<'a> TraitDef<'a> { self, struct_def, type_ident, + &selflike_args, &nonselflike_args, ) } else { @@ -935,6 +937,7 @@ impl<'a> TraitDef<'a> { self, enum_def, type_ident, + &selflike_args, &nonselflike_args, ) } else { @@ -971,11 +974,12 @@ impl<'a> MethodDef<'a> { cx: &ExtCtxt<'_>, trait_: &TraitDef<'_>, type_ident: Ident, + selflike_args: &[Box], nonselflike_args: &[Box], fields: &SubstructureFields<'_>, ) -> BlockOrExpr { let span = trait_.span; - let substructure = Substructure { type_ident, nonselflike_args, fields }; + let substructure = Substructure { type_ident, selflike_args, nonselflike_args, fields }; let mut f = self.combine_substructure.borrow_mut(); let f: &mut CombineSubstructureFunc<'_> = &mut *f; f(cx, span, &substructure) @@ -1150,6 +1154,7 @@ impl<'a> MethodDef<'a> { cx, trait_, type_ident, + selflike_args, nonselflike_args, &Struct(struct_def, selflike_fields), ) @@ -1161,6 +1166,7 @@ impl<'a> MethodDef<'a> { trait_: &TraitDef<'_>, struct_def: &VariantData, type_ident: Ident, + selflike_args: &[Box], nonselflike_args: &[Box], ) -> BlockOrExpr { let summary = trait_.summarise_struct(cx, struct_def); @@ -1169,6 +1175,7 @@ impl<'a> MethodDef<'a> { cx, trait_, type_ident, + selflike_args, nonselflike_args, &StaticStruct(struct_def, summary), ) @@ -1305,6 +1312,7 @@ impl<'a> MethodDef<'a> { cx, trait_, type_ident, + &selflike_args, nonselflike_args, &EnumDiscr(discr_field, None), ); @@ -1316,6 +1324,7 @@ impl<'a> MethodDef<'a> { cx, trait_, type_ident, + &selflike_args, nonselflike_args, &AllFieldlessEnum(enum_def), ); @@ -1329,6 +1338,7 @@ impl<'a> MethodDef<'a> { cx, trait_, type_ident, + &selflike_args, nonselflike_args, &EnumMatching(variant, Vec::new()), ); @@ -1381,6 +1391,7 @@ impl<'a> MethodDef<'a> { cx, trait_, type_ident, + &selflike_args, nonselflike_args, &substructure, ) @@ -1402,6 +1413,7 @@ impl<'a> MethodDef<'a> { cx, trait_, type_ident, + &selflike_args, nonselflike_args, &EnumMatching(v, Vec::new()), ) @@ -1448,6 +1460,7 @@ impl<'a> MethodDef<'a> { cx, trait_, type_ident, + &selflike_args.clone(), nonselflike_args, &EnumDiscr(discr_field, Some(get_match_expr(selflike_args))), ); @@ -1464,12 +1477,14 @@ impl<'a> MethodDef<'a> { trait_: &TraitDef<'_>, enum_def: &EnumDef, type_ident: Ident, + selflike_args: &[Box], nonselflike_args: &[Box], ) -> BlockOrExpr { self.call_substructure_method( cx, trait_, type_ident, + selflike_args, nonselflike_args, &StaticEnum(enum_def), ) diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 8a505668d0dad..d28bc220ecfe5 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -80,12 +80,14 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::breakpoint | sym::bswap | sym::caller_location + | sym::can_compare_bitwise | sym::carrying_mul_add | sym::ceilf16 | sym::ceilf32 | sym::ceilf64 | sym::ceilf128 | sym::cold_path + | sym::compare_bitwise | sym::const_eval_select | sym::contract_check_ensures | sym::contract_check_requires @@ -340,6 +342,18 @@ pub(crate) fn check_intrinsic_type( vec![Ty::new_mut_ptr(tcx, param(0)), Ty::new_imm_ptr(tcx, param(0)), tcx.types.usize], tcx.types.unit, ), + sym::compare_bitwise => { + let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::Anon }; + let first_arg = + Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0)); + let br = + ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BoundRegionKind::Anon }; + let second_arg = + Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0)); + + (1, 0, vec![first_arg, second_arg], tcx.types.bool) + } + sym::can_compare_bitwise => (1, 0, vec![], tcx.types.bool), sym::compare_bytes => { let byte_ptr = Ty::new_imm_ptr(tcx, tcx.types.u8); (0, 0, vec![byte_ptr, byte_ptr, tcx.types.usize], tcx.types.i32) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index d8f16ef5561db..797ac4f866191 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1992,6 +1992,14 @@ impl<'tcx> Ty<'tcx> { } } + pub fn is_pod(self, tcx: TyCtxt<'tcx>) -> bool { + match self.kind() { + ty::Int(..) | ty::Uint(..) | ty::Bool | ty::Char => true, + ty::Array(element_ty, _len) => element_ty.is_pod(tcx), + _ => false, + } + } + pub fn is_trivially_wf(self, tcx: TyCtxt<'tcx>) -> bool { match *self.kind() { ty::Bool diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index f25874fbbcb89..d3e3659fcf2d4 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -12,6 +12,7 @@ pub(super) struct LowerIntrinsics; impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let local_decls = &body.local_decls; + let typing_env = body.typing_env(tcx); for block in body.basic_blocks.as_mut() { let terminator = block.terminator.as_mut().unwrap(); if let TerminatorKind::Call { func, args, destination, target, .. } = @@ -20,6 +21,44 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { && let Some(intrinsic) = tcx.intrinsic(def_id) { match intrinsic.name { + sym::can_compare_bitwise => { + let mut can_compare = false; + + // The type that we want to check is the generic arg of can_compare_bitwise + let arg = generic_args[0].as_type().unwrap(); + + // can_compare_bitwise is only used when T is a struct, and our task is to + // check if all the fields of the struct are POD and if there is no padding + // between them. + let ty::Adt(adt_def, args) = arg.kind() else { unreachable!() }; + assert!(adt_def.is_struct()); + + // Check if all the struct fields are POD + let is_pod = + adt_def.all_fields().all(|field| field.ty(tcx, args).is_pod(tcx)); + + // Only query the layout if all the struct fields are POD. Then use the + // layout to check if there is any padding between them. + if is_pod && let Ok(layout) = tcx.layout_of(typing_env.as_query_input(arg)) + { + can_compare = layout.is_pod_layout( + &rustc_middle::ty::layout::LayoutCx::new(tcx, typing_env), + ) + } + + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( + *destination, + Rvalue::Use(Operand::Constant(Box::new(ConstOperand { + span: terminator.source_info.span, + user_ty: None, + const_: Const::from_bool(tcx, can_compare), + }))), + ))), + )); + terminator.kind = TerminatorKind::Goto { target: target.unwrap() }; + } sym::unreachable => { terminator.kind = TerminatorKind::Unreachable; } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index cd730ede4ba1f..fc6647cdec70d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -622,6 +622,7 @@ symbols! { call_ref_future, caller, caller_location, + can_compare_bitwise, capture_disjoint_fields, carrying_mul_add, catch_unwind, @@ -699,6 +700,7 @@ symbols! { collapse_debuginfo, column, common, + compare_bitwise, compare_bytes, compare_exchange, compare_exchange_weak, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 564a896076b0d..79d5367929995 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2579,6 +2579,26 @@ pub const unsafe fn typed_swap_nonoverlapping(x: *mut T, y: *mut T) { unsafe { ptr::swap_nonoverlapping(x, y, 1) }; } +#[rustc_nounwind] +#[inline] +#[rustc_intrinsic] +#[rustc_intrinsic_const_stable_indirect] +pub const fn can_compare_bitwise() -> bool; + +#[rustc_nounwind] +#[inline] +#[rustc_intrinsic] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn compare_bitwise(a: &T, b: &T) -> bool { + // SAFETY: These pointers were originally references to the same memory. + unsafe { + let len = size_of_val(a); + let a = ptr::from_ref(a).cast::(); + let b = ptr::from_ref(b).cast::(); + compare_bytes(a, b, len) == 0 + } +} + /// Returns whether we should perform some UB-checking at runtime. This eventually evaluates to /// `cfg!(ub_checks)`, but behaves different from `cfg!` when mixing crates built with different /// flags: if the crate has UB checks enabled or carries the `#[rustc_preserve_ub_checks]` diff --git a/tests/assembly-llvm/derive-partialeq.rs b/tests/assembly-llvm/derive-partialeq.rs new file mode 100644 index 0000000000000..61ee9446741dc --- /dev/null +++ b/tests/assembly-llvm/derive-partialeq.rs @@ -0,0 +1,22 @@ +//@ compile-flags: -Copt-level=3 +//@ assembly-output: emit-asm +//@ only-x86_64 + +#![crate_type = "lib"] + +#[derive(PartialEq)] +struct Foo { + a: u16, + b: u16, +} + +// CHECK-LABEL: two_u16: +#[no_mangle] +pub fn two_u16(a: &Foo, b: &Foo) -> bool { + // CHECK-NEXT: .cfi_startproc + // CHECK-NEXT: movl + // CHECK-NEXT: cmpl + // CHECK-NEXT: sete + // CHECK-NEXT: retq + a == b +} diff --git a/tests/ui/deriving/deriving-all-codegen.stdout b/tests/ui/deriving/deriving-all-codegen.stdout index a40dece22a261..6726a7ff1897e 100644 --- a/tests/ui/deriving/deriving-all-codegen.stdout +++ b/tests/ui/deriving/deriving-all-codegen.stdout @@ -135,7 +135,9 @@ impl ::core::marker::StructuralPartialEq for Point { } impl ::core::cmp::PartialEq for Point { #[inline] fn eq(&self, other: &Point) -> bool { - self.x == other.x && self.y == other.y + if ::core::intrinsics::can_compare_bitwise::() { + ::core::intrinsics::compare_bitwise(self, other) + } else { self.x == other.x && self.y == other.y } } } #[automatically_derived] @@ -223,7 +225,11 @@ impl ::core::marker::StructuralPartialEq for PackedPoint { } impl ::core::cmp::PartialEq for PackedPoint { #[inline] fn eq(&self, other: &PackedPoint) -> bool { - ({ self.x }) == ({ other.x }) && ({ self.y }) == ({ other.y }) + if ::core::intrinsics::can_compare_bitwise::() { + ::core::intrinsics::compare_bitwise(self, other) + } else { + ({ self.x }) == ({ other.x }) && ({ self.y }) == ({ other.y }) + } } } #[automatically_derived] @@ -483,10 +489,14 @@ impl ::core::marker::StructuralPartialEq for Big { } impl ::core::cmp::PartialEq for Big { #[inline] fn eq(&self, other: &Big) -> bool { - self.b1 == other.b1 && self.b2 == other.b2 && self.b3 == other.b3 && - self.b4 == other.b4 && self.b5 == other.b5 && - self.b6 == other.b6 && self.b7 == other.b7 && - self.b8 == other.b8 + if ::core::intrinsics::can_compare_bitwise::() { + ::core::intrinsics::compare_bitwise(self, other) + } else { + self.b1 == other.b1 && self.b2 == other.b2 && self.b3 == other.b3 + && self.b4 == other.b4 && self.b5 == other.b5 && + self.b6 == other.b6 && self.b7 == other.b7 && + self.b8 == other.b8 + } } } #[automatically_derived] @@ -602,11 +612,15 @@ impl ::core::marker::StructuralPartialEq for Reorder { } impl ::core::cmp::PartialEq for Reorder { #[inline] fn eq(&self, other: &Reorder) -> bool { - self.b2 == other.b2 && self.b4 == other.b4 && self.b5 == other.b5 && - self.b6 == other.b6 && self.b7 == other.b7 && - self.b8 == other.b8 && self.b10 == other.b10 && - self.b1 == other.b1 && self.b3 == other.b3 && - self._b == other._b && self.b9 == other.b9 + if ::core::intrinsics::can_compare_bitwise::() { + ::core::intrinsics::compare_bitwise(self, other) + } else { + self.b2 == other.b2 && self.b4 == other.b4 && self.b5 == other.b5 + && self.b6 == other.b6 && self.b7 == other.b7 && + self.b8 == other.b8 && self.b10 == other.b10 && + self.b1 == other.b1 && self.b3 == other.b3 && + self._b == other._b && self.b9 == other.b9 + } } } #[automatically_derived] @@ -844,7 +858,11 @@ impl T::A: ::core::cmp::PartialEq { #[inline] fn eq(&self, other: &Generic) -> bool { - self.t == other.t && self.ta == other.ta && self.u == other.u + if ::core::intrinsics::can_compare_bitwise::() { + ::core::intrinsics::compare_bitwise(self, other) + } else { + self.t == other.t && self.ta == other.ta && self.u == other.u + } } } #[automatically_derived] @@ -964,8 +982,12 @@ impl) -> bool { - ({ self.0 }) == ({ other.0 }) && ({ self.1 }) == ({ other.1 }) && - ({ self.2 }) == ({ other.2 }) + if ::core::intrinsics::can_compare_bitwise::() { + ::core::intrinsics::compare_bitwise(self, other) + } else { + ({ self.0 }) == ({ other.0 }) && ({ self.1 }) == ({ other.1 }) && + ({ self.2 }) == ({ other.2 }) + } } } #[automatically_derived] diff --git a/tests/ui/stats/macro-stats.stderr b/tests/ui/stats/macro-stats.stderr index 11a6bfdfcd292..03f1a62331d0e 100644 --- a/tests/ui/stats/macro-stats.stderr +++ b/tests/ui/stats/macro-stats.stderr @@ -8,9 +8,9 @@ macro-stats #[derive(Hash)] 2 17 8.5 macro-stats q! 1 26 26.0 519 519.0 macro-stats #[derive(Ord)] 1 15 15.0 503 503.0 macro-stats #[derive(Default)] 2 16 8.0 403 201.5 +macro-stats #[derive(PartialEq)] 1 11 11.0 402 402.0 macro-stats #[derive(Eq)] 1 11 11.0 325 325.0 macro-stats #[derive(Debug)] 1 8 8.0 277 277.0 -macro-stats #[derive(PartialEq)] 1 9 9.0 267 267.0 macro-stats #[derive(Copy)] 1 2 2.0 61 61.0 macro-stats p! 1 3 3.0 32 32.0 macro-stats trait_impl_tys! 1 2 2.0 28 28.0 From 53231e00b55eb6e7b57fe5c04c85cbb19fe815bd Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Mon, 24 Nov 2025 00:50:36 -0500 Subject: [PATCH 2/6] Add intrinsic_fallback_is_spec --- library/core/src/intrinsics/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 79d5367929995..7efddf6d2da84 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2589,6 +2589,7 @@ pub const fn can_compare_bitwise() -> bool; #[inline] #[rustc_intrinsic] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +#[miri::intrinsic_fallback_is_spec] pub const fn compare_bitwise(a: &T, b: &T) -> bool { // SAFETY: These pointers were originally references to the same memory. unsafe { From 507666c7663e83fdb6f2fd90c4bdde914a61a0a0 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Mon, 24 Nov 2025 01:09:11 -0500 Subject: [PATCH 3/6] Add a pre-codegen test --- ...rtialeq.cmp_has_float.PreCodegen.after.mir | 51 ++++++++++++++ ...ialeq.cmp_has_padding.PreCodegen.after.mir | 51 ++++++++++++++ ...tialeq.cmp_no_padding.PreCodegen.after.mir | 19 +++++ ..._no_padding_after_opt.PreCodegen.after.mir | 19 +++++ ....cmp_no_padding_array.PreCodegen.after.mir | 23 +++++++ .../mir-opt/pre-codegen/derived_partialeq.rs | 69 +++++++++++++++++++ 6 files changed, 232 insertions(+) create mode 100644 tests/mir-opt/pre-codegen/derived_partialeq.cmp_has_float.PreCodegen.after.mir create mode 100644 tests/mir-opt/pre-codegen/derived_partialeq.cmp_has_padding.PreCodegen.after.mir create mode 100644 tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding.PreCodegen.after.mir create mode 100644 tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding_after_opt.PreCodegen.after.mir create mode 100644 tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding_array.PreCodegen.after.mir create mode 100644 tests/mir-opt/pre-codegen/derived_partialeq.rs diff --git a/tests/mir-opt/pre-codegen/derived_partialeq.cmp_has_float.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/derived_partialeq.cmp_has_float.PreCodegen.after.mir new file mode 100644 index 0000000000000..ac166684324de --- /dev/null +++ b/tests/mir-opt/pre-codegen/derived_partialeq.cmp_has_float.PreCodegen.after.mir @@ -0,0 +1,51 @@ +// MIR for `cmp_has_float` after PreCodegen + +fn cmp_has_float(_1: &HasFloat, _2: &HasFloat) -> bool { + debug a => _1; + debug b => _2; + let mut _0: bool; + scope 1 (inlined std::cmp::impls::::eq) { + scope 2 (inlined ::eq) { + let mut _3: f32; + let mut _4: f32; + let mut _5: bool; + let mut _6: u32; + let mut _7: u32; + } + } + + bb0: { + StorageLive(_5); + StorageLive(_3); + _3 = copy ((*_1).0: f32); + StorageLive(_4); + _4 = copy ((*_2).0: f32); + _5 = Eq(move _3, move _4); + switchInt(move _5) -> [0: bb1, otherwise: bb2]; + } + + bb1: { + StorageDead(_4); + StorageDead(_3); + _0 = const false; + goto -> bb3; + } + + bb2: { + StorageDead(_4); + StorageDead(_3); + StorageLive(_6); + _6 = copy ((*_1).1: u32); + StorageLive(_7); + _7 = copy ((*_2).1: u32); + _0 = Eq(move _6, move _7); + StorageDead(_7); + StorageDead(_6); + goto -> bb3; + } + + bb3: { + StorageDead(_5); + return; + } +} diff --git a/tests/mir-opt/pre-codegen/derived_partialeq.cmp_has_padding.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/derived_partialeq.cmp_has_padding.PreCodegen.after.mir new file mode 100644 index 0000000000000..46dec20695aef --- /dev/null +++ b/tests/mir-opt/pre-codegen/derived_partialeq.cmp_has_padding.PreCodegen.after.mir @@ -0,0 +1,51 @@ +// MIR for `cmp_has_padding` after PreCodegen + +fn cmp_has_padding(_1: &HasPadding, _2: &HasPadding) -> bool { + debug a => _1; + debug b => _2; + let mut _0: bool; + scope 1 (inlined std::cmp::impls::::eq) { + scope 2 (inlined ::eq) { + let mut _3: u8; + let mut _4: u8; + let mut _5: bool; + let mut _6: u16; + let mut _7: u16; + } + } + + bb0: { + StorageLive(_5); + StorageLive(_3); + _3 = copy ((*_1).0: u8); + StorageLive(_4); + _4 = copy ((*_2).0: u8); + _5 = Eq(move _3, move _4); + switchInt(move _5) -> [0: bb1, otherwise: bb2]; + } + + bb1: { + StorageDead(_4); + StorageDead(_3); + _0 = const false; + goto -> bb3; + } + + bb2: { + StorageDead(_4); + StorageDead(_3); + StorageLive(_6); + _6 = copy ((*_1).1: u16); + StorageLive(_7); + _7 = copy ((*_2).1: u16); + _0 = Eq(move _6, move _7); + StorageDead(_7); + StorageDead(_6); + goto -> bb3; + } + + bb3: { + StorageDead(_5); + return; + } +} diff --git a/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding.PreCodegen.after.mir new file mode 100644 index 0000000000000..f74f8a44c95ec --- /dev/null +++ b/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding.PreCodegen.after.mir @@ -0,0 +1,19 @@ +// MIR for `cmp_no_padding` after PreCodegen + +fn cmp_no_padding(_1: &NoPadding, _2: &NoPadding) -> bool { + debug a => _1; + debug b => _2; + let mut _0: bool; + scope 1 (inlined std::cmp::impls::::eq) { + scope 2 (inlined ::eq) { + } + } + + bb0: { + _0 = compare_bitwise::(move _1, move _2) -> [return: bb1, unwind unreachable]; + } + + bb1: { + return; + } +} diff --git a/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding_after_opt.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding_after_opt.PreCodegen.after.mir new file mode 100644 index 0000000000000..c9b04acb319f1 --- /dev/null +++ b/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding_after_opt.PreCodegen.after.mir @@ -0,0 +1,19 @@ +// MIR for `cmp_no_padding_after_opt` after PreCodegen + +fn cmp_no_padding_after_opt(_1: &NoPaddingAfterOpt, _2: &NoPaddingAfterOpt) -> bool { + debug a => _1; + debug b => _2; + let mut _0: bool; + scope 1 (inlined std::cmp::impls::::eq) { + scope 2 (inlined ::eq) { + } + } + + bb0: { + _0 = compare_bitwise::(move _1, move _2) -> [return: bb1, unwind unreachable]; + } + + bb1: { + return; + } +} diff --git a/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding_array.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding_array.PreCodegen.after.mir new file mode 100644 index 0000000000000..1de1112059c28 --- /dev/null +++ b/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding_array.PreCodegen.after.mir @@ -0,0 +1,23 @@ +// MIR for `cmp_no_padding_array` after PreCodegen + +fn cmp_no_padding_array(_1: &NoPaddingArray, _2: &NoPaddingArray) -> bool { + debug a => _1; + debug b => _2; + let mut _0: bool; + scope 1 (inlined std::cmp::impls::::eq) { + scope 2 (inlined ::eq) { + scope 3 (inlined array::equality::::eq) { + scope 4 (inlined >::spec_eq) { + } + } + } + } + + bb0: { + _0 = compare_bitwise::(move _1, move _2) -> [return: bb1, unwind unreachable]; + } + + bb1: { + return; + } +} diff --git a/tests/mir-opt/pre-codegen/derived_partialeq.rs b/tests/mir-opt/pre-codegen/derived_partialeq.rs new file mode 100644 index 0000000000000..b915ebd4bdec0 --- /dev/null +++ b/tests/mir-opt/pre-codegen/derived_partialeq.rs @@ -0,0 +1,69 @@ +//@ compile-flags: -O -Zmir-opt-level=2 -Cdebuginfo=0 + +#![crate_type = "lib"] + +#[derive(PartialEq)] +pub struct NoPadding { + x: u16, + y: u16, +} + +// CHECK-LABEL: fn cmp_no_padding( +fn cmp_no_padding(a: &NoPadding, b: &NoPadding) -> bool { + // CHECK: compare_bitwise::( + a == b +} +// EMIT_MIR derived_partialeq.cmp_no_padding.PreCodegen.after.mir + +#[derive(PartialEq)] +pub struct NoPaddingArray { + x: [u16; 2], + y: u16, +} + +// CHECK-LABEL: fn cmp_no_padding_array( +fn cmp_no_padding_array(a: &NoPaddingArray, b: &NoPaddingArray) -> bool { + // CHECK: compare_bitwise::( + a == b +} +// EMIT_MIR derived_partialeq.cmp_no_padding_array.PreCodegen.after.mir + +#[derive(PartialEq)] +pub struct NoPaddingAfterOpt { + x: u8, + y: u16, + z: u8, +} + +// CHECK-LABEL: fn cmp_no_padding_after_opt( +fn cmp_no_padding_after_opt(a: &NoPaddingAfterOpt, b: &NoPaddingAfterOpt) -> bool { + // CHECK: compare_bitwise::( + a == b +} +// EMIT_MIR derived_partialeq.cmp_no_padding_after_opt.PreCodegen.after.mir + +#[derive(PartialEq)] +pub struct HasPadding { + x: u8, + y: u16, +} + +// CHECK-LABEL: fn cmp_has_padding( +fn cmp_has_padding(a: &HasPadding, b: &HasPadding) -> bool { + // CHECK-NOT: compare_bitwise + a == b +} +// EMIT_MIR derived_partialeq.cmp_has_padding.PreCodegen.after.mir + +#[derive(PartialEq)] +pub struct HasFloat { + x: f32, + y: u32, +} + +// CHECK-LABEL: fn cmp_has_float( +fn cmp_has_float(a: &HasFloat, b: &HasFloat) -> bool { + // CHECK-NOT: compare_bitwise + a == b +} +// EMIT_MIR derived_partialeq.cmp_has_float.PreCodegen.after.mir From 6c5a7213c4ae3721999b1eace420fc7bd3b4a7a6 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sun, 30 Nov 2025 16:00:28 -0500 Subject: [PATCH 4/6] Don't try to evaluate const FnDefs in KnownPanicsLint --- compiler/rustc_mir_transform/src/known_panics_lint.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 1f5d31932f1a2..679c91b66e913 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -255,6 +255,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } + // Don't try to evaluate the Operand::Const of calls to a concrete fn + if matches!(c.ty().kind(), ty::FnDef(..)) { + return None; + } + // Normalization needed b/c known panics lint runs in // `mir_drops_elaborated_and_const_checked`, which happens before // optimized MIR. Only after optimizing the MIR can we guarantee From f2018e82ebdf002bb46f0d97fdcb3655c5a94d31 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 6 Dec 2025 10:28:04 -0500 Subject: [PATCH 5/6] Add a fast-path for monomorphic unsizing coercions --- .../rustc_trait_selection/src/traits/select/confirmation.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 20a8842f2e8e5..9e55418b77e34 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -1185,6 +1185,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // `Struct` -> `Struct` (&ty::Adt(def, args_a), &ty::Adt(_, args_b)) => { + if args_a.is_empty() || args_b.is_empty() { + assert!(args_a.is_empty()); + assert!(args_b.is_empty()); + return Err(SelectionError::Unimplemented); + } let unsizing_params = tcx.unsizing_params_for_adt(def.did()); if unsizing_params.is_empty() { return Err(SelectionError::Unimplemented); From f67692a3069323baef14e140eb0701e0bd2311e8 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 6 Dec 2025 19:05:15 -0500 Subject: [PATCH 6/6] Expand to a call to compare_bytes instead --- .../src/deriving/cmp/partial_eq.rs | 41 +++++++++++++++---- compiler/rustc_expand/src/build.rs | 11 +++++ library/core/src/intrinsics/mod.rs | 15 ------- ...tialeq.cmp_no_padding.PreCodegen.after.mir | 33 ++++++++++++++- ..._no_padding_after_opt.PreCodegen.after.mir | 33 ++++++++++++++- ....cmp_no_padding_array.PreCodegen.after.mir | 33 ++++++++++++++- .../mir-opt/pre-codegen/derived_partialeq.rs | 10 ++--- tests/ui/deriving/deriving-all-codegen.stdout | 36 +++++++++++++--- tests/ui/stats/macro-stats.stderr | 2 +- 9 files changed, 177 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs index 196234553e302..30616e3a8a774 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs @@ -1,4 +1,7 @@ -use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, MetaItem, Mutability, Safety}; +use rustc_ast::{ + BinOpKind, BlockCheckMode, BorrowKind, Expr, ExprKind, GenericArg, MetaItem, MutTy, Mutability, + Safety, TyKind, UnsafeSource, +}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::{Ident, Span, kw, sym}; use thin_vec::thin_vec; @@ -189,7 +192,7 @@ fn get_substructure_equality_expr( span, true, cx.std_path(&[sym::intrinsics, sym::can_compare_bitwise]), - vec![GenericArg::Type(self_ty)], + vec![GenericArg::Type(self_ty.clone())], )); let cond = cx.expr_call(span, path, thin_vec![]); @@ -197,12 +200,36 @@ fn get_substructure_equality_expr( cx.dcx().span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`"); }; - // Construct intrinsics::compare_bitwise(self, other) - let compare_bitwise = cx.std_path(&[sym::intrinsics, sym::compare_bitwise]); - let call_compare_bitwise = - cx.expr_call_global(span, compare_bitwise, thin_vec![cx.expr_self(span), rhs.clone()]); + // Construct intrinsics::compare_bitwise( + // self as *const Self as *const u8, + // other as *const Self as *const u8, + // + // ); + let self_ptr_ty = cx.ty(span, TyKind::Ptr(MutTy { ty: self_ty, mutbl: Mutability::Not })); + let u8_ty = cx.ty_ident(span, Ident::with_dummy_span(sym::u8)); + let u8_ptr_ty = cx.ty(span, TyKind::Ptr(MutTy { ty: u8_ty, mutbl: Mutability::Not })); - cx.expr_if(span, cond, call_compare_bitwise, Some(field_comparison)) + let self_ptr = cx.expr_cast(span, cx.expr_self(span), self_ptr_ty.clone()); + let self_ptr_u8 = cx.expr_cast(span, self_ptr, u8_ptr_ty.clone()); + + let other_ptr = cx.expr_cast(span, rhs.clone(), self_ptr_ty); + let other_ptr_u8 = cx.expr_cast(span, other_ptr, u8_ptr_ty); + + let size_of_val = cx.std_path(&[sym::intrinsics, sym::size_of_val]); + let size = cx.expr_call_global(span, size_of_val, thin_vec![cx.expr_self(span)]); + + let compare_bytes = cx.std_path(&[sym::intrinsics, sym::compare_bytes]); + + let call_compare_bytes = + cx.expr_call_global(span, compare_bytes, thin_vec![self_ptr_u8, other_ptr_u8, size]); + + let mut call = cx.block_expr(call_compare_bytes); + call.rules = BlockCheckMode::Unsafe(UnsafeSource::CompilerGenerated); + let call = cx.expr_block(call); + + let is_eq = cx.expr_binary(span, BinOpKind::Eq, call, cx.expr_i32(span, 0)); + + cx.expr_if(span, cond, is_eq, Some(field_comparison)) } else { field_comparison } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 6be65b0fff16f..7f6fa9ce4f2f4 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -394,6 +394,11 @@ impl<'a> ExtCtxt<'a> { let pathexpr = self.expr_path(self.path_global(sp, fn_path)); self.expr_call(sp, pathexpr, args) } + + pub fn expr_cast(&self, span: Span, src: Box, ty: Box) -> Box { + self.expr(span, ast::ExprKind::Cast(src, ty)) + } + pub fn expr_block(&self, b: Box) -> Box { self.expr(b.span, ast::ExprKind::Block(b, None)) } @@ -445,6 +450,12 @@ impl<'a> ExtCtxt<'a> { self.expr(span, ast::ExprKind::Lit(lit)) } + pub fn expr_i32(&self, span: Span, n: u32) -> Box { + let suffix = Some(ast::IntTy::I32.name()); + let lit = token::Lit::new(token::Integer, sym::integer(n), suffix); + self.expr(span, ast::ExprKind::Lit(lit)) + } + pub fn expr_bool(&self, span: Span, value: bool) -> Box { let lit = token::Lit::new(token::Bool, if value { kw::True } else { kw::False }, None); self.expr(span, ast::ExprKind::Lit(lit)) diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 7efddf6d2da84..6e0442363f3bc 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2585,21 +2585,6 @@ pub const unsafe fn typed_swap_nonoverlapping(x: *mut T, y: *mut T) { #[rustc_intrinsic_const_stable_indirect] pub const fn can_compare_bitwise() -> bool; -#[rustc_nounwind] -#[inline] -#[rustc_intrinsic] -#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] -#[miri::intrinsic_fallback_is_spec] -pub const fn compare_bitwise(a: &T, b: &T) -> bool { - // SAFETY: These pointers were originally references to the same memory. - unsafe { - let len = size_of_val(a); - let a = ptr::from_ref(a).cast::(); - let b = ptr::from_ref(b).cast::(); - compare_bytes(a, b, len) == 0 - } -} - /// Returns whether we should perform some UB-checking at runtime. This eventually evaluates to /// `cfg!(ub_checks)`, but behaves different from `cfg!` when mixing crates built with different /// flags: if the crate has UB checks enabled or carries the `#[rustc_preserve_ub_checks]` diff --git a/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding.PreCodegen.after.mir index f74f8a44c95ec..ca9bf611e3676 100644 --- a/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding.PreCodegen.after.mir @@ -6,14 +6,45 @@ fn cmp_no_padding(_1: &NoPadding, _2: &NoPadding) -> bool { let mut _0: bool; scope 1 (inlined std::cmp::impls::::eq) { scope 2 (inlined ::eq) { + let mut _3: *const NoPadding; + let mut _4: *const u8; + let mut _5: *const NoPadding; + let mut _6: *const u8; + let mut _7: *const NoPadding; + let mut _8: usize; + let mut _9: i32; } } bb0: { - _0 = compare_bitwise::(move _1, move _2) -> [return: bb1, unwind unreachable]; + StorageLive(_9); + StorageLive(_4); + StorageLive(_3); + _3 = &raw const (*_1); + _4 = copy _3 as *const u8 (PtrToPtr); + StorageDead(_3); + StorageLive(_6); + StorageLive(_5); + _5 = &raw const (*_2); + _6 = copy _5 as *const u8 (PtrToPtr); + StorageDead(_5); + StorageLive(_8); + StorageLive(_7); + _7 = &raw const (*_1); + _8 = std::intrinsics::size_of_val::(move _7) -> [return: bb1, unwind unreachable]; } bb1: { + StorageDead(_7); + _9 = compare_bytes(move _4, move _6, move _8) -> [return: bb2, unwind unreachable]; + } + + bb2: { + StorageDead(_8); + StorageDead(_6); + StorageDead(_4); + _0 = Eq(move _9, const 0_i32); + StorageDead(_9); return; } } diff --git a/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding_after_opt.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding_after_opt.PreCodegen.after.mir index c9b04acb319f1..cbaba36d3f9d0 100644 --- a/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding_after_opt.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding_after_opt.PreCodegen.after.mir @@ -6,14 +6,45 @@ fn cmp_no_padding_after_opt(_1: &NoPaddingAfterOpt, _2: &NoPaddingAfterOpt) -> b let mut _0: bool; scope 1 (inlined std::cmp::impls::::eq) { scope 2 (inlined ::eq) { + let mut _3: *const NoPaddingAfterOpt; + let mut _4: *const u8; + let mut _5: *const NoPaddingAfterOpt; + let mut _6: *const u8; + let mut _7: *const NoPaddingAfterOpt; + let mut _8: usize; + let mut _9: i32; } } bb0: { - _0 = compare_bitwise::(move _1, move _2) -> [return: bb1, unwind unreachable]; + StorageLive(_9); + StorageLive(_4); + StorageLive(_3); + _3 = &raw const (*_1); + _4 = copy _3 as *const u8 (PtrToPtr); + StorageDead(_3); + StorageLive(_6); + StorageLive(_5); + _5 = &raw const (*_2); + _6 = copy _5 as *const u8 (PtrToPtr); + StorageDead(_5); + StorageLive(_8); + StorageLive(_7); + _7 = &raw const (*_1); + _8 = std::intrinsics::size_of_val::(move _7) -> [return: bb1, unwind unreachable]; } bb1: { + StorageDead(_7); + _9 = compare_bytes(move _4, move _6, move _8) -> [return: bb2, unwind unreachable]; + } + + bb2: { + StorageDead(_8); + StorageDead(_6); + StorageDead(_4); + _0 = Eq(move _9, const 0_i32); + StorageDead(_9); return; } } diff --git a/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding_array.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding_array.PreCodegen.after.mir index 1de1112059c28..4e4bfc91a54ec 100644 --- a/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding_array.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/derived_partialeq.cmp_no_padding_array.PreCodegen.after.mir @@ -6,6 +6,13 @@ fn cmp_no_padding_array(_1: &NoPaddingArray, _2: &NoPaddingArray) -> bool { let mut _0: bool; scope 1 (inlined std::cmp::impls::::eq) { scope 2 (inlined ::eq) { + let mut _3: *const NoPaddingArray; + let mut _4: *const u8; + let mut _5: *const NoPaddingArray; + let mut _6: *const u8; + let mut _7: *const NoPaddingArray; + let mut _8: usize; + let mut _9: i32; scope 3 (inlined array::equality::::eq) { scope 4 (inlined >::spec_eq) { } @@ -14,10 +21,34 @@ fn cmp_no_padding_array(_1: &NoPaddingArray, _2: &NoPaddingArray) -> bool { } bb0: { - _0 = compare_bitwise::(move _1, move _2) -> [return: bb1, unwind unreachable]; + StorageLive(_9); + StorageLive(_4); + StorageLive(_3); + _3 = &raw const (*_1); + _4 = copy _3 as *const u8 (PtrToPtr); + StorageDead(_3); + StorageLive(_6); + StorageLive(_5); + _5 = &raw const (*_2); + _6 = copy _5 as *const u8 (PtrToPtr); + StorageDead(_5); + StorageLive(_8); + StorageLive(_7); + _7 = &raw const (*_1); + _8 = std::intrinsics::size_of_val::(move _7) -> [return: bb1, unwind unreachable]; } bb1: { + StorageDead(_7); + _9 = compare_bytes(move _4, move _6, move _8) -> [return: bb2, unwind unreachable]; + } + + bb2: { + StorageDead(_8); + StorageDead(_6); + StorageDead(_4); + _0 = Eq(move _9, const 0_i32); + StorageDead(_9); return; } } diff --git a/tests/mir-opt/pre-codegen/derived_partialeq.rs b/tests/mir-opt/pre-codegen/derived_partialeq.rs index b915ebd4bdec0..4877fab7c38f9 100644 --- a/tests/mir-opt/pre-codegen/derived_partialeq.rs +++ b/tests/mir-opt/pre-codegen/derived_partialeq.rs @@ -10,7 +10,7 @@ pub struct NoPadding { // CHECK-LABEL: fn cmp_no_padding( fn cmp_no_padding(a: &NoPadding, b: &NoPadding) -> bool { - // CHECK: compare_bitwise::( + // CHECK: compare_bytes( a == b } // EMIT_MIR derived_partialeq.cmp_no_padding.PreCodegen.after.mir @@ -23,7 +23,7 @@ pub struct NoPaddingArray { // CHECK-LABEL: fn cmp_no_padding_array( fn cmp_no_padding_array(a: &NoPaddingArray, b: &NoPaddingArray) -> bool { - // CHECK: compare_bitwise::( + // CHECK: compare_bytes( a == b } // EMIT_MIR derived_partialeq.cmp_no_padding_array.PreCodegen.after.mir @@ -37,7 +37,7 @@ pub struct NoPaddingAfterOpt { // CHECK-LABEL: fn cmp_no_padding_after_opt( fn cmp_no_padding_after_opt(a: &NoPaddingAfterOpt, b: &NoPaddingAfterOpt) -> bool { - // CHECK: compare_bitwise::( + // CHECK: compare_bytes( a == b } // EMIT_MIR derived_partialeq.cmp_no_padding_after_opt.PreCodegen.after.mir @@ -50,7 +50,7 @@ pub struct HasPadding { // CHECK-LABEL: fn cmp_has_padding( fn cmp_has_padding(a: &HasPadding, b: &HasPadding) -> bool { - // CHECK-NOT: compare_bitwise + // CHECK-NOT: compare_bytes( a == b } // EMIT_MIR derived_partialeq.cmp_has_padding.PreCodegen.after.mir @@ -63,7 +63,7 @@ pub struct HasFloat { // CHECK-LABEL: fn cmp_has_float( fn cmp_has_float(a: &HasFloat, b: &HasFloat) -> bool { - // CHECK-NOT: compare_bitwise + // CHECK-NOT: compare_bytes( a == b } // EMIT_MIR derived_partialeq.cmp_has_float.PreCodegen.after.mir diff --git a/tests/ui/deriving/deriving-all-codegen.stdout b/tests/ui/deriving/deriving-all-codegen.stdout index 6726a7ff1897e..484f9a602ab6c 100644 --- a/tests/ui/deriving/deriving-all-codegen.stdout +++ b/tests/ui/deriving/deriving-all-codegen.stdout @@ -136,7 +136,11 @@ impl ::core::cmp::PartialEq for Point { #[inline] fn eq(&self, other: &Point) -> bool { if ::core::intrinsics::can_compare_bitwise::() { - ::core::intrinsics::compare_bitwise(self, other) + (unsafe { + ::core::intrinsics::compare_bytes(self as *const Self as + *const u8, other as *const Self as *const u8, + ::core::intrinsics::size_of_val(self)) + }) == 0i32 } else { self.x == other.x && self.y == other.y } } } @@ -226,7 +230,11 @@ impl ::core::cmp::PartialEq for PackedPoint { #[inline] fn eq(&self, other: &PackedPoint) -> bool { if ::core::intrinsics::can_compare_bitwise::() { - ::core::intrinsics::compare_bitwise(self, other) + (unsafe { + ::core::intrinsics::compare_bytes(self as *const Self as + *const u8, other as *const Self as *const u8, + ::core::intrinsics::size_of_val(self)) + }) == 0i32 } else { ({ self.x }) == ({ other.x }) && ({ self.y }) == ({ other.y }) } @@ -490,7 +498,11 @@ impl ::core::cmp::PartialEq for Big { #[inline] fn eq(&self, other: &Big) -> bool { if ::core::intrinsics::can_compare_bitwise::() { - ::core::intrinsics::compare_bitwise(self, other) + (unsafe { + ::core::intrinsics::compare_bytes(self as *const Self as + *const u8, other as *const Self as *const u8, + ::core::intrinsics::size_of_val(self)) + }) == 0i32 } else { self.b1 == other.b1 && self.b2 == other.b2 && self.b3 == other.b3 && self.b4 == other.b4 && self.b5 == other.b5 && @@ -613,7 +625,11 @@ impl ::core::cmp::PartialEq for Reorder { #[inline] fn eq(&self, other: &Reorder) -> bool { if ::core::intrinsics::can_compare_bitwise::() { - ::core::intrinsics::compare_bitwise(self, other) + (unsafe { + ::core::intrinsics::compare_bytes(self as *const Self as + *const u8, other as *const Self as *const u8, + ::core::intrinsics::size_of_val(self)) + }) == 0i32 } else { self.b2 == other.b2 && self.b4 == other.b4 && self.b5 == other.b5 && self.b6 == other.b6 && self.b7 == other.b7 && @@ -859,7 +875,11 @@ impl #[inline] fn eq(&self, other: &Generic) -> bool { if ::core::intrinsics::can_compare_bitwise::() { - ::core::intrinsics::compare_bitwise(self, other) + (unsafe { + ::core::intrinsics::compare_bytes(self as *const Self as + *const u8, other as *const Self as *const u8, + ::core::intrinsics::size_of_val(self)) + }) == 0i32 } else { self.t == other.t && self.ta == other.ta && self.u == other.u } @@ -983,7 +1003,11 @@ impl) -> bool { if ::core::intrinsics::can_compare_bitwise::() { - ::core::intrinsics::compare_bitwise(self, other) + (unsafe { + ::core::intrinsics::compare_bytes(self as *const Self as + *const u8, other as *const Self as *const u8, + ::core::intrinsics::size_of_val(self)) + }) == 0i32 } else { ({ self.0 }) == ({ other.0 }) && ({ self.1 }) == ({ other.1 }) && ({ self.2 }) == ({ other.2 }) diff --git a/tests/ui/stats/macro-stats.stderr b/tests/ui/stats/macro-stats.stderr index 03f1a62331d0e..a48b920a74d0d 100644 --- a/tests/ui/stats/macro-stats.stderr +++ b/tests/ui/stats/macro-stats.stderr @@ -4,11 +4,11 @@ macro-stats Macro Name Uses Lines Avg Lines B macro-stats ----------------------------------------------------------------------------------- macro-stats #[derive(Clone)] 8 67 8.4 1_879 234.9 macro-stats #[derive(PartialOrd)] 1 17 17.0 675 675.0 +macro-stats #[derive(PartialEq)] 1 15 15.0 604 604.0 macro-stats #[derive(Hash)] 2 17 8.5 577 288.5 macro-stats q! 1 26 26.0 519 519.0 macro-stats #[derive(Ord)] 1 15 15.0 503 503.0 macro-stats #[derive(Default)] 2 16 8.0 403 201.5 -macro-stats #[derive(PartialEq)] 1 11 11.0 402 402.0 macro-stats #[derive(Eq)] 1 11 11.0 325 325.0 macro-stats #[derive(Debug)] 1 8 8.0 277 277.0 macro-stats #[derive(Copy)] 1 2 2.0 61 61.0