On Wed, 25 Nov 2015, Marek Polacek wrote: > This is a bit tangled and I don't know how to best fix this so let me explain > in more detail. > > The gist is that a C_MAYBE_CONST_EXPR leaks into the gimplifier. > > In the testcase, we have > > (short) ((i ? *e : 0) & ~u | i & u); > > where 'e' is a pointer to volatile. During c_parser_conditional_expression, > we > wrap 'i' and '*e' in C_MAYBE_CONST_EXPR. Later on, we get to build_c_cast, > and > here we're trying to cast > > (c_maybe_const_expr <i> != 0 ? (unsigned int) c_maybe_const_expr <*e > : 0) > & ~u | (unsigned int) i & u > > to "short", so we call convert()->convert_to_integer() etc. As a part of this > conversion, we try to fold the expr. Now, we match this pattern: > > (x & ~m) | (y & m) -> ((x ^ y) & m) ^ x > > Look how 'x' is duplicated in the result of the pattern, and since (because of > the volatile 'e') it has TREE_SIDE_EFFECTS, we need to wrap it in a SAVE_EXPR, > which means the convert() produces > > (short int) (((SAVE_EXPR <c_maybe_const_expr <i> != 0 ? (unsigned short) > c_maybe_const_expr <*e > : 0>) > ^ (unsigned short) i) & (unsigned short) u > ^ (SAVE_EXPR <c_maybe_const_expr <i > != 0 ? (unsigned short) > c_maybe_const_expr <*e > : 0>)) > > What's important is that we end up with a C_MAYBE_CONST_EXPR in a SAVE_EXPR. > > Down the line, we get into c_process_expr_stmt, where there's > > expr = c_fully_fold (expr, false, NULL); > > so we try to fully-fold the whole expression. However, c_fully_fold just > gives up when it sees a SAVE_EXPR, so it doesn't scrub out C_MAYBE_CONST_EXPR. > It then leaks into the gimplifier and crashes. > > This pattern is probably the only one that is problematical in this regard. > Looking more at this pattern, it looks dubious, because imagine the 'x' in > the pattern is something complex, e.g. a huge COND_EXPR or somesuch -- in > that case duplicating the 'x' does more harm than good.
But the whole point of the SAVE_EXPR is that it does _not_ "duplicate" it, it just creates another use of the same value. > So after all, I > think this transformation should be restricted a bit, and only kick in when > 'x' is a constant, an SSA_NAME, or a certain DECL_P. Seems simple_operand_p > in fold-const.c was what I was after, so I've just used that. > > With this patch, we won't create those SAVE_EXPRs so c_fully_fold is able to > get rid of the C_MAYBE_CONST_EXPRs and the gimplifier is happy. > > Thoughts? No. If c_fully_fold can't handle SAVE_EXPRs then maybe c_gimplify_expr can simply strip them. Richard. > Bootstrapped/regtested on x86_64-linux, ok for trunk? > > 2015-11-25 Marek Polacek <pola...@redhat.com> > > PR c/68513 > * fold-const.c (simple_operand_p): Export. > * fold-const.h (simple_operand_p): Declare. > * match.pd ((x & ~m) | (y & m)): Guard with simple_operand_p. > > * gcc.dg/torture/pr68513.c: New test. > > diff --git gcc/fold-const.c gcc/fold-const.c > index 698062e..9bb3a53 100644 > --- gcc/fold-const.c > +++ gcc/fold-const.c > @@ -122,7 +122,6 @@ static tree decode_field_reference (location_t, tree, > HOST_WIDE_INT *, > HOST_WIDE_INT *, > machine_mode *, int *, int *, int *, > tree *, tree *); > -static int simple_operand_p (const_tree); > static bool simple_operand_p_2 (tree); > static tree range_binop (enum tree_code, tree, tree, int, tree, int); > static tree range_predecessor (tree); > @@ -4059,10 +4058,10 @@ sign_bit_p (tree exp, const_tree val) > return NULL_TREE; > } > > -/* Subroutine for fold_truth_andor_1: determine if an operand is simple > enough > - to be evaluated unconditionally. */ > +/* Determine if an operand is simple enough to be evaluated > + unconditionally. */ > > -static int > +bool > simple_operand_p (const_tree exp) > { > /* Strip any conversions that don't change the machine mode. */ > diff --git gcc/fold-const.h gcc/fold-const.h > index 7741802..717c840 100644 > --- gcc/fold-const.h > +++ gcc/fold-const.h > @@ -181,6 +181,7 @@ extern tree const_unop (enum tree_code, tree, tree); > extern tree const_binop (enum tree_code, tree, tree, tree); > extern bool negate_mathfn_p (combined_fn); > extern const char *c_getstr (tree); > +extern bool simple_operand_p (const_tree); > > /* Return OFF converted to a pointer offset type suitable as offset for > POINTER_PLUS_EXPR. Use location LOC for this conversion. */ > diff --git gcc/match.pd gcc/match.pd > index e86cc8b..4aee4a6 100644 > --- gcc/match.pd > +++ gcc/match.pd > @@ -877,7 +877,8 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) > /* (x & ~m) | (y & m) -> ((x ^ y) & m) ^ x */ > (simplify > (bit_ior:c (bit_and:cs @0 (bit_not @2)) (bit_and:cs @1 @2)) > - (bit_xor (bit_and (bit_xor @0 @1) @2) @0)) > + (if (simple_operand_p (@0)) > + (bit_xor (bit_and (bit_xor @0 @1) @2) @0))) > > /* Fold A - (A & B) into ~B & A. */ > (simplify > diff --git gcc/testsuite/gcc.dg/torture/pr68513.c > gcc/testsuite/gcc.dg/torture/pr68513.c > index e69de29..4e08b29 100644 > --- gcc/testsuite/gcc.dg/torture/pr68513.c > +++ gcc/testsuite/gcc.dg/torture/pr68513.c > @@ -0,0 +1,13 @@ > +/* PR c/68513 */ > +/* { dg-do compile } */ > + > +int i; > +unsigned u; > +volatile unsigned int *e; > + > +void > +fn1 (void) > +{ > + (short) ((i ? *e : 0) & ~u | i & u); > + (short) (((0, 0) ? *e : 0) & ~u | i & u); > +} > > Marek > > -- Richard Biener <rguent...@suse.de> SUSE LINUX GmbH, GF: Felix Imendoerffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nuernberg)