From 759132c205c6b9c9a6bcd7aae8adbbfbb6cbaf54 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 26 May 2020 16:10:58 -0400
Subject: [PATCH 180/377] FIXME: some more unary op folding

---
 gcc/analyzer/analyzer.h       |  1 +
 gcc/analyzer/region-model2.cc | 74 +++++++++++++++++++++++++----------
 gcc/analyzer/region-model2.h  |  3 ++
 3 files changed, 58 insertions(+), 20 deletions(-)

diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index b40d8d5c876..00d27d77071 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -62,6 +62,7 @@ class svalue2;
   class setjmp_svalue2;
   class initial_svalue2;
   class unaryop_svalue2;
+  class binop_svalue2;
 class region2;
   class function_region2;
   class label_region2;
diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index 2e775335bc5..510a1d26394 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -2884,15 +2884,40 @@ const svalue2 *
 region_model2_manager::maybe_fold_unaryop (tree type, enum tree_code op,
 					   const svalue2 *arg)
 {
-  /* Handle redundant casts.  */
-  if (op == NOP_EXPR)
-    if (arg->get_type () && useless_type_conversion_p (arg->get_type (), type))
-      return arg;
+  switch (op)
+    {
+    default: break;
+    case NOP_EXPR:
+      {
+	/* Handle redundant casts.  */
+	if (arg->get_type ()
+	    && useless_type_conversion_p (arg->get_type (), type))
+	  return arg;
+      }
+      break;
+    case TRUTH_NOT_EXPR:
+      {
+	/* Invert comparisons e.g. "!(x == y)" => "x != y".  */
+	if (const binop_svalue2 *binop = arg->dyn_cast_binop_svalue2 ())
+	  if (TREE_CODE_CLASS (binop->get_op ()) == tcc_comparison)
+	    {
+	      enum tree_code inv_op
+		= invert_tree_comparison (binop->get_op (),
+					  HONOR_NANS (binop->get_type ()));
+	      if (inv_op != ERROR_MARK)
+		return get_or_create_binop (binop->get_type (), inv_op,
+					    binop->get_arg0 (),
+					    binop->get_arg1 ());
+	    }
+      }
+      break;
+    }
 
   /* Constants.  */
   if (tree cst = arg->maybe_get_constant ())
     if (tree result = fold_unary (op, type, cst))
       return get_or_create_constant_svalue2 (result);
+
   return NULL;
 }
 
@@ -2918,7 +2943,8 @@ region_model2_manager::get_or_create_cast (tree type, const svalue2 *arg)
 
 const svalue2 *
 region_model2_manager::maybe_fold_binop (tree type, enum tree_code op,
-					 const svalue2 *arg0, const svalue2 *arg1)
+					 const svalue2 *arg0,
+					 const svalue2 *arg1)
 {
   tree cst0 = arg0->maybe_get_constant ();
   tree cst1 = arg1->maybe_get_constant ();
@@ -2964,7 +2990,8 @@ region_model2_manager::maybe_fold_binop (tree type, enum tree_code op,
 
 const svalue2 *
 region_model2_manager::get_or_create_binop (tree type, enum tree_code op,
-					    const svalue2 *arg0, const svalue2 *arg1)
+					    const svalue2 *arg0,
+					    const svalue2 *arg1)
 {
   if (const svalue2 *folded = maybe_fold_binop (type, op, arg0, arg1))
     return folded;
@@ -3998,13 +4025,15 @@ region_model2::on_gassign (const gassign *assign,
       }
       break;
 
-    /* Unary ops.  */
     case ABS_EXPR:
     case ABSU_EXPR:
+    case BIT_NOT_EXPR:
     case FIX_TRUNC_EXPR:
     case FLOAT_EXPR:
+    case NEGATE_EXPR:
     case NOP_EXPR:
       {
+	/* Unary ops.  */
 	const svalue2 *rhs_sval = get_rvalue (rhs1, ctxt);
 	const svalue2 *sval_unaryop
 	  = m_mgr->get_or_create_unaryop (TREE_TYPE (lhs), op, rhs_sval);
@@ -4044,18 +4073,6 @@ region_model2::on_gassign (const gassign *assign,
       }
       break;
 
-    case NEGATE_EXPR:
-    case BIT_NOT_EXPR:
-      {
-	/* Unary ops.  */
-	const svalue2 *rhs1_sval = get_rvalue (rhs1, ctxt);
-	const svalue2 *sval_unaryop
-	  = m_mgr->get_or_create_unaryop (TREE_TYPE (lhs), op,
-					rhs1_sval);
-	set_value (lhs_reg, sval_unaryop, ctxt);
-      }
-      break;
-
     case PLUS_EXPR:
     case MINUS_EXPR:
     case MULT_EXPR:
@@ -8113,18 +8130,35 @@ test_unaryop_svalue2_folding ()
 {
   region_model2_manager mgr;
   tree x = build_global_decl ("x", integer_type_node);
+  tree y = build_global_decl ("y", integer_type_node);
 
   test_region_model2_context ctxt;
   region_model2 model (&mgr);
   const svalue2 *x_init = model.get_rvalue (x, &ctxt);
+  const svalue2 *y_init = model.get_rvalue (y, &ctxt);
   const region2 *x_reg = model.get_lvalue (x, &ctxt);
   ASSERT_EQ (x_init, mgr.get_or_create_initial_value (x_reg));
 
   /* "(int)x" -> "x".  */
   ASSERT_EQ (x_init, mgr.get_or_create_cast (integer_type_node, x_init));
 
-  /* "(void *nt)x" -> something other than "x".  */
+  /* "(void *)x" -> something other than "x".  */
   ASSERT_NE (x_init, mgr.get_or_create_cast (ptr_type_node, x_init));
+
+  /* "!(x == y)" -> "x != y".  */
+  ASSERT_EQ (mgr.get_or_create_unaryop
+	       (boolean_type_node, TRUTH_NOT_EXPR,
+		mgr.get_or_create_binop (boolean_type_node, EQ_EXPR,
+					 x_init, y_init)),
+	     mgr.get_or_create_binop (boolean_type_node, NE_EXPR,
+				      x_init, y_init));
+  /* "!(x > y)" -> "x <= y".  */
+  ASSERT_EQ (mgr.get_or_create_unaryop
+	       (boolean_type_node, TRUTH_NOT_EXPR,
+		mgr.get_or_create_binop (boolean_type_node, GT_EXPR,
+					 x_init, y_init)),
+	     mgr.get_or_create_binop (boolean_type_node, LE_EXPR,
+				      x_init, y_init));
 }
 
 /* Verify that binops on constant svalues are folded.  */
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index af8849733e0..2b927ea836d 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -190,6 +190,8 @@ public:
   dyn_cast_initial_svalue2 () const { return NULL; }
   virtual const unaryop_svalue2 *
   dyn_cast_unaryop_svalue2 () const { return NULL; }
+  virtual const binop_svalue2 *
+  dyn_cast_binop_svalue2 () const { return NULL; }
 
   tree maybe_get_constant () const;
   const svalue2 *maybe_undo_cast () const;
@@ -647,6 +649,7 @@ public:
   }
 
   enum kind get_kind () const FINAL OVERRIDE { return SK_BINOP; }
+  virtual const binop_svalue2 *dyn_cast_binop_svalue2 () const { return this; }
 
   enum tree_code get_op () const { return m_op; }
   const svalue2 *get_arg0 () const { return m_arg0; }
-- 
2.26.2

