OK.

On Tue, Sep 13, 2016 at 3:55 AM, Jakub Jelinek <ja...@redhat.com> wrote:
> Hi!
>
> The testcase below shows a bug in POINTER_PLUS_EXPR constexpr handling.
> It is handled by 2 different functions, cxx_eval_pointer_plus_expression
> and cxx_eval_binary_expression, where the first one calls
> cxx_eval_constant_expression on both operands and in some cases optimizes
> it, in other cases returns NULL_TREE.  The other function is called
> only if the former returned NULL_TREE, and calls
> cxx_eval_constant_expression on both operands again and always builds
> something.  For C++11 I guess it is fine, but for C++14 if there are any
> side-effects in the operands of POINTER_PLUS_EXPR, like i++ in the testcase,
> this means that i++ is evaluated twice if the first function doesn't find
> the &foo[bar] + baz pattern it is looking for.
>
> Fixed by adding this folding into cxx_eval_binary_expression instead,
> actually as it is large enough into a helper function of it, which is
> invoked on the already cxx_eval_constant_expression processed arguments.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk/6.3?
>
> 2016-09-13  Jakub Jelinek  <ja...@redhat.com>
>
>         PR c++/77553
>         * constexpr.c (cxx_fold_pointer_plus_expression): New function.
>         (cxx_eval_binary_expression): Use it for POINTER_PLUS_EXPR.
>         (cxx_eval_pointer_plus_expression): Remove.
>         (cxx_eval_constant_expression) <case POINTER_PLUS_EXPR>: Don't
>         call cxx_eval_pointer_plus_expression.
>
>         * g++.dg/cpp1y/constexpr-77553.C: New test.
>
> --- gcc/cp/constexpr.c.jj       2016-09-06 20:00:36.000000000 +0200
> +++ gcc/cp/constexpr.c  2016-09-11 15:18:24.103581105 +0200
> @@ -1813,6 +1813,63 @@ cxx_eval_unary_expression (const constex
>    return r;
>  }
>
> +/* Helper function for cxx_eval_binary_expression.  Try to optimize
> +   original POINTER_PLUS_EXPR T, LHS p+ RHS, return NULL_TREE if the
> +   generic folding should be used.  */
> +
> +static tree
> +cxx_fold_pointer_plus_expression (const constexpr_ctx *ctx, tree t,
> +                                 tree lhs, tree rhs, bool *non_constant_p,
> +                                 bool *overflow_p)
> +{
> +  STRIP_NOPS (lhs);
> +  if (TREE_CODE (lhs) != ADDR_EXPR)
> +    return NULL_TREE;
> +
> +  lhs = TREE_OPERAND (lhs, 0);
> +
> +  /* &A[i] p+ j => &A[i + j] */
> +  if (TREE_CODE (lhs) == ARRAY_REF
> +      && TREE_CODE (TREE_OPERAND (lhs, 1)) == INTEGER_CST
> +      && TREE_CODE (rhs) == INTEGER_CST
> +      && TYPE_SIZE_UNIT (TREE_TYPE (lhs))
> +      && TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (lhs))) == INTEGER_CST)
> +    {
> +      tree orig_type = TREE_TYPE (t);
> +      location_t loc = EXPR_LOCATION (t);
> +      tree type = TREE_TYPE (lhs);
> +
> +      t = fold_convert_loc (loc, ssizetype, TREE_OPERAND (lhs, 1));
> +      tree nelts = array_type_nelts_top (TREE_TYPE (TREE_OPERAND (lhs, 0)));
> +      nelts = cxx_eval_constant_expression (ctx, nelts, false, 
> non_constant_p,
> +                                           overflow_p);
> +      if (*non_constant_p)
> +       return NULL_TREE;
> +      /* Don't fold an out-of-bound access.  */
> +      if (!tree_int_cst_le (t, nelts))
> +       return NULL_TREE;
> +      rhs = cp_fold_convert (ssizetype, rhs);
> +      /* Don't fold if rhs can't be divided exactly by TYPE_SIZE_UNIT.
> +        constexpr int A[1]; ... (char *)&A[0] + 1 */
> +      if (!integer_zerop (fold_build2_loc (loc, TRUNC_MOD_EXPR, sizetype,
> +                                          rhs, TYPE_SIZE_UNIT (type))))
> +       return NULL_TREE;
> +      /* Make sure to treat the second operand of POINTER_PLUS_EXPR
> +        as signed.  */
> +      rhs = fold_build2_loc (loc, EXACT_DIV_EXPR, ssizetype, rhs,
> +                            TYPE_SIZE_UNIT (type));
> +      t = size_binop_loc (loc, PLUS_EXPR, rhs, t);
> +      t = build4_loc (loc, ARRAY_REF, type, TREE_OPERAND (lhs, 0),
> +                     t, NULL_TREE, NULL_TREE);
> +      t = cp_build_addr_expr (t, tf_warning_or_error);
> +      t = cp_fold_convert (orig_type, t);
> +      return cxx_eval_constant_expression (ctx, t, /*lval*/false,
> +                                          non_constant_p, overflow_p);
> +    }
> +
> +  return NULL_TREE;
> +}
> +
>  /* Subroutine of cxx_eval_constant_expression.
>     Like cxx_eval_unary_expression, except for binary expressions.  */
>
> @@ -1865,6 +1922,9 @@ cxx_eval_binary_expression (const conste
>         error ("arithmetic involving a null pointer in %qE", lhs);
>        return t;
>      }
> +  else if (code == POINTER_PLUS_EXPR)
> +    r = cxx_fold_pointer_plus_expression (ctx, t, lhs, rhs, non_constant_p,
> +                                         overflow_p);
>
>    if (r == NULL_TREE)
>      r = fold_binary_loc (loc, code, type, lhs, rhs);
> @@ -3579,69 +3639,6 @@ cxx_eval_switch_expr (const constexpr_ct
>    return NULL_TREE;
>  }
>
> -/* Subroutine of cxx_eval_constant_expression.
> -   Attempt to reduce a POINTER_PLUS_EXPR expression T.  */
> -
> -static tree
> -cxx_eval_pointer_plus_expression (const constexpr_ctx *ctx, tree t,
> -                                 bool lval, bool *non_constant_p,
> -                                 bool *overflow_p)
> -{
> -  tree orig_type = TREE_TYPE (t);
> -  tree op00 = TREE_OPERAND (t, 0);
> -  tree op01 = TREE_OPERAND (t, 1);
> -  location_t loc = EXPR_LOCATION (t);
> -
> -  op00 = cxx_eval_constant_expression (ctx, op00, lval,
> -                                      non_constant_p, overflow_p);
> -
> -  STRIP_NOPS (op00);
> -  if (TREE_CODE (op00) != ADDR_EXPR)
> -    return NULL_TREE;
> -
> -  op01 = cxx_eval_constant_expression (ctx, op01, lval,
> -                                      non_constant_p, overflow_p);
> -  op00 = TREE_OPERAND (op00, 0);
> -
> -  /* &A[i] p+ j => &A[i + j] */
> -  if (TREE_CODE (op00) == ARRAY_REF
> -      && TREE_CODE (TREE_OPERAND (op00, 1)) == INTEGER_CST
> -      && TREE_CODE (op01) == INTEGER_CST
> -      && TYPE_SIZE_UNIT (TREE_TYPE (op00))
> -      && TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (op00))) == INTEGER_CST)
> -    {
> -      tree type = TREE_TYPE (op00);
> -      t = fold_convert_loc (loc, ssizetype, TREE_OPERAND (op00, 1));
> -      tree nelts = array_type_nelts_top (TREE_TYPE (TREE_OPERAND (op00, 0)));
> -      nelts = cxx_eval_constant_expression (ctx, nelts, false, 
> non_constant_p,
> -                                           overflow_p);
> -      if (*non_constant_p)
> -       return NULL_TREE;
> -      /* Don't fold an out-of-bound access.  */
> -      if (!tree_int_cst_le (t, nelts))
> -       return NULL_TREE;
> -      op01 = cp_fold_convert (ssizetype, op01);
> -      /* Don't fold if op01 can't be divided exactly by TYPE_SIZE_UNIT.
> -        constexpr int A[1]; ... (char *)&A[0] + 1 */
> -      if (!integer_zerop (fold_build2_loc (loc, TRUNC_MOD_EXPR, sizetype,
> -                                          op01, TYPE_SIZE_UNIT (type))))
> -       return NULL_TREE;
> -      /* Make sure to treat the second operand of POINTER_PLUS_EXPR
> -        as signed.  */
> -      op01 = fold_build2_loc (loc, EXACT_DIV_EXPR, ssizetype, op01,
> -                             TYPE_SIZE_UNIT (type));
> -      t = size_binop_loc (loc, PLUS_EXPR, op01, t);
> -      t = build4_loc (loc, ARRAY_REF, type, TREE_OPERAND (op00, 0),
> -                     t, NULL_TREE, NULL_TREE);
> -      t = cp_build_addr_expr (t, tf_warning_or_error);
> -      t = cp_fold_convert (orig_type, t);
> -      return cxx_eval_constant_expression (ctx, t, lval, non_constant_p,
> -                                          overflow_p);
> -    }
> -
> -  return NULL_TREE;
> -}
> -
>  /* Attempt to reduce the expression T to a constant value.
>     On failure, issue diagnostic and return error_mark_node.  */
>  /* FIXME unify with c_fully_fold */
> @@ -3984,12 +3981,6 @@ cxx_eval_constant_expression (const cons
>        break;
>
>      case POINTER_PLUS_EXPR:
> -      r = cxx_eval_pointer_plus_expression (ctx, t, lval, non_constant_p,
> -                                           overflow_p);
> -      if (r)
> -       break;
> -      /* fall through */
> -
>      case PLUS_EXPR:
>      case MINUS_EXPR:
>      case MULT_EXPR:
> --- gcc/testsuite/g++.dg/cpp1y/constexpr-77553.C.jj     2016-09-11 
> 15:31:41.692253287 +0200
> +++ gcc/testsuite/g++.dg/cpp1y/constexpr-77553.C        2016-09-11 
> 15:31:07.000000000 +0200
> @@ -0,0 +1,29 @@
> +// PR c++/77553
> +// { dg-do compile { target c++14 } }
> +
> +constexpr void
> +bar (int *x)
> +{
> +  int i = 0;
> +  x[i++] = 1;
> +  x[3] = i;
> +}
> +
> +constexpr int
> +foo ()
> +{
> +  int a[] = { 0, 0, 0, 0 };
> +  bar (a);
> +
> +  return a[0] + 8 * a[1] + 64 * a[2] + 512 * a[3];
> +}
> +
> +constexpr int b = foo ();
> +
> +int
> +main ()
> +{
> +  static_assert (b == 513, "");
> +  if (foo () != 513)
> +    __builtin_abort ();
> +}
>
>         Jakub

Reply via email to