Skip to content

Commit c8ff0e0

Browse files
committed
Simplify binary ops.
1 parent b8c2074 commit c8ff0e0

19 files changed

+806
-429
lines changed

compiler/rustc_mir_transform/src/gvn.rs

+109-2
Original file line numberDiff line numberDiff line change
@@ -338,11 +338,20 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
338338
Some(self.insert(Value::Constant { value, disambiguator }))
339339
}
340340

341+
fn insert_bool(&mut self, flag: bool) -> VnIndex {
342+
// Booleans are deterministic.
343+
self.insert(Value::Constant { value: Const::from_bool(self.tcx, flag), disambiguator: 0 })
344+
}
345+
341346
fn insert_scalar(&mut self, scalar: Scalar, ty: Ty<'tcx>) -> VnIndex {
342347
self.insert_constant(Const::from_scalar(self.tcx, scalar, ty))
343348
.expect("scalars are deterministic")
344349
}
345350

351+
fn insert_tuple(&mut self, values: Vec<VnIndex>) -> VnIndex {
352+
self.insert(Value::Aggregate(AggregateTy::Tuple, VariantIdx::from_u32(0), values))
353+
}
354+
346355
#[instrument(level = "trace", skip(self), ret)]
347356
fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
348357
use Value::*;
@@ -775,14 +784,26 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
775784
Value::Cast { kind, value, from, to }
776785
}
777786
Rvalue::BinaryOp(op, box (ref mut lhs, ref mut rhs)) => {
787+
let ty = lhs.ty(self.local_decls, self.tcx);
778788
let lhs = self.simplify_operand(lhs, location);
779789
let rhs = self.simplify_operand(rhs, location);
780-
Value::BinaryOp(op, lhs?, rhs?)
790+
let lhs = lhs?;
791+
let rhs = rhs?;
792+
if let Some(value) = self.simplify_binary(op, false, ty, lhs, rhs) {
793+
return Some(value);
794+
}
795+
Value::BinaryOp(op, lhs, rhs)
781796
}
782797
Rvalue::CheckedBinaryOp(op, box (ref mut lhs, ref mut rhs)) => {
798+
let ty = lhs.ty(self.local_decls, self.tcx);
783799
let lhs = self.simplify_operand(lhs, location);
784800
let rhs = self.simplify_operand(rhs, location);
785-
Value::CheckedBinaryOp(op, lhs?, rhs?)
801+
let lhs = lhs?;
802+
let rhs = rhs?;
803+
if let Some(value) = self.simplify_binary(op, true, ty, lhs, rhs) {
804+
return Some(value);
805+
}
806+
Value::CheckedBinaryOp(op, lhs, rhs)
786807
}
787808
Rvalue::UnaryOp(op, ref mut arg) => {
788809
let arg = self.simplify_operand(arg, location)?;
@@ -884,6 +905,92 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
884905

885906
Some(self.insert(Value::Aggregate(ty, variant_index, fields)))
886907
}
908+
909+
#[instrument(level = "trace", skip(self), ret)]
910+
fn simplify_binary(
911+
&mut self,
912+
op: BinOp,
913+
checked: bool,
914+
lhs_ty: Ty<'tcx>,
915+
lhs: VnIndex,
916+
rhs: VnIndex,
917+
) -> Option<VnIndex> {
918+
// Floats are weird enough that none of the logic below applies.
919+
let reasonable_ty =
920+
lhs_ty.is_integral() || lhs_ty.is_bool() || lhs_ty.is_char() || lhs_ty.is_any_ptr();
921+
if !reasonable_ty {
922+
return None;
923+
}
924+
925+
let layout = self.ecx.layout_of(lhs_ty).ok()?;
926+
927+
let as_bits = |value| {
928+
let constant = self.evaluated[value].as_ref()?;
929+
let scalar = self.ecx.read_scalar(constant).ok()?;
930+
scalar.to_bits(constant.layout.size).ok()
931+
};
932+
933+
// Represent the values as `Ok(bits)` or `Err(VnIndex)`.
934+
let a = as_bits(lhs).ok_or(lhs);
935+
let b = as_bits(rhs).ok_or(rhs);
936+
let result = match (op, a, b) {
937+
// Neutral elements.
938+
(BinOp::Add | BinOp::BitOr | BinOp::BitXor, Ok(0), Err(p))
939+
| (
940+
BinOp::Add
941+
| BinOp::BitOr
942+
| BinOp::BitXor
943+
| BinOp::Sub
944+
| BinOp::Offset
945+
| BinOp::Shl
946+
| BinOp::Shr,
947+
Err(p),
948+
Ok(0),
949+
)
950+
| (BinOp::Mul, Ok(1), Err(p))
951+
| (BinOp::Mul | BinOp::Div, Err(p), Ok(1)) => p,
952+
// Attempt to simplify `x & ALL_ONES` to `x`, with `ALL_ONES` depending on type size.
953+
(BinOp::BitAnd, Err(p), Ok(ones)) | (BinOp::BitAnd, Ok(ones), Err(p))
954+
if ones == layout.size.truncate(u128::MAX)
955+
|| (layout.ty.is_bool() && ones == 1) =>
956+
{
957+
p
958+
}
959+
// Absorbing elements.
960+
(BinOp::Mul | BinOp::BitAnd, _, Ok(0))
961+
| (BinOp::Rem, _, Ok(1))
962+
| (
963+
BinOp::Mul | BinOp::Div | BinOp::Rem | BinOp::BitAnd | BinOp::Shl | BinOp::Shr,
964+
Ok(0),
965+
_,
966+
) => self.insert_scalar(Scalar::from_uint(0u128, layout.size), lhs_ty),
967+
// Attempt to simplify `x | ALL_ONES` to `ALL_ONES`.
968+
(BinOp::BitOr, _, Ok(ones)) | (BinOp::BitOr, Ok(ones), _)
969+
if ones == layout.size.truncate(u128::MAX)
970+
|| (layout.ty.is_bool() && ones == 1) =>
971+
{
972+
self.insert_scalar(Scalar::from_uint(ones, layout.size), lhs_ty)
973+
}
974+
// Sub/Xor with itself.
975+
(BinOp::Sub | BinOp::BitXor, a, b) if a == b => {
976+
self.insert_scalar(Scalar::from_uint(0u128, layout.size), lhs_ty)
977+
}
978+
// Comparison:
979+
// - if both operands can be computed as bits, just compare the bits;
980+
// - if we proved that both operands have the same value, we can insert true/false;
981+
// - otherwise, do nothing, as we do not try to prove inequality.
982+
(BinOp::Eq, a, b) if (a.is_ok() && b.is_ok()) || a == b => self.insert_bool(a == b),
983+
(BinOp::Ne, a, b) if (a.is_ok() && b.is_ok()) || a == b => self.insert_bool(a != b),
984+
_ => return None,
985+
};
986+
987+
if checked {
988+
let false_val = self.insert_bool(false);
989+
Some(self.insert_tuple(vec![result, false_val]))
990+
} else {
991+
Some(result)
992+
}
993+
}
887994
}
888995

889996
fn op_to_prop_const<'tcx>(

tests/mir-opt/const_prop/boolean_identities.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ pub fn test(x: bool, y: bool) -> bool {
55
// CHECK-LABEL: fn test(
66
// CHECK: debug a => [[a:_.*]];
77
// CHECK: debug b => [[b:_.*]];
8-
// FIXME(cjgillot) simplify algebraic identity
9-
// CHECK-NOT: [[a]] = const true;
10-
// CHECK-NOT: [[b]] = const false;
11-
// CHECK-NOT: _0 = const false;
8+
// CHECK: [[a]] = const true;
9+
// CHECK: [[b]] = const false;
10+
// CHECK: _0 = const false;
1211
let a = (y | true);
1312
let b = (x & false);
1413
a & b

tests/mir-opt/const_prop/boolean_identities.test.GVN.diff

+7-5
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,23 @@
2424
StorageLive(_4);
2525
_4 = _2;
2626
- _3 = BitOr(move _4, const true);
27-
+ _3 = BitOr(_2, const true);
27+
+ _3 = const true;
2828
StorageDead(_4);
2929
- StorageLive(_5);
3030
+ nop;
3131
StorageLive(_6);
3232
_6 = _1;
3333
- _5 = BitAnd(move _6, const false);
34-
+ _5 = BitAnd(_1, const false);
34+
+ _5 = const false;
3535
StorageDead(_6);
3636
StorageLive(_7);
37-
_7 = _3;
37+
- _7 = _3;
38+
+ _7 = const true;
3839
StorageLive(_8);
39-
_8 = _5;
40+
- _8 = _5;
4041
- _0 = BitAnd(move _7, move _8);
41-
+ _0 = BitAnd(_3, _5);
42+
+ _8 = const false;
43+
+ _0 = const false;
4244
StorageDead(_8);
4345
StorageDead(_7);
4446
- StorageDead(_5);

tests/mir-opt/const_prop/boxes.main.GVN.panic-abort.diff

+6-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020

2121
bb0: {
2222
StorageLive(_1);
23-
StorageLive(_2);
23+
- StorageLive(_2);
24+
+ nop;
2425
StorageLive(_3);
2526
- _4 = SizeOf(i32);
2627
- _5 = AlignOf(i32);
@@ -39,8 +40,10 @@
3940
StorageDead(_7);
4041
_9 = (((_3.0: std::ptr::Unique<i32>).0: std::ptr::NonNull<i32>).0: *const i32);
4142
_2 = (*_9);
42-
_1 = Add(move _2, const 0_i32);
43-
StorageDead(_2);
43+
- _1 = Add(move _2, const 0_i32);
44+
- StorageDead(_2);
45+
+ _1 = _2;
46+
+ nop;
4447
drop(_3) -> [return: bb2, unwind unreachable];
4548
}
4649

tests/mir-opt/const_prop/boxes.main.GVN.panic-unwind.diff

+6-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020

2121
bb0: {
2222
StorageLive(_1);
23-
StorageLive(_2);
23+
- StorageLive(_2);
24+
+ nop;
2425
StorageLive(_3);
2526
- _4 = SizeOf(i32);
2627
- _5 = AlignOf(i32);
@@ -39,8 +40,10 @@
3940
StorageDead(_7);
4041
_9 = (((_3.0: std::ptr::Unique<i32>).0: std::ptr::NonNull<i32>).0: *const i32);
4142
_2 = (*_9);
42-
_1 = Add(move _2, const 0_i32);
43-
StorageDead(_2);
43+
- _1 = Add(move _2, const 0_i32);
44+
- StorageDead(_2);
45+
+ _1 = _2;
46+
+ nop;
4447
drop(_3) -> [return: bb2, unwind: bb3];
4548
}
4649

tests/mir-opt/const_prop/boxes.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ fn main() {
1212
// CHECK: debug x => [[x:_.*]];
1313
// CHECK: (*{{_.*}}) = const 42_i32;
1414
// CHECK: [[tmp:_.*]] = (*{{_.*}});
15-
// CHECK: [[x]] = Add(move [[tmp]], const 0_i32);
15+
// CHECK: [[x]] = [[tmp]];
1616
let x = *(#[rustc_box]
1717
Box::new(42))
1818
+ 0;

tests/mir-opt/const_prop/mult_by_zero.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
// EMIT_MIR mult_by_zero.test.GVN.diff
44
fn test(x: i32) -> i32 {
55
// CHECK: fn test(
6-
// FIXME(cjgillot) simplify algebraic identity
7-
// CHECK-NOT: _0 = const 0_i32;
6+
// CHECK: _0 = const 0_i32;
87
x * 0
98
}
109

tests/mir-opt/const_prop/mult_by_zero.test.GVN.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
StorageLive(_2);
1111
_2 = _1;
1212
- _0 = Mul(move _2, const 0_i32);
13-
+ _0 = Mul(_1, const 0_i32);
13+
+ _0 = const 0_i32;
1414
StorageDead(_2);
1515
return;
1616
}

0 commit comments

Comments
 (0)