Skip to content

Commit 666030c

Browse files
committed
Simplify binary ops.
1 parent 92f2e0a commit 666030c

19 files changed

+808
-431
lines changed

compiler/rustc_mir_transform/src/gvn.rs

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

348+
fn insert_bool(&mut self, flag: bool) -> VnIndex {
349+
// Booleans are deterministic.
350+
self.insert(Value::Constant { value: Const::from_bool(self.tcx, flag), disambiguator: 0 })
351+
}
352+
348353
fn insert_scalar(&mut self, scalar: Scalar, ty: Ty<'tcx>) -> VnIndex {
349354
self.insert_constant(Const::from_scalar(self.tcx, scalar, ty))
350355
.expect("scalars are deterministic")
351356
}
352357

358+
fn insert_tuple(&mut self, values: Vec<VnIndex>) -> VnIndex {
359+
self.insert(Value::Aggregate(AggregateTy::Tuple, VariantIdx::from_u32(0), values))
360+
}
361+
353362
#[instrument(level = "trace", skip(self), ret)]
354363
fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
355364
use Value::*;
@@ -785,14 +794,26 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
785794
Value::Cast { kind, value, from, to }
786795
}
787796
Rvalue::BinaryOp(op, box (ref mut lhs, ref mut rhs)) => {
797+
let ty = lhs.ty(self.local_decls, self.tcx);
788798
let lhs = self.simplify_operand(lhs, location);
789799
let rhs = self.simplify_operand(rhs, location);
790-
Value::BinaryOp(op, lhs?, rhs?)
800+
let lhs = lhs?;
801+
let rhs = rhs?;
802+
if let Some(value) = self.simplify_binary(op, false, ty, lhs, rhs) {
803+
return Some(value);
804+
}
805+
Value::BinaryOp(op, lhs, rhs)
791806
}
792807
Rvalue::CheckedBinaryOp(op, box (ref mut lhs, ref mut rhs)) => {
808+
let ty = lhs.ty(self.local_decls, self.tcx);
793809
let lhs = self.simplify_operand(lhs, location);
794810
let rhs = self.simplify_operand(rhs, location);
795-
Value::CheckedBinaryOp(op, lhs?, rhs?)
811+
let lhs = lhs?;
812+
let rhs = rhs?;
813+
if let Some(value) = self.simplify_binary(op, true, ty, lhs, rhs) {
814+
return Some(value);
815+
}
816+
Value::CheckedBinaryOp(op, lhs, rhs)
796817
}
797818
Rvalue::UnaryOp(op, ref mut arg) => {
798819
let arg = self.simplify_operand(arg, location)?;
@@ -894,6 +915,92 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
894915

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

8991006
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)