On Wed, 24 Feb 2021, Jakub Jelinek wrote: > 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.
But it still is a pessimization. VCE says there's no code to be generated but NOP_EXPR says there is a conversion involved, even if you later elide it via ssa_name_has_boolean_range. So I wonder what other optimizations are prevented here? Why does uninit warn with VCE but not with NOP_EXPR? Or does the warning disappear because of those other optimizations you mention? > 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 > > -- Richard Biener <rguent...@suse.de> SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany; GF: Felix Imendörffer; HRB 36809 (AG Nuernberg)