On Fri, 2020-01-31 at 12:04 -0700, Martin Sebor wrote:
> Attached is a reworked patch since the first one didn't go far
> enough to solve the major problems. The new solution relies on
> get_range_strlen_dynamic the same way as the sprintf optimization,
> and does away with the determine_min_objsize function and calling
> compute_builtin_object_size.
>
> To minimize testsuite fallout I extended get_range_strlen to handle
> a couple more kinds expressions(*), but I still had to xfail and
> disable a few tests that were relying on being able to use the type
> of the destination object as the upper bound on the string length.
>
> Tested on x86_64-linux.
>
> Martin
>
> [*] With all the issues around MEM_REFs and types this change needs
> extra scrutiny. I'm still not sure I fully understand what can and
> what cannot be safely relied on at this level.
>
> On 1/15/20 6:18 AM, Martin Sebor wrote:
> > The strcmp optimization newly introduced in GCC 10 relies on
> > the size of the smallest referenced array object to determine
> > whether the function can return zero. When the size of
> > the object is smaller than the length of the other string
> > argument the optimization folds the equality to false.
> >
> > The bug report has identified a couple of problems here:
> > 1) when the access to the array object is via a pointer to
> > a (possibly indirect) member of a union, in GIMPLE the pointer
> > may actually point to a different member than the one in
> > the original source code. Thus the size of the array may
> > appear to be smaller than in the source code which can then
> > result in the optimization being invalid.
> > 2) when the pointer in the access may point to two or more
> > arrays of different size (i.e., it's the result of a PHI),
> > assuming it points to the smallest of them can also lead
> > to an incorrect result when the optimization is applied.
> >
> > The attached patch adjusts the optimization to 1) avoid making
> > any assumptions about the sizes of objects accessed via union
> > types, and b) use the size of the largest object in PHI nodes.
> >
> > Tested on x86_64-linux.
> >
> > Martin
>
>
> PR tree-optimization/92765 - wrong code for strcmp of a union member
>
> gcc/ChangeLog:
>
> PR tree-optimization/92765
> * gimple-fold.c (get_range_strlen_tree): Handle MEM_REF and PARM_DECL.
> * tree-ssa-strlen.c (compute_string_length): Remove.
> (determine_min_objsize): Remove.
> (get_len_or_size): Add an argument. Call get_range_strlen_dynamic.
> Avoid using type size as the upper bound on string length.
> (handle_builtin_string_cmp): Add an argument. Adjust.
> (strlen_check_and_optimize_call): Pass additional argument to
> handle_builtin_string_cmp.
>
> gcc/testsuite/ChangeLog:
>
> PR tree-optimization/92765
> * g++.dg/tree-ssa/strlenopt-1.C: New test.
> * g++.dg/tree-ssa/strlenopt-2.C: New test.
> * gcc.dg/Warray-bounds-58.c: New test.
> * gcc.dg/Wrestrict-20.c: Avoid a valid -Wformat-overflow.
> * gcc.dg/Wstring-compare.c: Xfail a test.
> * gcc.dg/strcmpopt_2.c: Disable tests.
> * gcc.dg/strcmpopt_4.c: Adjust tests.
> * gcc.dg/strcmpopt_10.c: New test.
> * gcc.dg/strlenopt-69.c: Disable tests.
> * gcc.dg/strlenopt-92.c: New test.
> * gcc.dg/strlenopt-93.c: New test.
> * gcc.dg/strlenopt.h: Declare calloc.
> * gcc.dg/tree-ssa/pr92056.c: Xfail tests until pr93518 is resolved.
> * gcc.dg/tree-ssa/builtin-sprintf-warn-23.c: Correct test (pr93517).
>
> diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
> index ed225922269..d70ac67e1ca 100644
> --- a/gcc/gimple-fold.c
> +++ b/gcc/gimple-fold.c
> @@ -1280,7 +1280,7 @@ get_range_strlen_tree (tree arg, bitmap *visited,
> strlen_range_kind rkind,
> c_strlen_data *pdata, unsigned eltsize)
> {
> gcc_assert (TREE_CODE (arg) != SSA_NAME);
> -
> +
> /* The length computed by this invocation of the function. */
> tree val = NULL_TREE;
>
> @@ -1422,7 +1422,42 @@ get_range_strlen_tree (tree arg, bitmap *visited,
> strlen_range_kind rkind,
> type about the length here. */
> tight_bound = true;
> }
> - else if (VAR_P (arg))
> + else if (TREE_CODE (arg) == MEM_REF
> + && TREE_CODE (TREE_TYPE (arg)) == ARRAY_TYPE
> + && TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) == INTEGER_TYPE
> + && TREE_CODE (TREE_OPERAND (arg, 0)) == ADDR_EXPR)
> + {
> + /* Handle a MEM_REF into a DECL accessing an array of integers,
> + being conservative about references to extern structures with
> + flexible array members that can be initialized to arbitrary
> + numbers of elements as an extension (static structs are okay).
> + FIXME: Make this less conservative -- see
> + component_ref_size in tree.c. */
I think it's generally been agreed that we can look at sizes of _DECL
nodes and this code doesn't look like this walks backwards through
casts or anything like that. So the worry would be if we forward
propagated through a cast into the MEM_REF node.
It looks like forwprop only propagates through "compatible" pointer
conversions. It makes me a bit nervous.
Jakub/Richi, comments on this hunk?
> diff --git a/gcc/testsuite/gcc.dg/Wstring-compare.c
> b/gcc/testsuite/gcc.dg/Wstring-compare.c
> index 0ca492db0ab..d1534bf7555 100644
> --- a/gcc/testsuite/gcc.dg/Wstring-compare.c
> +++ b/gcc/testsuite/gcc.dg/Wstring-compare.c
In general I have a slight preference for pulling these into new files
when we need to xfail them. Why? Because a test which previously
passed, but now fails (even an xfail) causes the tester to flag the
build as failing due to a testsuite regression.
But I don't think that preference is significant enough to ask you to
redo the work. Just something to ponder in the future.
> diff --git a/gcc/testsuite/gcc.dg/strcmpopt_2.c
> b/gcc/testsuite/gcc.dg/strcmpopt_2.c
> index 57d8f651c28..f31761be173 100644
> --- a/gcc/testsuite/gcc.dg/strcmpopt_2.c
> +++ b/gcc/testsuite/gcc.dg/strcmpopt_2.c
So I'd pulled f1, f3, f5, f7 into a new file. But disabling them like
you've done is reasonable as well.
> diff --git a/gcc/testsuite/gcc.dg/strcmpopt_4.c
> b/gcc/testsuite/gcc.dg/strcmpopt_4.c
> index 4e26522eed1..b07fbb6b7b0 100644
> --- a/gcc/testsuite/gcc.dg/strcmpopt_4.c
> +++ b/gcc/testsuite/gcc.dg/strcmpopt_4.c
THanks for creating the new test. I'd done the exact same thing in my
local tree. I'm a little surprised that f_param passes. Can you
double check that?
diff --git a/gcc/testsuite/gcc.dg/strlenopt-69.c
b/gcc/testsuite/gcc.dg/strlenopt-69.c
> index 9ad8e2e8aac..9df6eeccb97 100644
> --- a/gcc/testsuite/gcc.dg/strlenopt-69.c
> +++ b/gcc/testsuite/gcc.dg/strlenopt-69.c
I'd pulled the offending tests into a new file, but your approach is
fine too.
>
> diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
> index ad9e98973b1..b9972c16e18 100644
> --- a/gcc/tree-ssa-strlen.c
> +++ b/gcc/tree-ssa-strlen.c
> -
> -/* Given strinfo IDX for ARG, set LENRNG[] to the range of lengths
> - of the string(s) referenced by ARG if it can be determined.
> - If the length cannot be determined, set *SIZE to the size of
> +/* Given strinfo IDX for ARG, sets LENRNG[] to the range of lengths
> + of the string(s) referenced by ARG if it can be determined.
> + If the length cannot be determined, sets *SIZE to the size of
> the array the string is stored in, if any. If no such array is
> - known, set *SIZE to -1. When the strings are nul-terminated set
> - *NULTERM to true, otherwise to false. Return true on success. */
> + known, sets *SIZE to -1. When the strings are nul-terminated sets
> + *NULTERM to true, otherwise to false. When nonnull uses RVALS to
> + determine range information. Returns true on success. */
"When nonnull uses RVALS to detemrine range information." That isn't a
sentence and just doesn't seem to make sense. Please review for
comment clarity.
This looks OK to me with the comment fixed. I like that we drop the
whole determine_min_objsize and replace it it the standard range bits
that we're using elsewhere.
Please give Richi and Jakub time to chime in on the gimple-fold.c
changes.
Jeff