This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[C++ PATCH] Fix constexpr POINTER_PLUS_EXPR handling (PR c++/77553)
- From: Jakub Jelinek <jakub at redhat dot com>
- To: Jason Merrill <jason at redhat dot com>
- Cc: gcc-patches at gcc dot gnu dot org
- Date: Tue, 13 Sep 2016 09:55:15 +0200
- Subject: [C++ PATCH] Fix constexpr POINTER_PLUS_EXPR handling (PR c++/77553)
- Authentication-results: sourceware.org; auth=none
- Reply-to: Jakub Jelinek <jakub at redhat dot com>
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 <jakub@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