On Mon, Oct 07, 2019 at 03:51:05PM -0400, Jason Merrill wrote:
> > -      if (TREE_CODE (arg1) == COMPOUND_EXPR)
> > +      if (TREE_CODE (arg1) == COMPOUND_EXPR
> > +     && (flag_strong_eval_order != 2
> > +         /* C++17 disallows this canonicalization for shifts.  */
> > +         || (code != LSHIFT_EXPR
> > +             && code != RSHIFT_EXPR
> > +             && code != LROTATE_EXPR
> > +             && code != RROTATE_EXPR)))
> 
> Perhaps we should handle this in cp_build_binary_op instead?

How?  By adding a SAVE_EXPR in there, so that generic code is safe?

> >     if (processing_template_decl && expr != error_mark_node)
> >       {
> 
> Hmm, it's weird that we have both grok_array_decl and build_x_array_ref.
> I'll try removing the former.

Indeed.  I've noticed that because cp_build_array_ref only swaps in the
non-array case, in:
template <typename T, typename U>
auto
foo (T &x, U &y)
{
  T t;
  U u;
  __builtin_memcpy (&t, &x, sizeof (t));
  __builtin_memcpy (&u, &y, sizeof (u));
  return t[u];
}

int
main ()
{
  int a[4] = { 3, 4, 5, 6 };
  int b = 2;
  auto c = foo (a, b);
  auto d = foo (b, a);
}
we actually use the *(t+u) form in the second instantiation case
(regardless of -fstrong-eval-order) and t[u] in the former case,
as it is only grok_array_decl that swaps in that case.

> > --- gcc/cp/typeck.c.jj      2019-10-07 13:08:58.717380894 +0200
> > +++ gcc/cp/typeck.c 2019-10-07 13:21:56.859450760 +0200
> > @@ -3550,6 +3550,10 @@ cp_build_array_ref (location_t loc, tree
> >     {
> >       tree ar = cp_default_conversion (array, complain);
> >       tree ind = cp_default_conversion (idx, complain);
> > +    tree first = NULL_TREE;
> > +
> > +    if (flag_strong_eval_order == 2 && TREE_SIDE_EFFECTS (ind))
> > +      ar = first = save_expr (ar);
> 
> save_expr will make a copy of the array, won't it?  That's unlikely to be

No.  First of all, this is on the else branch of
TREE_CODE (TREE_TYPE (array)) == ARRAY_TYPE, so either array is a pointer,
or idx is an array, or pointer, and it is after cp_default_conversion, so I
think ar must be either a pointer or integer.
I haven't touched the ARRAY_REF case earlier, because that I believe is
handled right in the gimplifier already.

> > +      if (flag_strong_eval_order == 2
> > +     && TREE_SIDE_EFFECTS (TREE_OPERAND (*expr_p, 1)))
> > +   {
> > +     enum gimplify_status t
> > +       = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
> > +                        is_gimple_val, fb_rvalue);
> > +     if (t == GS_ERROR)
> > +       break;
> > +     else if (is_gimple_variable (TREE_OPERAND (*expr_p, 0))
> > +              && TREE_CODE (TREE_OPERAND (*expr_p, 0)) != SSA_NAME)
> > +       TREE_OPERAND (*expr_p, 0)
> > +         = get_initialized_tmp_var (TREE_OPERAND (*expr_p, 0), pre_p,
> > +                                    NULL);
> 
> I still think this pattern would be cleaner with a new gimple predicate that
> returns true for invariant || SSA_NAME.  I think that would have the same
> effect as this code.

The problem is that we need a reliable way to discover safe GIMPLE
temporaries for that to work.  If SSA_NAME is created, great, but in various
contexts (OpenMP/OpenACC bodies, and various other cases) allow_ssa is
false, at which point the gimplifier creates a temporary artificial VAR_DECL.
If there is a predicate that rejects such a temporary, gimplify_expr will
ICE:
      gcc_assert (!VOID_TYPE_P (TREE_TYPE (*expr_p)));
      *expr_p = get_formal_tmp_var (*expr_p, pre_p);
...
  /* Make sure the temporary matches our predicate.  */
  gcc_assert ((*gimple_test_f) (*expr_p));

Now, we could have a predicate that does invariant || SSA_NAME || (VAR_P &&
DECL_ARTIFICIAL), but I'm not certain that is good enough, DECL_ARTIFICIAL
are also TARGET_EXPR temporaries and many others and I couldn't convince
myself one can't write a valid testcase that would still fail.
Of course, we could add some VAR_DECL flag and set it on the
create_tmp_from_val created temporaries, but is that the way to go?

        Jakub

Reply via email to