On 2/3/22 10:52, Jakub Jelinek wrote:
On Tue, Jan 18, 2022 at 05:40:48PM +0100, Jakub Jelinek via Gcc-patches wrote:
About the rest of the patch, I thought I had seen richi comment on IRC (but
can't find the comment now) that these cases where we could give a constant
answer but decide not to because of C++ rules should be handled in the front
end rather than generic code, which makes sense to me; that is, use
folding_initializer only for giving more constant results, not for giving
fewer constant results. Maybe add another flag for limiting constant
results if we think it's significantly easier to recognize these cases in
fold?
I'm afraid avoiding the match.pd & fold-const code here would be a lot of
work.
Sorry for dropping the ball on this.
Here is a patch that introduces folding_cxx_constexpr flag and documents
that folding_initializer should be used for enabling more folding
optimizations, while folding_cxx_constexpr should be used to prevent
optimizations C++ doesn't allow in the constant expression evaluation.
Ok for trunk if it passes bootstrap/regtest?
2022-02-03 Jakub Jelinek <ja...@redhat.com>
PR c++/89074
PR c++/104033
* fold-const.h (folding_initializer): Adjust comment.
(folding_cxx_constexpr): Declare.
* fold-const.cc (folding_initializer): Adjust comment.
(folding_cxx_constexpr): New variable.
(address_compare): Restrict the decl vs. STRING_CST
or vice versa or STRING_CST vs. STRING_CST or
is_global_var != is_global_var optimizations to !folding_cxx_constexpr.
Punt for FUNCTION_DECLs with non-zero offsets. If folding_initializer,
assume non-aliased functions have non-zero size and have different
addresses. For folding_cxx_constexpr, punt on comparisons of start
of some object and end of another one, regardless whether it is a decl
or string literal. Also punt for folding_cxx_constexpr on
STRING_CST vs. STRING_CST comparisons if the two literals could be
overlapping.
* constexpr.cc (cxx_eval_binary_expression): Temporarily set
folding_cxx_constexpr.
* g++.dg/cpp1y/constexpr-89074-3.C: New test.
--- gcc/fold-const.h.jj 2022-02-01 20:10:51.235856007 +0100
+++ gcc/fold-const.h 2022-02-03 15:02:02.700228631 +0100
@@ -20,9 +20,16 @@ along with GCC; see the file COPYING3.
#ifndef GCC_FOLD_CONST_H
#define GCC_FOLD_CONST_H
-/* Non-zero if we are folding constants inside an initializer; zero
- otherwise. */
+/* Nonzero if we are folding constants inside an initializer or a C++
+ manifestly-constant-evaluated context; zero otherwise.
+ Should be used when folding in initializer enables additional
+ optimizations. */
extern int folding_initializer;
+/* Nonzer of we are folding C++ manifestly-constant-evaluated context; zero
"Nonzero if"
+ otherwise.
+ Should be used when certain constructs shouldn't be optimized
+ during folding in that context. */
+extern bool folding_cxx_constexpr;
/* Convert between trees and native memory representation. */
extern int native_encode_expr (const_tree, unsigned char *, int, int off =
-1);
--- gcc/fold-const.cc.jj 2022-02-03 14:31:32.243129408 +0100
+++ gcc/fold-const.cc 2022-02-03 16:25:27.541467112 +0100
@@ -86,9 +86,17 @@ along with GCC; see the file COPYING3.
#include "gimple-range.h"
/* Nonzero if we are folding constants inside an initializer or a C++
- manifestly-constant-evaluated context; zero otherwise. */
+ manifestly-constant-evaluated context; zero otherwise.
+ Should be used when folding in initializer enables additional
+ optimizations. */
int folding_initializer = 0;
+/* Nonzer of we are folding C++ manifestly-constant-evaluated context; zero
"Nonzero if"
+ otherwise.
+ Should be used when certain constructs shouldn't be optimized
+ during folding in that context. */
+bool folding_cxx_constexpr = false;
+
/* The following constants represent a bit based encoding of GCC's
comparison operators. This encoding simplifies transformations
on relational comparison operators, such as AND and OR. */
@@ -16628,41 +16636,91 @@ address_compare (tree_code code, tree ty
HOST_WIDE_INT ioff0 = -1, ioff1 = -1;
off0.is_constant (&ioff0);
off1.is_constant (&ioff1);
- if ((DECL_P (base0) && TREE_CODE (base1) == STRING_CST)
- || (TREE_CODE (base0) == STRING_CST && DECL_P (base1))
- || (TREE_CODE (base0) == STRING_CST
- && TREE_CODE (base1) == STRING_CST
- && ioff0 >= 0 && ioff1 >= 0
- && ioff0 < TREE_STRING_LENGTH (base0)
- && ioff1 < TREE_STRING_LENGTH (base1)
- /* This is a too conservative test that the STRING_CSTs
- will not end up being string-merged. */
- && strncmp (TREE_STRING_POINTER (base0) + ioff0,
- TREE_STRING_POINTER (base1) + ioff1,
- MIN (TREE_STRING_LENGTH (base0) - ioff0,
- TREE_STRING_LENGTH (base1) - ioff1)) != 0))
+ if (!folding_cxx_constexpr
+ && ((DECL_P (base0) && TREE_CODE (base1) == STRING_CST)
+ || (TREE_CODE (base0) == STRING_CST && DECL_P (base1))
+ || (TREE_CODE (base0) == STRING_CST
+ && TREE_CODE (base1) == STRING_CST
+ && ioff0 >= 0 && ioff1 >= 0
+ && ioff0 < TREE_STRING_LENGTH (base0)
+ && ioff1 < TREE_STRING_LENGTH (base1)
+ /* This is a too conservative test that the STRING_CSTs
+ will not end up being string-merged. */
+ && strncmp (TREE_STRING_POINTER (base0) + ioff0,
+ TREE_STRING_POINTER (base1) + ioff1,
+ MIN (TREE_STRING_LENGTH (base0) - ioff0,
+ TREE_STRING_LENGTH (base1) - ioff1)) != 0)))
;
- else if (!DECL_P (base0) || !DECL_P (base1))
+ /* Punt on non-zero offsets from functions. */
+ else if ((TREE_CODE (base0) == FUNCTION_DECL && ioff0)
+ || (TREE_CODE (base1) == FUNCTION_DECL && ioff1))
+ return 2;
+ else if ((!DECL_P (base0)
+ && (!folding_cxx_constexpr || TREE_CODE (base0) != STRING_CST))
+ || (!DECL_P (base1)
+ && (!folding_cxx_constexpr || TREE_CODE (base1) != STRING_CST)))
I think it would be clearer to leave the !DECL_P case alone and add
/* In C++ it is unspecified, and so non-constant, whether two
equivalent strings have the same address. */
else if (folding_cxx_constexpr
&& (TREE_CODE (base0) == STRING_CST
|| TREE_CODE (base1) == STRING_CST)
OK with that change and the above typo fixes.
return 2;
/* If this is a pointer comparison, ignore for now even
valid equalities where one pointer is the offset zero
of one object and the other to one past end of another one. */
- else if (!folding_initializer && !INTEGRAL_TYPE_P (type))
+ else if (!folding_cxx_constexpr && !INTEGRAL_TYPE_P (type))
;
/* Assume that automatic variables can't be adjacent to global
variables. */
- else if (is_global_var (base0) != is_global_var (base1))
+ else if (!folding_cxx_constexpr
+ && is_global_var (base0) != is_global_var (base1))
+ ;
+ /* For initializers, assume addresses of different functions are
+ different. */
+ else if (folding_initializer
+ && TREE_CODE (base0) == FUNCTION_DECL
+ && TREE_CODE (base1) == FUNCTION_DECL)
;
else
{
- tree sz0 = DECL_SIZE_UNIT (base0);
- tree sz1 = DECL_SIZE_UNIT (base1);
- /* If sizes are unknown, e.g. VLA or not representable, punt. */
- if (!tree_fits_poly_int64_p (sz0) || !tree_fits_poly_int64_p (sz1))
- return 2;
+ poly_int64 size0, size1;
+ if (TREE_CODE (base0) == STRING_CST)
+ {
+ if (ioff0 < 0
+ || ioff0 > TREE_STRING_LENGTH (base0))
+ return 2;
+ size0 = TREE_STRING_LENGTH (base0);
+ }
+ /* For initializers, assume function decls don't overlap and have
+ non-empty size. */
+ else if (folding_initializer && TREE_CODE (base0) == FUNCTION_DECL)
+ size0 = 1;
+ else
+ {
+ tree sz0 = DECL_SIZE_UNIT (base0);
+ /* If sizes are unknown, e.g. VLA or not representable, punt. */
+ if (!tree_fits_poly_int64_p (sz0))
+ return 2;
+
+ size0 = tree_to_poly_int64 (sz0);
+ }
+
+ if (TREE_CODE (base1) == STRING_CST)
+ {
+ if (ioff1 < 0
+ || ioff1 > TREE_STRING_LENGTH (base1))
+ return 2;
+ size1 = TREE_STRING_LENGTH (base1);
+ }
+ /* For initializers, assume function decls don't overlap and have
+ non-empty size. */
+ else if (folding_initializer && TREE_CODE (base1) == FUNCTION_DECL)
+ size1 = 1;
+ else
+ {
+ tree sz1 = DECL_SIZE_UNIT (base1);
+ /* If sizes are unknown, e.g. VLA or not representable, punt. */
+ if (!tree_fits_poly_int64_p (sz1))
+ return 2;
+
+ size1 = tree_to_poly_int64 (sz1);
+ }
- poly_int64 size0 = tree_to_poly_int64 (sz0);
- poly_int64 size1 = tree_to_poly_int64 (sz1);
/* If one offset is pointing (or could be) to the beginning of one
object and the other is pointing to one past the last byte of the
other object, punt. */
@@ -16678,6 +16736,27 @@ address_compare (tree_code code, tree ty
&& (known_ne (off0, 0)
|| (known_ne (size0, 0) && known_ne (size1, 0))))
equal = 0;
+ if (equal == 0
+ && TREE_CODE (base0) == STRING_CST
+ && TREE_CODE (base1) == STRING_CST)
+ {
+ /* If the bytes in the string literals starting at the pointers
+ differ, the pointers need to be different. */
+ if (memcmp (TREE_STRING_POINTER (base0) + ioff0,
+ TREE_STRING_POINTER (base1) + ioff1,
+ MIN (TREE_STRING_LENGTH (base0) - ioff0,
+ TREE_STRING_LENGTH (base1) - ioff1)) == 0)
+ {
+ HOST_WIDE_INT ioffmin = MIN (ioff0, ioff1);
+ if (memcmp (TREE_STRING_POINTER (base0) + ioff0 - ioffmin,
+ TREE_STRING_POINTER (base1) + ioff1 - ioffmin,
+ ioffmin) == 0)
+ /* If even the bytes in the string literal before the
+ pointers are the same, the string literals could be
+ tail merged. */
+ return 2;
+ }
+ }
}
return equal;
}
--- gcc/cp/constexpr.cc.jj 2022-01-26 19:40:22.957796808 +0100
+++ gcc/cp/constexpr.cc 2022-02-03 15:04:00.760558570 +0100
@@ -3413,7 +3413,10 @@ cxx_eval_binary_expression (const conste
if (ctx->manifestly_const_eval
&& (flag_constexpr_fp_except
|| TREE_CODE (type) != REAL_TYPE))
- r = fold_binary_initializer_loc (loc, code, type, lhs, rhs);
+ {
+ auto ofcc = make_temp_override (folding_cxx_constexpr, true);
+ r = fold_binary_initializer_loc (loc, code, type, lhs, rhs);
+ }
else
r = fold_binary_loc (loc, code, type, lhs, rhs);
}
--- gcc/testsuite/g++.dg/cpp1y/constexpr-89074-3.C.jj 2022-02-03
14:58:53.734901694 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-89074-3.C 2022-02-03
14:58:53.734901694 +0100
@@ -0,0 +1,132 @@
+// PR c++/89074
+// { dg-do compile { target c++14 } }
+
+int fn1 (void) { return 0; }
+int fn2 (void) { return 1; }
+
+constexpr bool
+f1 ()
+{
+ char a[] = { 1, 2, 3, 4 };
+
+ if (&a[1] == "foo")
+ return false;
+
+ if (&a[1] == &"foo"[4])
+ return false;
+
+ if (&"foo"[1] == &a[0])
+ return false;
+
+ if (&"foo"[3] == &a[4])
+ return false;
+
+ if (&a[0] == "foo")
+ return false;
+
+ // Pointer to start of one object (var) and end of another one (literal)
+ if (&a[0] == &"foo"[4]) // { dg-error "is not a constant expression" }
+ return false;
+
+ return true;
+}
+
+constexpr bool
+f2 ()
+{
+ char a[] = { 1, 2, 3, 4 };
+
+ // Pointer to end of one object (var) and start of another one (literal)
+ if (&a[4] == "foo") // { dg-error "is not a constant expression" }
+ return false;
+
+ return true;
+}
+
+char v[] = { 1, 2, 3, 4 };
+
+constexpr bool
+f3 ()
+{
+ char a[] = { 1, 2, 3, 4 };
+
+ if (&a[1] == &v[1])
+ return false;
+
+ if (&a[0] == &v[3])
+ return false;
+
+ if (&a[2] == &v[4])
+ return false;
+
+ // Pointer to start of one object (automatic var) and end of another one
(non-automagic var)
+ if (&a[0] == &v[4]) // { dg-error "is not a constant expression" }
+ return false;
+
+ return true;
+}
+
+constexpr bool
+f4 ()
+{
+ char a[] = { 1, 2, 3, 4, 5 };
+
+ // Pointer to end of one object (automatic var) and start of another one
(non-automagic var)
+ if (&a[5] == &v[0]) // { dg-error "is not a constant expression" }
+ return false;
+
+ return true;
+}
+
+constexpr bool
+f5 ()
+{
+ if (fn1 != fn1)
+ return false;
+
+ if (fn1 == fn2)
+ return false;
+
+ if (&"abcde"[0] == &"edcba"[1])
+ return false;
+
+ if (&"abcde"[1] == &"edcba"[6])
+ return false;
+
+ // Pointer to start of one object (literal) and end of another one (literal)
+ if (&"abcde"[0] == &"edcba"[6]) // { dg-error "is not a constant
expression" }
+ return false;
+
+ return true;
+}
+
+constexpr bool
+f6 ()
+{
+ // Pointer to start of one object (literal) and end of another one (literal)
+ if (&"abcde"[6] == &"edcba"[0]) // { dg-error "is not a constant
expression" }
+ return false;
+
+ return true;
+}
+
+constexpr bool
+f7 ()
+{
+ if (&"abcde"[3] == &"fabcde"[3])
+ return false;
+
+ // These could be suffix merged, with &"abcde"[0] == &"fabcde"[1].
+ if (&"abcde"[3] == &"fabcde"[4]) // { dg-error "is not a constant
expression" }
+ return false;
+
+ return true;
+}
+
+constexpr bool a = f1 ();
+constexpr bool b = f2 ();
+constexpr bool c = f3 ();
+constexpr bool d = f4 ();
+constexpr bool e = f5 ();
+constexpr bool f = f6 ();
+constexpr bool g = f7 ();
Jakub