On 11/5/20 12:29 PM, Martin Sebor wrote:
On 10/1/20 11:25 AM, Martin Sebor wrote:
On 10/1/20 9:34 AM, Aldy Hernandez wrote:
On 10/1/20 3:22 PM, Andrew MacLeod wrote:
> On 10/1/20 5:05 AM, Aldy Hernandez via Gcc-patches wrote:
>>> Thanks for doing all this! There isn't anything I don't understand
>>> in the sprintf changes so no questions from me (well, almost none).
>>> Just some comments:
>> Thanks for your comments on the sprintf/strlen API conversion.
>>
>>> The current call statement is available in all functions that take
>>> a directive argument, as dir->info.callstmt. There should be no
need
>>> to also add it as a new argument to the functions that now need it.
>> Fixed.
>>
>>> The change adds code along these lines in a bunch of places:
>>>
>>> + value_range vr;
>>> + if (!query->range_of_expr (vr, arg, stmt))
>>> + vr.set_varying (TREE_TYPE (arg));
>>>
>>> I thought under the new Ranger APIs when a range couldn't be
>>> determined it would be automatically set to the maximum for
>>> the type. I like that and have been moving in that direction
>>> with my code myself (rather than having an API fail, have it
>>> set the max range and succeed).
>> I went through all the above idioms and noticed all are being
used on
>> supported types (integers or pointers). So range_of_expr will
always
>> return true. I've removed the if() and the set_varying.
>>
>>> Since that isn't so in this case, I think it would still be nice
>>> if the added code could be written as if the range were set to
>>> varying in this case and (ideally) reduced to just initialization:
>>>
>>> value_range vr = some-function (query, stmt, arg);
>>>
>>> some-function could be an inline helper defined just for the
sprintf
>>> pass (and maybe also strlen which also seems to use the same
pattern),
>>> or it could be a value_range AKA irange ctor, or it could be a
member
>>> of range_query, whatever is the most appropriate.
>>>
>>> (If assigning/copying a value_range is thought to be too expensive,
>>> declaring it first and then passing it to that helper to set it
>>> would work too).
>>>
>>> In strlen, is the removed comment no longer relevant? (I.e., does
>>> the ranger solve the problem?)
>>>
>>> - /* The range below may be "inaccurate" if a constant has
been
>>> - substituted earlier for VAL by this pass that hasn't been
>>> - propagated through the CFG. This shoud be fixed by the
new
>>> - on-demand VRP if/when it becomes available (hopefully in
>>> - GCC 11). */
>> It should.
>>
>>> I'm wondering about the comment added to get_range_strlen_dynamic
>>> and other places:
>>>
>>> + // FIXME: Use range_query instead of global ranges.
>>>
>>> Is that something you're planning to do in a followup or should
>>> I remember to do it at some point?
>> I'm not planning on doing it. It's just a reminder that it would be
>> beneficial to do so.
>>
>>> Otherwise I have no concern with the changes.
>> It's not cleared whether Andrew approved all 3 parts of the patchset
>> or just the valuation part. I'll wait for his nod before committing
>> this chunk.
>>
>> Aldy
>>
> I have no issue with it, so OK.
Pushed all 3 patches.
>
> Just an observation that should be pointed out, I believe Aldy has
all
> the code for converting to a ranger, but we have not pursued that any
> further yet since there is a regression due to our lack of
equivalence
> processing I think? That should be resolved in the coming month,
but at
> the moment is a holdback/concern for converting these passes...
iirc.
Yes. Martin, the take away here is that the strlen/sprintf pass has
been converted to the new API, but ranger is still not up and running
on it (even on the branch).
With the new API, all you have to do is remove all instances of
evrp_range_analyzer and replace them with a ranger. That's it.
Below is an untested patch that would convert you to a ranger once
it's contributed.
IIRC when I enabled the ranger for your pass a while back, there was
one or two regressions due to missing equivalences, and the rest were
because the tests were expecting an actual specific range, and the
ranger returned a slightly different/better one. You'll need to
adjust your tests.
Ack. I'll be on the lookout for the ranger commit (if you hppen
to remember and CC me on it just in case I might miss it that would
be great).
I have applied the patch and ran some tests. There are quite
a few failures (see the list below). I have only looked at
a couple. The one in in gcc.dg/tree-ssa/builtin-sprintf-warn-3.c
boils down to the following test case. There should be no warning
for either sprintf call. The one in h() is a false positive and
the reason for at least some of the regressions. Somehow,
the conversions between int and char are causing Ranger to lose
the range.
$ cat t.c && gcc -O2 -S -Wall t.c
char a[2];
extern int x;
signed char f (int min, int max)
{
signed char i = x;
return i < min || max < i ? min : i;
}
void ff (signed char i)
{
__builtin_sprintf (a, "%i", f (0, 9)); // okay
}
signed char g (signed char min, signed char max)
{
signed char i = x;
return i < min || max < i ? min : i;
}
void gg (void)
{
__builtin_sprintf (a, "%i", g (0, 9)); // bogus warning
}
t.c: In function ‘gg’:
t.c:24:26: warning: ‘%i’ directive writing between 1 and 4 bytes into a
region of size 2 [-Wformat-overflow=]
24 | __builtin_sprintf (a, "%i", g (0, 9)); // bogus warning
| ^~
t.c:24:25: note: directive argument in the range [-128, 127]
24 | __builtin_sprintf (a, "%i", g (0, 9)); // bogus warning
| ^~~~
t.c:24:3: note: ‘__builtin_sprintf’ output between 2 and 5 bytes into a
destination of size 2
24 | __builtin_sprintf (a, "%i", g (0, 9)); // bogus warning
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Another failure (the one in builtin-sprintf-warn-22.c) is this where
the false positive is due to the strlen pass somehow having lost
the upper bound on the sum of the two string lengths.
I'm guessing this might have something to do with the strlen pass
calling set_range_info() on the nonconstant results of strlen calls
it can determine the range for. How would I go about doing it with
ranger? I see ranger_cache::set_global_range() and the class ctor
takes a gimple_ranger as argument. Would calling the function be
the right way to do it? (I couldn't find anything on the Wiki.)
$ cat t.c && gcc -O2 -S -Wall t.c
typedef __SIZE_TYPE__ size_t;
char a[1025];
void g (char *s1, char *s2)
{
size_t n = __builtin_strlen (s1), d = __builtin_strlen (s2);
if (n + d + 1 >= 1025)
return;
__builtin_sprintf (a, "%s.%s", s1, s2); // { dg-bogus
"\\\[-Wformat-overflow" }
}
t.c: In function ‘g’:
t.c:11:29: warning: ‘%s’ directive writing up to 1023 bytes into a
region of size between 1 and 1024 [-Wformat-overflow=]
11 | __builtin_sprintf (a, "%s.%s", s1, s2); // { dg-bogus
"\\\[-Wformat-overflow" }
| ^~
t.c:11:3: note: ‘__builtin_sprintf’ output between 2 and 2048 bytes into
a destination of size 1025
11 | __builtin_sprintf (a, "%s.%s", s1, s2); // { dg-bogus
"\\\[-Wformat-overflow" }
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I'll see if I can reduce the others into test cases but I'll have
to hold off on enabling the ranger in the sprintf/strlen pass until
these issues are resolved.
Thanks
Martin
FAIL: gcc.dg/Wstringop-overflow-20.c (test for warnings, line 28)
FAIL: gcc.dg/Wstringop-overflow-20.c (test for warnings, line 29)
FAIL: gcc.dg/Wstringop-overflow-20.c (test for warnings, line 32)
FAIL: gcc.dg/Wstringop-overflow-20.c (test for warnings, line 38)
FAIL: gcc.dg/Wstringop-overflow-20.c (test for warnings, line 39)
XPASS: gcc.dg/pr80776-1.c (test for bogus messages, line 22)
FAIL: gcc.dg/strlenopt-81.c (test for excess errors)
FAIL: gcc.dg/strlenopt-90.c scan-tree-dump-not strlen1 "strlen \\(\\&a"
FAIL: gcc.dg/strlenopt-80.c scan-tree-dump-times optimized
"failure_on_line \\(" 0
FAIL: gcc.dg/strlenopt-89.c scan-tree-dump-not strlen1 "strlen \\(\\&a"
FAIL: gcc.dg/strlenopt-89.c scan-tree-dump-not strlen1 "a1\\] = 0;"
FAIL: gcc.dg/strlenopt-89.c scan-tree-dump-not strlen1 "a2 \\+ 1B\\] = 0;"
FAIL: gcc.dg/strlenopt-89.c scan-tree-dump-not strlen1 "a3 \\+ 2B\\] = 0;"
FAIL: gcc.dg/strlenopt-89.c scan-tree-dump-not strlen1 "a4 \\+ 3B\\] = 0;"
FAIL: gcc.dg/strlenopt-89.c scan-tree-dump-not strlen1 "a5 \\+ 4B\\] = 0;"
FAIL: gcc.dg/strlenopt-89.c scan-tree-dump-not strlen1 "a6 \\+ 5B\\] = 0;"
FAIL: gcc.dg/strlenopt-89.c scan-tree-dump-not strlen1 "a7 \\+ 6B\\] = 0;"
FAIL: gcc.dg/strlenopt-89.c scan-tree-dump-not strlen1 "a8 \\+ 7B\\] = 0;"
FAIL: gcc.dg/tree-ssa/builtin-sprintf-warn-3.c (test for excess errors)
(many failures)
FAIL: gcc.dg/tree-ssa/builtin-sprintf-warn-22.c (test for bogus
messages, line 21)
Thanks
Martin
Aldy
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index f4d1c5ca256..9f9e95b7155 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -58,8 +58,8 @@ along with GCC; see the file COPYING3. If not see
#include "tree-ssa-loop.h"
#include "tree-scalar-evolution.h"
#include "vr-values.h"
-#include "gimple-ssa-evrp-analyze.h"
#include "tree-ssa.h"
+#include "gimple-range.h"
/* A vector indexed by SSA_NAME_VERSION. 0 means unknown, positive
value
is an index into strinfo vector, negative value stands for
@@ -5755,16 +5755,13 @@ class strlen_dom_walker : public dom_walker
public:
strlen_dom_walker (cdi_direction direction)
: dom_walker (direction),
- evrp (false),
m_cleanup_cfg (false)
{}
virtual edge before_dom_children (basic_block);
virtual void after_dom_children (basic_block);
- /* EVRP analyzer used for printf argument range processing, and
- to track strlen results across integer variable assignments. */
- evrp_range_analyzer evrp;
+ gimple_ranger ranger;
/* Flag that will trigger TODO_cleanup_cfg to be returned in strlen
execute function. */
@@ -5777,8 +5774,6 @@ public:
edge
strlen_dom_walker::before_dom_children (basic_block bb)
{
- evrp.enter (bb);
-
basic_block dombb = get_immediate_dominator (CDI_DOMINATORS, bb);
if (dombb == NULL)
@@ -5853,13 +5848,7 @@ strlen_dom_walker::before_dom_children
(basic_block bb)
/* Attempt to optimize individual statements. */
for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p
(gsi); )
{
- gimple *stmt = gsi_stmt (gsi);
-
- /* First record ranges generated by this statement so they
- can be used by printf argument processing. */
- evrp.record_ranges_from_stmt (stmt, false);
-
- if (check_and_optimize_stmt (&gsi, &cleanup_eh, &evrp))
+ if (check_and_optimize_stmt (&gsi, &cleanup_eh, &ranger))
gsi_next (&gsi);
}
@@ -5878,8 +5867,6 @@ strlen_dom_walker::before_dom_children
(basic_block bb)
void
strlen_dom_walker::after_dom_children (basic_block bb)
{
- evrp.leave (bb);
-
if (bb->aux)
{
stridx_to_strinfo = ((vec<strinfo *, va_heap, vl_embed> *)
bb->aux);