From 5fc23ad8e6b7a5ef0b5a6936cb043bd85f8bed59 Mon Sep 17 00:00:00 2001
From: Camille GILLOT <gillot.camille@gmail.com>
Date: Sat, 25 Mar 2023 22:33:35 +0000
Subject: [PATCH] Simplify unary operations.

---
 compiler/rustc_mir_transform/src/gvn.rs       |  20 +++
 .../gvn.fn_pointers.GVN.panic-abort.diff      |  18 +--
 .../gvn.fn_pointers.GVN.panic-unwind.diff     |  18 +--
 tests/mir-opt/gvn.rs                          |  27 ++++
 tests/mir-opt/gvn.unary.GVN.panic-abort.diff  | 153 ++++++++++++++++++
 tests/mir-opt/gvn.unary.GVN.panic-unwind.diff | 153 ++++++++++++++++++
 6 files changed, 371 insertions(+), 18 deletions(-)
 create mode 100644 tests/mir-opt/gvn.unary.GVN.panic-abort.diff
 create mode 100644 tests/mir-opt/gvn.unary.GVN.panic-unwind.diff

diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 3052369c3ca..699b218d46d 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -817,6 +817,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             }
             Rvalue::UnaryOp(op, ref mut arg) => {
                 let arg = self.simplify_operand(arg, location)?;
+                if let Some(value) = self.simplify_unary(op, arg) {
+                    return Some(value);
+                }
                 Value::UnaryOp(op, arg)
             }
             Rvalue::Discriminant(ref mut place) => {
@@ -916,6 +919,23 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         Some(self.insert(Value::Aggregate(ty, variant_index, fields)))
     }
 
+    #[instrument(level = "trace", skip(self), ret)]
+    fn simplify_unary(&mut self, op: UnOp, value: VnIndex) -> Option<VnIndex> {
+        let value = match (op, self.get(value)) {
+            (UnOp::Not, Value::UnaryOp(UnOp::Not, inner)) => return Some(*inner),
+            (UnOp::Neg, Value::UnaryOp(UnOp::Neg, inner)) => return Some(*inner),
+            (UnOp::Not, Value::BinaryOp(BinOp::Eq, lhs, rhs)) => {
+                Value::BinaryOp(BinOp::Ne, *lhs, *rhs)
+            }
+            (UnOp::Not, Value::BinaryOp(BinOp::Ne, lhs, rhs)) => {
+                Value::BinaryOp(BinOp::Eq, *lhs, *rhs)
+            }
+            _ => return None,
+        };
+
+        Some(self.insert(value))
+    }
+
     #[instrument(level = "trace", skip(self), ret)]
     fn simplify_binary(
         &mut self,
diff --git a/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff b/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff
index 0901896af53..02bf95840da 100644
--- a/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff
+++ b/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff
@@ -8,10 +8,10 @@
       let mut _3: fn(u8) -> u8;
       let _5: ();
       let mut _6: fn(u8) -> u8;
-      let mut _9: {closure@$DIR/gvn.rs:585:19: 585:21};
+      let mut _9: {closure@$DIR/gvn.rs:610:19: 610:21};
       let _10: ();
       let mut _11: fn();
-      let mut _13: {closure@$DIR/gvn.rs:585:19: 585:21};
+      let mut _13: {closure@$DIR/gvn.rs:610:19: 610:21};
       let _14: ();
       let mut _15: fn();
       scope 1 {
@@ -19,7 +19,7 @@
           let _4: fn(u8) -> u8;
           scope 2 {
               debug g => _4;
-              let _7: {closure@$DIR/gvn.rs:585:19: 585:21};
+              let _7: {closure@$DIR/gvn.rs:610:19: 610:21};
               scope 3 {
                   debug closure => _7;
                   let _8: fn();
@@ -62,16 +62,16 @@
           StorageDead(_6);
           StorageDead(_5);
 -         StorageLive(_7);
--         _7 = {closure@$DIR/gvn.rs:585:19: 585:21};
+-         _7 = {closure@$DIR/gvn.rs:610:19: 610:21};
 -         StorageLive(_8);
 +         nop;
-+         _7 = const ZeroSized: {closure@$DIR/gvn.rs:585:19: 585:21};
++         _7 = const ZeroSized: {closure@$DIR/gvn.rs:610:19: 610:21};
 +         nop;
           StorageLive(_9);
 -         _9 = _7;
 -         _8 = move _9 as fn() (PointerCoercion(ClosureFnPointer(Normal)));
-+         _9 = const ZeroSized: {closure@$DIR/gvn.rs:585:19: 585:21};
-+         _8 = const ZeroSized: {closure@$DIR/gvn.rs:585:19: 585:21} as fn() (PointerCoercion(ClosureFnPointer(Normal)));
++         _9 = const ZeroSized: {closure@$DIR/gvn.rs:610:19: 610:21};
++         _8 = const ZeroSized: {closure@$DIR/gvn.rs:610:19: 610:21} as fn() (PointerCoercion(ClosureFnPointer(Normal)));
           StorageDead(_9);
           StorageLive(_10);
           StorageLive(_11);
@@ -88,8 +88,8 @@
           StorageLive(_13);
 -         _13 = _7;
 -         _12 = move _13 as fn() (PointerCoercion(ClosureFnPointer(Normal)));
-+         _13 = const ZeroSized: {closure@$DIR/gvn.rs:585:19: 585:21};
-+         _12 = const ZeroSized: {closure@$DIR/gvn.rs:585:19: 585:21} as fn() (PointerCoercion(ClosureFnPointer(Normal)));
++         _13 = const ZeroSized: {closure@$DIR/gvn.rs:610:19: 610:21};
++         _12 = const ZeroSized: {closure@$DIR/gvn.rs:610:19: 610:21} as fn() (PointerCoercion(ClosureFnPointer(Normal)));
           StorageDead(_13);
           StorageLive(_14);
           StorageLive(_15);
diff --git a/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff b/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff
index 4b95191ba82..c5dcc8a8ec9 100644
--- a/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff
+++ b/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff
@@ -8,10 +8,10 @@
       let mut _3: fn(u8) -> u8;
       let _5: ();
       let mut _6: fn(u8) -> u8;
-      let mut _9: {closure@$DIR/gvn.rs:585:19: 585:21};
+      let mut _9: {closure@$DIR/gvn.rs:610:19: 610:21};
       let _10: ();
       let mut _11: fn();
-      let mut _13: {closure@$DIR/gvn.rs:585:19: 585:21};
+      let mut _13: {closure@$DIR/gvn.rs:610:19: 610:21};
       let _14: ();
       let mut _15: fn();
       scope 1 {
@@ -19,7 +19,7 @@
           let _4: fn(u8) -> u8;
           scope 2 {
               debug g => _4;
-              let _7: {closure@$DIR/gvn.rs:585:19: 585:21};
+              let _7: {closure@$DIR/gvn.rs:610:19: 610:21};
               scope 3 {
                   debug closure => _7;
                   let _8: fn();
@@ -62,16 +62,16 @@
           StorageDead(_6);
           StorageDead(_5);
 -         StorageLive(_7);
--         _7 = {closure@$DIR/gvn.rs:585:19: 585:21};
+-         _7 = {closure@$DIR/gvn.rs:610:19: 610:21};
 -         StorageLive(_8);
 +         nop;
-+         _7 = const ZeroSized: {closure@$DIR/gvn.rs:585:19: 585:21};
++         _7 = const ZeroSized: {closure@$DIR/gvn.rs:610:19: 610:21};
 +         nop;
           StorageLive(_9);
 -         _9 = _7;
 -         _8 = move _9 as fn() (PointerCoercion(ClosureFnPointer(Normal)));
-+         _9 = const ZeroSized: {closure@$DIR/gvn.rs:585:19: 585:21};
-+         _8 = const ZeroSized: {closure@$DIR/gvn.rs:585:19: 585:21} as fn() (PointerCoercion(ClosureFnPointer(Normal)));
++         _9 = const ZeroSized: {closure@$DIR/gvn.rs:610:19: 610:21};
++         _8 = const ZeroSized: {closure@$DIR/gvn.rs:610:19: 610:21} as fn() (PointerCoercion(ClosureFnPointer(Normal)));
           StorageDead(_9);
           StorageLive(_10);
           StorageLive(_11);
@@ -88,8 +88,8 @@
           StorageLive(_13);
 -         _13 = _7;
 -         _12 = move _13 as fn() (PointerCoercion(ClosureFnPointer(Normal)));
-+         _13 = const ZeroSized: {closure@$DIR/gvn.rs:585:19: 585:21};
-+         _12 = const ZeroSized: {closure@$DIR/gvn.rs:585:19: 585:21} as fn() (PointerCoercion(ClosureFnPointer(Normal)));
++         _13 = const ZeroSized: {closure@$DIR/gvn.rs:610:19: 610:21};
++         _12 = const ZeroSized: {closure@$DIR/gvn.rs:610:19: 610:21} as fn() (PointerCoercion(ClosureFnPointer(Normal)));
           StorageDead(_13);
           StorageLive(_14);
           StorageLive(_15);
diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs
index 8c06e3a6385..f8f4fdcd732 100644
--- a/tests/mir-opt/gvn.rs
+++ b/tests/mir-opt/gvn.rs
@@ -166,6 +166,31 @@ fn repeated_index<T: Copy, const N: usize>(x: T, idx: usize) {
     opaque(a[idx]);
 }
 
+fn unary(x: i64) {
+    // CHECK-LABEL: fn unary(
+    // CHECK: opaque::<i64>(_1)
+    opaque(--x); // This is `x`.
+
+    // CHECK: [[b:_.*]] = Lt(_1, const 13_i64);
+    // CHECK: opaque::<bool>([[b]])
+    let b = x < 13;
+    opaque(!!b); // This is `b`.
+
+    // Both lines should test the same thing.
+    // CHECK: [[c:_.*]] = Ne(_1, const 15_i64);
+    // CHECK: opaque::<bool>([[c]])
+    // CHECK: opaque::<bool>([[c]])
+    opaque(x != 15);
+    opaque(!(x == 15));
+
+    // Both lines should test the same thing.
+    // CHECK: [[d:_.*]] = Eq(_1, const 35_i64);
+    // CHECK: opaque::<bool>([[d]])
+    // CHECK: opaque::<bool>([[d]])
+    opaque(x == 35);
+    opaque(!(x != 35));
+}
+
 /// Verify symbolic integer arithmetic simplifications.
 fn arithmetic(x: u64) {
     // CHECK-LABEL: fn arithmetic(
@@ -623,6 +648,7 @@ fn main() {
     subexpression_elimination(2, 4, 5);
     wrap_unwrap(5);
     repeated_index::<u32, 7>(5, 3);
+    unary(i64::MIN);
     arithmetic(5);
     comparison(5, 6);
     arithmetic_checked(5);
@@ -651,6 +677,7 @@ fn identity<T>(x: T) -> T {
 // EMIT_MIR gvn.subexpression_elimination.GVN.diff
 // EMIT_MIR gvn.wrap_unwrap.GVN.diff
 // EMIT_MIR gvn.repeated_index.GVN.diff
+// EMIT_MIR gvn.unary.GVN.diff
 // EMIT_MIR gvn.arithmetic.GVN.diff
 // EMIT_MIR gvn.comparison.GVN.diff
 // EMIT_MIR gvn.arithmetic_checked.GVN.diff
diff --git a/tests/mir-opt/gvn.unary.GVN.panic-abort.diff b/tests/mir-opt/gvn.unary.GVN.panic-abort.diff
new file mode 100644
index 00000000000..9469032f294
--- /dev/null
+++ b/tests/mir-opt/gvn.unary.GVN.panic-abort.diff
@@ -0,0 +1,153 @@
+- // MIR for `unary` before GVN
++ // MIR for `unary` after GVN
+  
+  fn unary(_1: i64) -> () {
+      debug x => _1;
+      let mut _0: ();
+      let _2: ();
+      let mut _3: i64;
+      let mut _4: i64;
+      let mut _5: i64;
+      let _6: bool;
+      let mut _7: i64;
+      let _8: ();
+      let mut _9: bool;
+      let mut _10: bool;
+      let mut _11: bool;
+      let _12: ();
+      let mut _13: bool;
+      let mut _14: i64;
+      let _15: ();
+      let mut _16: bool;
+      let mut _17: bool;
+      let mut _18: i64;
+      let _19: ();
+      let mut _20: bool;
+      let mut _21: i64;
+      let _22: ();
+      let mut _23: bool;
+      let mut _24: bool;
+      let mut _25: i64;
+      scope 1 {
+          debug b => _6;
+      }
+  
+      bb0: {
+          StorageLive(_2);
+          StorageLive(_3);
+          StorageLive(_4);
+          StorageLive(_5);
+          _5 = _1;
+-         _4 = Neg(move _5);
++         _4 = Neg(_1);
+          StorageDead(_5);
+-         _3 = Neg(move _4);
++         _3 = _1;
+          StorageDead(_4);
+-         _2 = opaque::<i64>(move _3) -> [return: bb1, unwind unreachable];
++         _2 = opaque::<i64>(_1) -> [return: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+          StorageDead(_3);
+          StorageDead(_2);
+-         StorageLive(_6);
++         nop;
+          StorageLive(_7);
+          _7 = _1;
+-         _6 = Lt(move _7, const 13_i64);
++         _6 = Lt(_1, const 13_i64);
+          StorageDead(_7);
+          StorageLive(_8);
+          StorageLive(_9);
+          StorageLive(_10);
+          StorageLive(_11);
+          _11 = _6;
+-         _10 = Not(move _11);
++         _10 = Not(_6);
+          StorageDead(_11);
+-         _9 = Not(move _10);
++         _9 = _6;
+          StorageDead(_10);
+-         _8 = opaque::<bool>(move _9) -> [return: bb2, unwind unreachable];
++         _8 = opaque::<bool>(_6) -> [return: bb2, unwind unreachable];
+      }
+  
+      bb2: {
+          StorageDead(_9);
+          StorageDead(_8);
+          StorageLive(_12);
+-         StorageLive(_13);
++         nop;
+          StorageLive(_14);
+          _14 = _1;
+-         _13 = Ne(move _14, const 15_i64);
++         _13 = Ne(_1, const 15_i64);
+          StorageDead(_14);
+-         _12 = opaque::<bool>(move _13) -> [return: bb3, unwind unreachable];
++         _12 = opaque::<bool>(_13) -> [return: bb3, unwind unreachable];
+      }
+  
+      bb3: {
+-         StorageDead(_13);
++         nop;
+          StorageDead(_12);
+          StorageLive(_15);
+          StorageLive(_16);
+          StorageLive(_17);
+          StorageLive(_18);
+          _18 = _1;
+-         _17 = Eq(move _18, const 15_i64);
++         _17 = Eq(_1, const 15_i64);
+          StorageDead(_18);
+-         _16 = Not(move _17);
++         _16 = _13;
+          StorageDead(_17);
+-         _15 = opaque::<bool>(move _16) -> [return: bb4, unwind unreachable];
++         _15 = opaque::<bool>(_13) -> [return: bb4, unwind unreachable];
+      }
+  
+      bb4: {
+          StorageDead(_16);
+          StorageDead(_15);
+          StorageLive(_19);
+-         StorageLive(_20);
++         nop;
+          StorageLive(_21);
+          _21 = _1;
+-         _20 = Eq(move _21, const 35_i64);
++         _20 = Eq(_1, const 35_i64);
+          StorageDead(_21);
+-         _19 = opaque::<bool>(move _20) -> [return: bb5, unwind unreachable];
++         _19 = opaque::<bool>(_20) -> [return: bb5, unwind unreachable];
+      }
+  
+      bb5: {
+-         StorageDead(_20);
++         nop;
+          StorageDead(_19);
+          StorageLive(_22);
+          StorageLive(_23);
+          StorageLive(_24);
+          StorageLive(_25);
+          _25 = _1;
+-         _24 = Ne(move _25, const 35_i64);
++         _24 = Ne(_1, const 35_i64);
+          StorageDead(_25);
+-         _23 = Not(move _24);
++         _23 = _20;
+          StorageDead(_24);
+-         _22 = opaque::<bool>(move _23) -> [return: bb6, unwind unreachable];
++         _22 = opaque::<bool>(_20) -> [return: bb6, unwind unreachable];
+      }
+  
+      bb6: {
+          StorageDead(_23);
+          StorageDead(_22);
+          _0 = const ();
+-         StorageDead(_6);
++         nop;
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/gvn.unary.GVN.panic-unwind.diff b/tests/mir-opt/gvn.unary.GVN.panic-unwind.diff
new file mode 100644
index 00000000000..e672f6fb6ba
--- /dev/null
+++ b/tests/mir-opt/gvn.unary.GVN.panic-unwind.diff
@@ -0,0 +1,153 @@
+- // MIR for `unary` before GVN
++ // MIR for `unary` after GVN
+  
+  fn unary(_1: i64) -> () {
+      debug x => _1;
+      let mut _0: ();
+      let _2: ();
+      let mut _3: i64;
+      let mut _4: i64;
+      let mut _5: i64;
+      let _6: bool;
+      let mut _7: i64;
+      let _8: ();
+      let mut _9: bool;
+      let mut _10: bool;
+      let mut _11: bool;
+      let _12: ();
+      let mut _13: bool;
+      let mut _14: i64;
+      let _15: ();
+      let mut _16: bool;
+      let mut _17: bool;
+      let mut _18: i64;
+      let _19: ();
+      let mut _20: bool;
+      let mut _21: i64;
+      let _22: ();
+      let mut _23: bool;
+      let mut _24: bool;
+      let mut _25: i64;
+      scope 1 {
+          debug b => _6;
+      }
+  
+      bb0: {
+          StorageLive(_2);
+          StorageLive(_3);
+          StorageLive(_4);
+          StorageLive(_5);
+          _5 = _1;
+-         _4 = Neg(move _5);
++         _4 = Neg(_1);
+          StorageDead(_5);
+-         _3 = Neg(move _4);
++         _3 = _1;
+          StorageDead(_4);
+-         _2 = opaque::<i64>(move _3) -> [return: bb1, unwind continue];
++         _2 = opaque::<i64>(_1) -> [return: bb1, unwind continue];
+      }
+  
+      bb1: {
+          StorageDead(_3);
+          StorageDead(_2);
+-         StorageLive(_6);
++         nop;
+          StorageLive(_7);
+          _7 = _1;
+-         _6 = Lt(move _7, const 13_i64);
++         _6 = Lt(_1, const 13_i64);
+          StorageDead(_7);
+          StorageLive(_8);
+          StorageLive(_9);
+          StorageLive(_10);
+          StorageLive(_11);
+          _11 = _6;
+-         _10 = Not(move _11);
++         _10 = Not(_6);
+          StorageDead(_11);
+-         _9 = Not(move _10);
++         _9 = _6;
+          StorageDead(_10);
+-         _8 = opaque::<bool>(move _9) -> [return: bb2, unwind continue];
++         _8 = opaque::<bool>(_6) -> [return: bb2, unwind continue];
+      }
+  
+      bb2: {
+          StorageDead(_9);
+          StorageDead(_8);
+          StorageLive(_12);
+-         StorageLive(_13);
++         nop;
+          StorageLive(_14);
+          _14 = _1;
+-         _13 = Ne(move _14, const 15_i64);
++         _13 = Ne(_1, const 15_i64);
+          StorageDead(_14);
+-         _12 = opaque::<bool>(move _13) -> [return: bb3, unwind continue];
++         _12 = opaque::<bool>(_13) -> [return: bb3, unwind continue];
+      }
+  
+      bb3: {
+-         StorageDead(_13);
++         nop;
+          StorageDead(_12);
+          StorageLive(_15);
+          StorageLive(_16);
+          StorageLive(_17);
+          StorageLive(_18);
+          _18 = _1;
+-         _17 = Eq(move _18, const 15_i64);
++         _17 = Eq(_1, const 15_i64);
+          StorageDead(_18);
+-         _16 = Not(move _17);
++         _16 = _13;
+          StorageDead(_17);
+-         _15 = opaque::<bool>(move _16) -> [return: bb4, unwind continue];
++         _15 = opaque::<bool>(_13) -> [return: bb4, unwind continue];
+      }
+  
+      bb4: {
+          StorageDead(_16);
+          StorageDead(_15);
+          StorageLive(_19);
+-         StorageLive(_20);
++         nop;
+          StorageLive(_21);
+          _21 = _1;
+-         _20 = Eq(move _21, const 35_i64);
++         _20 = Eq(_1, const 35_i64);
+          StorageDead(_21);
+-         _19 = opaque::<bool>(move _20) -> [return: bb5, unwind continue];
++         _19 = opaque::<bool>(_20) -> [return: bb5, unwind continue];
+      }
+  
+      bb5: {
+-         StorageDead(_20);
++         nop;
+          StorageDead(_19);
+          StorageLive(_22);
+          StorageLive(_23);
+          StorageLive(_24);
+          StorageLive(_25);
+          _25 = _1;
+-         _24 = Ne(move _25, const 35_i64);
++         _24 = Ne(_1, const 35_i64);
+          StorageDead(_25);
+-         _23 = Not(move _24);
++         _23 = _20;
+          StorageDead(_24);
+-         _22 = opaque::<bool>(move _23) -> [return: bb6, unwind continue];
++         _22 = opaque::<bool>(_20) -> [return: bb6, unwind continue];
+      }
+  
+      bb6: {
+          StorageDead(_23);
+          StorageDead(_22);
+          _0 = const ();
+-         StorageDead(_6);
++         nop;
+          return;
+      }
+  }
+