Hi!

SRA creates a VCE from integer to bool and that VCE then prevents other
optimizations or e.g. prevents the uninit pass from avoiding a false
positive warning.

In the PR using NOP_EXPR has been discussed as one possibility and has been
rejected because at expansion it will emit a superfluous & 1 operation.
I still think it is a good idea to use NOP_EXPR and so have changed
expansion to not emit that & 1 operation in that case.  Both spots are
done with tight conditions (bool only etc.), as I'd like to fix just
that case and not introduce a wider general optimization, but perhaps
later we could lift it and do a general range of arbitrary
type_has_mode_precision_p to non-type_has_mode_precision_p with same
TYPE_MODE case.

On this particular case, the expr.c part isn't really needed, because we
have an unsigned char SSA_NAME with values 0 or 1, VCE to bool and
comparison of that bool against false in a condition, and with the
match.pd change the VCE is simplified into NOP_EXPR and that is later
folded into just comparison of the unsigned char value against 0.
If I under the debugger without the match.pd change the VCE into NOP_EXPR
at the start of TER, then without the expr.c change indeed & 1 is emitted
but then combine removes it again (non-zero bits logic makes that possible),
with the expr.c change we don't emit it at all, which is good because
we can't rely on the combine managing to do it in all cases.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2021-02-24  Jakub Jelinek  <ja...@redhat.com>

        PR tree-optimization/80635
        * match.pd (view_convert<bool> (A) -> (bool) (A)): Simplify VCE of
        ssa_name_has_boolean_range with integral type to bool.
        * expr.c (expand_expr_real_2): Avoid REDUCE_BIT_FIELD for cast to
        boolean if operand is ssa_name_has_boolean_range.

        * g++.dg/warn/pr80635-1.C: New test.
        * g++.dg/warn/pr80635-2.C: New test.

--- gcc/match.pd.jj     2021-02-18 16:21:01.000000000 +0100
+++ gcc/match.pd        2021-02-23 13:16:15.095416289 +0100
@@ -3316,7 +3316,13 @@ (define_operator_list COND_TERNARY
   (view_convert @0)
   (if ((INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type))
        && (INTEGRAL_TYPE_P (TREE_TYPE (@0)) || POINTER_TYPE_P (TREE_TYPE (@0)))
-       && TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (@0)))
+       && (TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (@0))
+          || (TREE_CODE (type) == BOOLEAN_TYPE
+              && TREE_CODE (@0) == SSA_NAME
+              && TREE_CODE (TREE_TYPE (@0)) != BOOLEAN_TYPE
+              && TYPE_MODE (type) == TYPE_MODE (TREE_TYPE (@0))
+              && type_has_mode_precision_p (TREE_TYPE (@0))
+              && ssa_name_has_boolean_range (@0))))
    (convert @0)))
 
 /* Strip inner integral conversions that do not change precision or size, or
--- gcc/expr.c.jj       2021-02-02 10:01:26.483903572 +0100
+++ gcc/expr.c  2021-02-23 13:10:26.400323532 +0100
@@ -8790,6 +8790,15 @@ expand_expr_real_2 (sepops ops, rtx targ
              && GET_CODE (op0) == SUBREG)
            SUBREG_PROMOTED_VAR_P (op0) = 0;
 
+         /* Don't reduce to boolean range if we know the operand
+            already has a boolean range.  */
+         if (reduce_bit_field
+             && TREE_CODE (type) == BOOLEAN_TYPE
+             && TREE_CODE (treeop0) == SSA_NAME
+             && TREE_CODE (TREE_TYPE (treeop0)) != BOOLEAN_TYPE
+             && type_has_mode_precision_p (TREE_TYPE (treeop0))
+             && ssa_name_has_boolean_range (treeop0))
+           return op0;
          return REDUCE_BIT_FIELD (op0);
        }
 
--- gcc/testsuite/g++.dg/warn/pr80635-1.C.jj    2021-02-23 13:17:35.398516088 
+0100
+++ gcc/testsuite/g++.dg/warn/pr80635-1.C       2021-02-23 13:19:21.712324315 
+0100
@@ -0,0 +1,46 @@
+// PR tree-optimization/80635
+// { dg-do compile { target c++11 } }
+// { dg-options "-O2 -Wmaybe-uninitialized" }
+
+using size_t = decltype (sizeof (1));
+inline void *operator new (size_t, void *p) { return p; }
+template<typename T>
+struct optional
+{
+  optional () : m_dummy (), live (false) {}
+  void emplace () { new (&m_item) T (); live = true; }
+  ~optional () { if (live) m_item.~T (); }
+
+  union
+  {
+    struct {} m_dummy;
+    T m_item;
+  };
+  bool live;
+};
+
+extern int get ();
+extern void set (int);
+
+struct A
+{
+  A () : m (get ()) {}
+  ~A () { set (m); }   // { dg-bogus "may be used uninitialized in this 
function" }
+
+  int m;
+};
+
+struct B
+{
+  B ();
+  ~B ();
+};
+
+void func ()
+{
+  optional<A> maybe_a;
+  optional<B> maybe_b;
+
+  maybe_a.emplace ();
+  maybe_b.emplace ();
+}
--- gcc/testsuite/g++.dg/warn/pr80635-2.C.jj    2021-02-23 13:17:38.426482145 
+0100
+++ gcc/testsuite/g++.dg/warn/pr80635-2.C       2021-02-23 13:27:34.215803360 
+0100
@@ -0,0 +1,31 @@
+// PR tree-optimization/80635
+// { dg-do compile { target c++17 } }
+// { dg-options "-O2 -Wmaybe-uninitialized" }
+
+#include <optional>
+
+extern int get ();
+extern void set (int);
+
+struct A
+{
+  A () : m (get ()) {}
+  ~A () { set (m); }   // { dg-bogus "may be used uninitialized in this 
function" }
+
+  int m;
+};
+
+struct B
+{
+  B ();
+  ~B ();
+};
+
+void func ()
+{
+  std::optional<A> maybe_a;
+  std::optional<B> maybe_b;
+
+  maybe_a.emplace ();
+  maybe_b.emplace ();
+}

        Jakub

Reply via email to