Skip to content

Commit c468ee3

Browse files
committed
Simplify raw_eq to Transmute+Eq for sizes with primitives
1 parent 52ecad9 commit c468ee3

8 files changed

Lines changed: 142 additions & 23 deletions

compiler/rustc_mir_transform/src/instsimplify.rs

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
//! Performs various peephole optimizations.
22
3-
use rustc_abi::ExternAbi;
3+
use rustc_abi::{ExternAbi, Integer};
44
use rustc_hir::{LangItem, find_attr};
5+
use rustc_index::IndexVec;
56
use rustc_middle::bug;
67
use rustc_middle::mir::visit::MutVisitor;
78
use rustc_middle::mir::*;
8-
use rustc_middle::ty::layout::ValidityRequirement;
9+
use rustc_middle::ty::layout::{IntegerExt, ValidityRequirement};
910
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, layout};
1011
use rustc_span::{Symbol, sym};
1112

@@ -33,10 +34,10 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify {
3334
if !preserve_ub_checks {
3435
SimplifyUbCheck { tcx }.visit_body(body);
3536
}
36-
let ctx = InstSimplifyContext {
37+
let mut ctx = InstSimplifyContext {
3738
tcx,
38-
local_decls: &body.local_decls,
3939
typing_env: body.typing_env(tcx),
40+
local_decls: &mut body.local_decls,
4041
};
4142
for block in body.basic_blocks.as_mut() {
4243
for statement in block.statements.iter_mut() {
@@ -55,6 +56,7 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify {
5556
let terminator = block.terminator.as_mut().unwrap();
5657
ctx.simplify_primitive_clone(terminator, &mut block.statements);
5758
ctx.simplify_size_or_align_of_val(terminator, &mut block.statements);
59+
ctx.simplify_raw_eq(terminator, &mut block.statements);
5860
ctx.simplify_intrinsic_assert(terminator);
5961
ctx.simplify_nounwind_call(terminator);
6062
simplify_duplicate_switch_targets(terminator);
@@ -68,7 +70,7 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify {
6870

6971
struct InstSimplifyContext<'a, 'tcx> {
7072
tcx: TyCtxt<'tcx>,
71-
local_decls: &'a LocalDecls<'tcx>,
73+
local_decls: &'a mut IndexVec<Local, LocalDecl<'tcx>>,
7274
typing_env: ty::TypingEnv<'tcx>,
7375
}
7476

@@ -318,6 +320,63 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
318320
}
319321
}
320322

323+
/// Simplify `raw_eq` intrinsic calls to `Eq` when the type has the size of a primitive.
324+
///
325+
/// For example, replace `raw_eq::<[u8; 4]>(a, b)` with `Eq(Transmute(a), Transmute(b))`.
326+
fn simplify_raw_eq(
327+
&mut self,
328+
terminator: &mut Terminator<'tcx>,
329+
statements: &mut Vec<Statement<'tcx>>,
330+
) {
331+
let tcx = self.tcx;
332+
let source_info = terminator.source_info;
333+
let span = source_info.span;
334+
if let TerminatorKind::Call {
335+
func, args, destination, target: Some(destination_block), ..
336+
} = &terminator.kind
337+
&& args.len() == 2
338+
&& let Some((fn_def_id, generics)) = func.const_fn_def()
339+
&& tcx.is_intrinsic(fn_def_id, sym::raw_eq)
340+
&& let generic_ty = generics.type_at(0)
341+
&& let Ok(layout) = tcx.layout_of(self.typing_env.as_query_input(generic_ty))
342+
&& let Ok(integer) = Integer::from_size(layout.size)
343+
{
344+
let ref_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, generic_ty);
345+
let uint_ty = integer.to_ty(tcx, false);
346+
347+
let mut transmute_operand = |op: &Operand<'tcx>| -> Operand<'tcx> {
348+
let ref_local = self.local_decls.push(LocalDecl::new(ref_ty, span));
349+
statements.push(Statement::new(
350+
source_info,
351+
StatementKind::Assign(Box::new((
352+
Place::from(ref_local),
353+
Rvalue::Use(op.clone(), WithRetag::Yes),
354+
))),
355+
));
356+
let place = Place::from(ref_local).project_deeper(&[ProjectionElem::Deref], tcx);
357+
let int_local = self.local_decls.push(LocalDecl::new(uint_ty, span));
358+
statements.push(Statement::new(
359+
source_info,
360+
StatementKind::Assign(Box::new((
361+
Place::from(int_local),
362+
Rvalue::Cast(CastKind::Transmute, Operand::Copy(place), uint_ty),
363+
))),
364+
));
365+
Operand::Move(Place::from(int_local))
366+
};
367+
let lhs_op = transmute_operand(&args[0].node);
368+
let rhs_op = transmute_operand(&args[1].node);
369+
statements.push(Statement::new(
370+
source_info,
371+
StatementKind::Assign(Box::new((
372+
*destination,
373+
Rvalue::BinaryOp(BinOp::Eq, Box::new((lhs_op, rhs_op))),
374+
))),
375+
));
376+
terminator.kind = TerminatorKind::Goto { target: *destination_block };
377+
}
378+
}
379+
321380
fn simplify_nounwind_call(&self, terminator: &mut Terminator<'tcx>) {
322381
let TerminatorKind::Call { ref func, ref mut unwind, .. } = terminator.kind else {
323382
return;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
- // MIR for `inner_array` before InstSimplify-after-simplifycfg
2+
+ // MIR for `inner_array` after InstSimplify-after-simplifycfg
3+
4+
fn inner_array(_1: &&[i32; 2], _2: &&[i32; 2]) -> bool {
5+
let mut _0: bool;
6+
+ let mut _3: &[i32; 2];
7+
+ let mut _4: u64;
8+
+ let mut _5: &[i32; 2];
9+
+ let mut _6: u64;
10+
11+
bb0: {
12+
- _0 = raw_eq::<[i32; 2]>(copy (*_1), copy (*_2)) -> [return: bb1, unwind unreachable];
13+
+ _3 = copy (*_1);
14+
+ _4 = copy (*_3) as u64 (Transmute);
15+
+ _5 = copy (*_2);
16+
+ _6 = copy (*_5) as u64 (Transmute);
17+
+ _0 = Eq(move _4, move _6);
18+
+ goto -> bb1;
19+
}
20+
21+
bb1: {
22+
return;
23+
}
24+
}
25+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//@ test-mir-pass: InstSimplify-after-simplifycfg
2+
#![crate_type = "lib"]
3+
#![feature(core_intrinsics)]
4+
#![feature(custom_mir)]
5+
6+
// Custom MIR so we can get an argument that's not just a local directly
7+
use std::intrinsics::mir::*;
8+
use std::intrinsics::raw_eq;
9+
10+
// EMIT_MIR raw_eq.inner_array.InstSimplify-after-simplifycfg.diff
11+
#[custom_mir(dialect = "runtime")]
12+
pub fn inner_array(a: &&[i32; 2], b: &&[i32; 2]) -> bool {
13+
// CHECK-LABEL: fn inner_array(_1: &&[i32; 2], _2: &&[i32; 2]) -> bool
14+
// CHECK: [[AREF:_.+]] = copy (*_1);
15+
// CHECK: [[AINT:_.+]] = copy (*[[AREF]]) as u64 (Transmute);
16+
// CHECK: [[BREF:_.+]] = copy (*_2);
17+
// CHECK: [[BINT:_.+]] = copy (*[[BREF]]) as u64 (Transmute);
18+
// CHECK: _0 = Eq(move [[AINT]], move [[BINT]]);
19+
mir! {
20+
{
21+
Call(RET = raw_eq(*a, *b), ReturnTo(ret), UnwindUnreachable())
22+
}
23+
ret = {
24+
Return()
25+
}
26+
}
27+
}

tests/mir-opt/pre-codegen/array_eq.eq_ipv4.PreCodegen.after.panic-abort.mir

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ fn eq_ipv4(_1: &[u8; 4], _2: &[u8; 4]) -> bool {
44
debug a => _1;
55
debug b => _2;
66
let mut _0: bool;
7+
let mut _3: u32;
8+
let mut _4: u32;
79
scope 1 (inlined std::cmp::impls::<impl PartialEq for &[u8; 4]>::eq) {
810
scope 2 (inlined array::equality::<impl PartialEq for [u8; 4]>::eq) {
911
scope 3 (inlined <u8 as array::equality::SpecArrayEq<u8, 4>>::spec_eq) {
@@ -12,10 +14,9 @@ fn eq_ipv4(_1: &[u8; 4], _2: &[u8; 4]) -> bool {
1214
}
1315

1416
bb0: {
15-
_0 = raw_eq::<[u8; 4]>(move _1, move _2) -> [return: bb1, unwind unreachable];
16-
}
17-
18-
bb1: {
17+
_3 = copy (*_1) as u32 (Transmute);
18+
_4 = copy (*_2) as u32 (Transmute);
19+
_0 = Eq(move _3, move _4);
1920
return;
2021
}
2122
}

tests/mir-opt/pre-codegen/array_eq.eq_ipv4.PreCodegen.after.panic-unwind.mir

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ fn eq_ipv4(_1: &[u8; 4], _2: &[u8; 4]) -> bool {
44
debug a => _1;
55
debug b => _2;
66
let mut _0: bool;
7+
let mut _3: u32;
8+
let mut _4: u32;
79
scope 1 (inlined std::cmp::impls::<impl PartialEq for &[u8; 4]>::eq) {
810
scope 2 (inlined array::equality::<impl PartialEq for [u8; 4]>::eq) {
911
scope 3 (inlined <u8 as array::equality::SpecArrayEq<u8, 4>>::spec_eq) {
@@ -12,10 +14,9 @@ fn eq_ipv4(_1: &[u8; 4], _2: &[u8; 4]) -> bool {
1214
}
1315

1416
bb0: {
15-
_0 = raw_eq::<[u8; 4]>(move _1, move _2) -> [return: bb1, unwind unreachable];
16-
}
17-
18-
bb1: {
17+
_3 = copy (*_1) as u32 (Transmute);
18+
_4 = copy (*_2) as u32 (Transmute);
19+
_0 = Eq(move _3, move _4);
1920
return;
2021
}
2122
}

tests/mir-opt/pre-codegen/array_eq.eq_ipv6.PreCodegen.after.panic-abort.mir

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ fn eq_ipv6(_1: &[u16; 8], _2: &[u16; 8]) -> bool {
44
debug a => _1;
55
debug b => _2;
66
let mut _0: bool;
7+
let mut _3: u128;
8+
let mut _4: u128;
79
scope 1 (inlined std::cmp::impls::<impl PartialEq for &[u16; 8]>::eq) {
810
scope 2 (inlined array::equality::<impl PartialEq for [u16; 8]>::eq) {
911
scope 3 (inlined <u16 as array::equality::SpecArrayEq<u16, 8>>::spec_eq) {
@@ -12,10 +14,9 @@ fn eq_ipv6(_1: &[u16; 8], _2: &[u16; 8]) -> bool {
1214
}
1315

1416
bb0: {
15-
_0 = raw_eq::<[u16; 8]>(move _1, move _2) -> [return: bb1, unwind unreachable];
16-
}
17-
18-
bb1: {
17+
_3 = copy (*_1) as u128 (Transmute);
18+
_4 = copy (*_2) as u128 (Transmute);
19+
_0 = Eq(move _3, move _4);
1920
return;
2021
}
2122
}

tests/mir-opt/pre-codegen/array_eq.eq_ipv6.PreCodegen.after.panic-unwind.mir

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ fn eq_ipv6(_1: &[u16; 8], _2: &[u16; 8]) -> bool {
44
debug a => _1;
55
debug b => _2;
66
let mut _0: bool;
7+
let mut _3: u128;
8+
let mut _4: u128;
79
scope 1 (inlined std::cmp::impls::<impl PartialEq for &[u16; 8]>::eq) {
810
scope 2 (inlined array::equality::<impl PartialEq for [u16; 8]>::eq) {
911
scope 3 (inlined <u16 as array::equality::SpecArrayEq<u16, 8>>::spec_eq) {
@@ -12,10 +14,9 @@ fn eq_ipv6(_1: &[u16; 8], _2: &[u16; 8]) -> bool {
1214
}
1315

1416
bb0: {
15-
_0 = raw_eq::<[u16; 8]>(move _1, move _2) -> [return: bb1, unwind unreachable];
16-
}
17-
18-
bb1: {
17+
_3 = copy (*_1) as u128 (Transmute);
18+
_4 = copy (*_2) as u128 (Transmute);
19+
_0 = Eq(move _3, move _4);
1920
return;
2021
}
2122
}

tests/mir-opt/pre-codegen/array_eq.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,17 @@ pub unsafe fn eq_odd_length<T: Copy>(a: &[u8; 3], b: &[u8; 3]) -> bool {
1313
// EMIT_MIR array_eq.eq_ipv4.PreCodegen.after.mir
1414
pub unsafe fn eq_ipv4<T: Copy>(a: &[u8; 4], b: &[u8; 4]) -> bool {
1515
// CHECK-LABEL: fn eq_ipv4(_1: &[u8; 4], _2: &[u8; 4]) -> bool
16-
// CHECK: _0 = raw_eq::<[u8; 4]>(move _1, move _2)
16+
// CHECK: [[A:_.+]] = copy (*_1) as u32 (Transmute);
17+
// CHECK: [[B:_.+]] = copy (*_2) as u32 (Transmute);
18+
// CHECK: _0 = Eq(move [[A]], move [[B]]);
1719
a == b
1820
}
1921

2022
// EMIT_MIR array_eq.eq_ipv6.PreCodegen.after.mir
2123
pub unsafe fn eq_ipv6<T: Copy>(a: &[u16; 8], b: &[u16; 8]) -> bool {
2224
// CHECK-LABEL: fn eq_ipv6(_1: &[u16; 8], _2: &[u16; 8]) -> bool
23-
// CHECK: _0 = raw_eq::<[u16; 8]>(move _1, move _2)
25+
// CHECK: [[A:_.+]] = copy (*_1) as u128 (Transmute);
26+
// CHECK: [[B:_.+]] = copy (*_2) as u128 (Transmute);
27+
// CHECK: _0 = Eq(move [[A]], move [[B]]);
2428
a == b
2529
}

0 commit comments

Comments
 (0)