diff mbox series

convert -Walloca pass to ranger

Message ID 3ed6bac0-46f4-097b-cc73-ad7f87ba16f5@redhat.com
State New
Headers show
Series convert -Walloca pass to ranger | expand

Commit Message

Aldy Hernandez Oct. 5, 2020, 9:51 a.m. UTC
The walloca pass is a mess.  It has all sorts of heuristics to divine 
problematic ranges fed into alloca, none of them very good, and all of 
them unreadable.  The mess therein was actually one of the original 
motivators for the ranger project (along with array bounds checking).

The attached patch is a conversion of the pass to ranger.  It's mostly 
an exercise in removing code.  The entire pass almost reduces to:

+  // If the user specified a limit, use it.
+  int_range_max r;
+  if (warn_limit_specified_p (is_vla)
+      && TREE_CODE (len) == SSA_NAME
+      && query.range_of_expr (r, len, stmt)
+      && !r.varying_p ())
+    {
+      // The invalid bits are anything outside of [0, MAX_SIZE].
+      static int_range<2> invalid_range (build_int_cst (size_type_node, 0),
+                                        build_int_cst (size_type_node,
+                                                       max_size),
+                                        VR_ANTI_RANGE);
+
+      r.intersect (invalid_range);
+      if (r.undefined_p ())
+       return alloca_type_and_limit (ALLOCA_OK);
+
+      return alloca_type_and_limit (ALLOCA_BOUND_MAYBE_LARGE,
+                                   wi::to_wide (integer_zero_node));
      }

That is, if the range of the integer passed to alloca is outside of 
[0,MAX_SIZE], warn, otherwise it's ok.  Plain and simple.

You will notice I removed the nuanced errors we gave before-- like 
trying to guess whether the problematic range came by virtue of a signed 
cast conversion.  These specific errors were never part of the original 
design, they were just stuff we could guess by how the IL looked.  It 
was non-exact and fragile.  Now we just say the alloca argument may be 
too large, period.

It the future, I would even like to remove the specific range the ranger 
was able to compute from the error message itself.  As will become 
obvious, the ranger can get pretty outrageous ranges that are entirely 
non-obvious by looking at the code.  Peppering the error messages with 
these ranges will ultimately just confuse the user.  But alas, that's a 
problem for another patch to solve.

This patch goes on top of the ranger which Andrew just posted.  It's 
likely to be adjusted as the ranger is committed.

     gcc/ChangeLog:

             * gimple-ssa-warn-alloca.c (enum alloca_type): Remove
             ALLOCA_BOUND_UNKNOWN and ALLOCA_CAST_FROM_SIGNED.
             (warn_limit_specified_p): New.
             (alloca_call_type_by_arg): Remove.
             (cast_from_signed_p): Remove.
             (is_max): Remove.
             (alloca_call_type): Remove heuristics and replace with call 
into
             ranger.
             (pass_walloca::execute): Instantiate ranger.

     gcc/testsuite/ChangeLog:

             * gcc.dg/Walloca-1.c: Adjust for ranger.
             * gcc.dg/Walloca-12.c: Same.
             * gcc.dg/Walloca-13.c: Same.
             * gcc.dg/Walloca-2.c: Same.
             * gcc.dg/Walloca-3.c: Same.
             * gcc.dg/Walloca-6.c: Same.

  void g (__SIZE_TYPE__ n)

Comments

David Malcolm Oct. 5, 2020, 3:28 p.m. UTC | #1
On Mon, 2020-10-05 at 11:51 +0200, Aldy Hernandez via Gcc-patches
wrote:
> The walloca pass is a mess.  It has all sorts of heuristics to
> divine 
> problematic ranges fed into alloca, none of them very good, and all
> of 
> them unreadable.  The mess therein was actually one of the original 
> motivators for the ranger project (along with array bounds checking).
> 
> The attached patch is a conversion of the pass to ranger.  It's
> mostly 
> an exercise in removing code.  The entire pass almost reduces to:
> 
> +  // If the user specified a limit, use it.
> +  int_range_max r;
> +  if (warn_limit_specified_p (is_vla)
> +      && TREE_CODE (len) == SSA_NAME
> +      && query.range_of_expr (r, len, stmt)
> +      && !r.varying_p ())
> +    {
> +      // The invalid bits are anything outside of [0, MAX_SIZE].
> +      static int_range<2> invalid_range (build_int_cst
> (size_type_node, 0),
> +                                        build_int_cst
> (size_type_node,
> +                                                       max_size),
> +                                        VR_ANTI_RANGE);
> +
> +      r.intersect (invalid_range);
> +      if (r.undefined_p ())
> +       return alloca_type_and_limit (ALLOCA_OK);
> +
> +      return alloca_type_and_limit (ALLOCA_BOUND_MAYBE_LARGE,
> +                                   wi::to_wide (integer_zero_node));
>       }
> 
> That is, if the range of the integer passed to alloca is outside of 
> [0,MAX_SIZE], warn, otherwise it's ok.  Plain and simple.
> 
> You will notice I removed the nuanced errors we gave before-- like 
> trying to guess whether the problematic range came by virtue of a
> signed 
> cast conversion.  These specific errors were never part of the
> original 
> design, they were just stuff we could guess by how the IL
> looked.  It 
> was non-exact and fragile.  Now we just say the alloca argument may
> be 
> too large, period.
> 
> It the future, I would even like to remove the specific range the
> ranger 
> was able to compute from the error message itself.  As will become 
> obvious, the ranger can get pretty outrageous ranges that are
> entirely 
> non-obvious by looking at the code.  Peppering the error messages
> with 
> these ranges will ultimately just confuse the user.  But alas, that's
> a 
> problem for another patch to solve.

I can't comment on the content of the patch itself, but with my "user
experience hat" on I'm wondering what diagnostic messages involving
ranges ought to look like.  I worry that if we simply drop range
information altogether from the messages, we're not giving the user
enough information to make a judgment call on whether to pay attention
to the diagnostic.  That said, I'm unhappy with the status quo of our
range-based messages, so I don't object to the patch.

Some possible ideas:

I added support for capturing and printing control-flow paths for
diagnostics in gcc 10; see gcc/diagnostic-path.h.  I added this for the
analyzer code, but it's usable outside of it.  It's possible to use
this to associate a chain of events with a diagnostic, by building a
diagnostic_path and associating it with a rich_location.  Perhaps the
ranger code could have a mode which captures diagnostic_paths, where
the events in the path give pertinent information about ranges - e.g.
the various conditionals that lead to a particular range.  There's a
pre-canned simple_diagnostic_path that can be populated with
simple_diagnostic_event, or you could create your own ranger-specific
event and path subclasses.  These can be temporary objects that live on
the stack next to the rich_location, just for the lifetime of emitting
the diagnostic.  Perhaps a "class ranger_rich_location" which
implicitly supplies an instance of "class ranger_diagnostic_path" to
the diagnostic, describing where the range information comes from?


Here are some more out-there ideas I had some years ago (I can't
remember if these ever made it to the mailing list).  These ideas focus
more on how the ranges are used, rather than where the ranges come
from.  There's an interaction, though, so I think it's on-topic - can
the ranger code supply this kind of information?

Consider this problematic call to sprintf:

$ cat demo.c
#include <stdio.h>

const char *test_1 (const char *msg)
{
  static char buf[16];
  sprintf (buf, "msg: %s\n", msg);
  return buf; 
}

void test_2 ()
{
  test_1 ("this is long enough to cause trouble");
}

Right now, we emit this (this is trunk, plus some fixes for line-
numbering bugs):

$ ./xgcc -B. -c demo.c  -Wall -O2
demo.c: In function ‘test_2’:
demo.c:6:23: warning: ‘%s’ directive writing 36 bytes into a region of
size 11 [-Wformat-overflow=]
     6 |   sprintf (buf, "msg: %s\n", msg);
       |                       ^~
demo.c:12:11:
    12 |   test_1 ("this is long enough to cause trouble");
       |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
demo.c:6:3: note: ‘sprintf’ output 43 bytes into a destination of size
16
     6 |   sprintf (buf, "msg: %s\n", msg);
       |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I brainstormed some ideas on making these kinds of warning easier for
the user to understand.

We could use the labeling-of-source-ranges to print something like:

demo.c: In function ‘test_2’:
demo.c:6:23: warning: ‘%s’ directive writing 36 bytes into a region of size 11 [-Wformat-overflow=]
     6 |   sprintf (buf, "msg: %s\n", msg);
       |            ~~~        ^~
       |            |          |
       |            |          required space: 36 bytes
       |            remaining capacity: 11 bytes
demo.c:12:11:
    12 |   test_1 ("this is long enough to cause trouble");
       |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       |           |
       |           required space: 36 bytes
demo.c:6:3: note: ‘sprintf’ output 43 bytes into a destination of size 16
     6 |   sprintf (buf, "msg: %s\n", msg);
       |            ~~~   ^~~~~~~~~
       |            |          |
       |            |          required space: 43 bytes
       |            size: 16 bytes

(making a distinction between "size" and "remaining capacity",
depending on whether the code is writing to the start of the buffer or
not)

Underlining "buf" requires access to its source location, which might
not be available yet in the C frontend.

A tweak to this would be to show the point where the overflow occurs
(if the substring location code is up to it...):

demo.c: In function ‘test_2’:
demo.c:6:23: warning: ‘%s’ directive writing 36 bytes into a region of
size 11 [-Wformat-overflow=]
     6 |   sprintf (buf, "msg: %s\n", msg);
       |            ~~~        ^~
       |            |          |
       |            |          required space: 36 bytes
       |            remaining capacity: 11 bytes
demo.c:12:11:
    12 |   test_1 ("this is long enough to cause trouble");
       |                       ^~~~~~~~~~~~~~~~~~~~~~~~~
       |                       |
       |                       overflow occurs here
demo.c:6:3: note: ‘sprintf’ output 43 bytes into a destination of size
16
     6 |   sprintf (buf, "msg: %s\n", msg);
       |            ~~~   ^~~~~~~~~
       |            |          |
       |            |          required space: 43 bytes
       |            size: 16 bytes


Getting really fancy, we could emit an ASCII art visualization to
(hopefully) make the buffer overflow crystal-clear (with an option to
disable it):

demo.c:6:3: note: buffer overflow [-fdiagnostics-show-buffer-overflow]
  snprintf of "%s" from:
                        |+---+---+ ... +---+---+|+---+---+ ... +---+---+|
                        ||  0|  1|     |  9| 10||| 11| 12|     | 41| 42||
                        ||'t'|'h'|     |'o'|'n'|||'g'|' '|     |'e'|NUL||
                        |+---+---+ ... +---+---+|+---+---+ ... +---+---+|
                        vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
                        |<---   ok          --->|<--     overflow    -->|
                        |                       |                       |
  to 'buf':
  |                     |                       |
  ++---+---+---+---+---+|+---+---+ ... +---+---+|
  ||  0|  1|  2|  3|   4||5  |  6|     | 14| 15||
  ||'m'|'s'|'g'|':'|' '|||'t'|'h'|     |'o'|'n'||
  ++---+---+---+---+---+|+---+---+ ... +---+---+|
                        |                       |

(thus showing the buffer content where it's known, eliding the middle
when it goes above 5 elements)

The parts on the right-hand side ("overflow" etc) could be colorized in
red.

Here's another version of the same UI idea, for the same diagnostic,
which tries to show the string data (in the name of readability; I
think this one is better than the above):

demo.c:6:3: note: buffer overflow...
  snprintf of "%s" from:
            |+-------------+|+---------------------------++---+|
            ||    0 - 10   |||          11  - 41         || 42||
            ||"this is lon"|||"g enough to cause trouble"||NUL||
            |+-------------+|+---------------------------++---+|
            vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
            |<---   ok  --->|<--           overflow         -->|
            |               |                                  |
  to 'buf':
  |         |               |
  ++-------+|+-------------+|
  || 0 - 4 |||   5 - 15    ||
  ||"msg: "|||"this is lon"||
  ++-------+|+-------------+|


Here's another possible visualization, of a different overflow:

  snprintf of "%s" from:

                    |+---+ ... +---+|+---+ ... +---|
                    || 0 |     | n |||n+1|     | 31|
                    |+---+ ... +---+|+---+ ... +---|
                    vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
                    |<---   ok  --->|<--overflow-->|
                    |               |              |
  to 'buf':
    |               |               |
    ++---+ ... +---+|+---+ ... +---+|
    || 0 |     | x |||x+1|     | 15||
    ++---+ ... +---+|+---+ ... +---+|

In this one, 32 bytes are being written into an unknown point ("x+1")
within a buffer of size 16.

A simple example where the overflowing write is to the start of the
buffer:

sprintf of an unbounded string to a fixed-size buf[100]:

demo.c:6:3: note: buffer overflow...
  snprintf of "%s" from:
  |+------+|+--------------++-------+|
  ||0...99|||100...strlen-1|| strlen||
  ||      |||              ||  NUL  ||
  |+------+|+--------------++-------+|
  vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
  |<--ok-->|<--   overflow        -->|
  |        |                         |
  to 'buf':
  |        |
  |+------+|
  ||0...99||
  |+------+|


Copying a string to a buffer allocated with strlen(), rather than
strlen() + 1:

  |                  |          |
  |+----------------+|+--------+|
  ||0...strlen() - 1||   NUL   ||
  |+----------------+|+--------+|
  vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
  |<---   ok      -->|<overflow>|
  |                  |          |
  to 'buf':
  |                  |
  |+----------------+|
  ||0...strlen() - 1||
  |+----------------+|

Further random UI ideas (brainstormed in Emacs; I have no idea if these
are *good* ideas):

   warning: buffer overflow: writing 9-110 bytes into a buffer with capacity 80 [-Wformat-overflow=]
   7 |   __builtin_sprintf (buf, "/%s/%s-%i.tmp", tmpdir, fname, num);
     |                      ~~~   ~~~^~~~~~~~~~
     |                      |        |
     |                      |        writing 9...110 bytes
     |                      capacity: 80 bytes
   note: details
   7 |   __builtin_sprintf (buf, "/%s/%s-%i.tmp", tmpdir, fname, num);
     |                      ~~~   ab~cd~ef~g~~~h
     |                      |     || || || |   |
     |                      |     || || || |   1 byte (NUL terminator)
     |                      |     || || || 4 bytes (".tmp")
     |                      |     || || |1...16 bytes ("%i" on 'num')
     |                      |     || || 1 byte ("-")
     |                      |     || |0-8 bytes ("%s" on 'fname')
     |                      |     || 1 byte ("/")
     |                      |     |0-79 bytes ("%s" on 'tmpdir')
     |                      |     1 byte ("/")
     |                      capacity: 80 bytes
   note: layout [-fsome-option-enabling-this]

(with alternating colorization to better distinguish all those ranges
and labels)

       |+---+--------------+----+-------------+-----+-----------+------+------+
start @||0  |1             |1-80|2-81         |10-89|11-90      |12-106|16-110|
  size:||  1|     0 - 79   |   1|            8|    1|    1-16???|     4|     1|
       ||"/"|%s on 'tmpdir'| "/"|%s on 'fname'| "-" |%i on 'num'|".tmp"|  NUL |
       |+---+--------------+----+-------------+-----+-----------+------+------+

or with a vertical orientation:

   note: layout [-fsome-option-enabling-this]
   +----------------+-----------+--------+---------------+
   |element         |starting at|    size|cumulative size|
   +----------------+-----------+--------+---------------+
   |"/"             |         0 |      1 |             1 |
   |"%s" on 'tmpdir'|         1 | 0 - 79 |        1 - 80 |
   |"/"             |    1 - 80 |      1 |        2 - 81 |
   |"%s" on 'fname' |    2 - 81 |  0 - 7 |        2 - 88 |
   |"-"             |    2 - 88 |      1 |        3 - 89 |
   |"%i" on 'num'   |    3 - 89 |  1- 16 |       4 - 105 |
   |".tmp"          |   4 - 105 |      4 |       8 - 109 |
   |NUL terminator  |   8 - 109 |      1 |       9 - 110 |
   +----------------+-----------+--------+---------------+

(I've probably got some of the numbers wrong above, but hopefully you
get the idea of where I'm going with this).

Maybe some kind of highlight to show where we can exceed the buffer
capacity.

I like calling out the NUL terminator explicitly (as it's so easy to
get wrong), and putting "buffer overflow" upfront in the text of the
warning, as I did above.

[...snip...]

Hope this is constructive
Dave
Andrew MacLeod Oct. 5, 2020, 3:48 p.m. UTC | #2
On 10/5/20 11:28 AM, David Malcolm via Gcc-patches wrote:
> On Mon, 2020-10-05 at 11:51 +0200, Aldy Hernandez via Gcc-patches
> wrote:
>> The walloca pass is a mess.  It has all sorts of heuristics to
>> divine
>> problematic ranges fed into alloca, none of them very good, and all
>> of
>> them unreadable.  The mess therein was actually one of the original
>> motivators for the ranger project (along with array bounds checking).
>>
>> The attached patch is a conversion of the pass to ranger.  It's
>> mostly
>> an exercise in removing code.  The entire pass almost reduces to:
>>
>> +  // If the user specified a limit, use it.
>> +  int_range_max r;
>> +  if (warn_limit_specified_p (is_vla)
>> +      && TREE_CODE (len) == SSA_NAME
>> +      && query.range_of_expr (r, len, stmt)
>> +      && !r.varying_p ())
>> +    {
>> +      // The invalid bits are anything outside of [0, MAX_SIZE].
>> +      static int_range<2> invalid_range (build_int_cst
>> (size_type_node, 0),
>> +                                        build_int_cst
>> (size_type_node,
>> +                                                       max_size),
>> +                                        VR_ANTI_RANGE);
>> +
>> +      r.intersect (invalid_range);
>> +      if (r.undefined_p ())
>> +       return alloca_type_and_limit (ALLOCA_OK);
>> +
>> +      return alloca_type_and_limit (ALLOCA_BOUND_MAYBE_LARGE,
>> +                                   wi::to_wide (integer_zero_node));
>>        }
>>
>> That is, if the range of the integer passed to alloca is outside of
>> [0,MAX_SIZE], warn, otherwise it's ok.  Plain and simple.
>>
>> You will notice I removed the nuanced errors we gave before-- like
>> trying to guess whether the problematic range came by virtue of a
>> signed
>> cast conversion.  These specific errors were never part of the
>> original
>> design, they were just stuff we could guess by how the IL
>> looked.  It
>> was non-exact and fragile.  Now we just say the alloca argument may
>> be
>> too large, period.
>>
>> It the future, I would even like to remove the specific range the
>> ranger
>> was able to compute from the error message itself.  As will become
>> obvious, the ranger can get pretty outrageous ranges that are
>> entirely
>> non-obvious by looking at the code.  Peppering the error messages
>> with
>> these ranges will ultimately just confuse the user.  But alas, that's
>> a
>> problem for another patch to solve.
> I can't comment on the content of the patch itself, but with my "user
> experience hat" on I'm wondering what diagnostic messages involving
> ranges ought to look like.  I worry that if we simply drop range
> information altogether from the messages, we're not giving the user
> enough information to make a judgment call on whether to pay attention
> to the diagnostic.  That said, I'm unhappy with the status quo of our
> range-based messages, so I don't object to the patch.

I also dont know what the right answer is regarding dumping of ranges.  
Most of the time I suspect the error only needs the end points, not the 
fun stuff in the middle if its a multi-range... but I suppose thats up 
to the actual issuer of the message to decide if thats important.
>
> Some possible ideas:
>
> I added support for capturing and printing control-flow paths for
> diagnostics in gcc 10; see gcc/diagnostic-path.h.  I added this for the
> analyzer code, but it's usable outside of it.  It's possible to use
> this to associate a chain of events with a diagnostic, by building a
> diagnostic_path and associating it with a rich_location.  Perhaps the
> ranger code could have a mode which captures diagnostic_paths, where
> the events in the path give pertinent information about ranges - e.g.
> the various conditionals that lead to a particular range.  There's a
> pre-canned simple_diagnostic_path that can be populated with
> simple_diagnostic_event, or you could create your own ranger-specific
> event and path subclasses.  These can be temporary objects that live on
> the stack next to the rich_location, just for the lifetime of emitting
> the diagnostic.  Perhaps a "class ranger_rich_location" which
> implicitly supplies an instance of "class ranger_diagnostic_path" to
> the diagnostic, describing where the range information comes from?
For debugging I have a trace, but it doesnt log the trace. just sort of 
logs as it does the walk backwards to determine things.  I use it to 
figure out where things have gone amok.

   THis sort of thing would probably be best tackled by simply 
inheriting from a ranger and overloading the core routines to log them.. 
thats effectively what the tracer does. just emits info before and after 
each of the core 5 calls.  I suppose one could just as easily log the 
info that comes back, and with a little bit of extra understanding, 
determine whether this is a "new" value or a previously computed one and 
automatically do what I am doing manually when determining the 
origination of something.

>
> Here are some more out-there ideas I had some years ago (I can't
> remember if these ever made it to the mailing list).  These ideas focus
> more on how the ranges are used, rather than where the ranges come
> from.  There's an interaction, though, so I think it's on-topic - can
> the ranger code supply this kind of information?
>
>
what kind of information are we talking about?  if you are talking about 
where the ranges came from, im sure one could collect that together in a 
derived class.  The ranger itself is really just cobbling together the 
various sources of information that are calculated by its components.  
Its really just an organizer and bookkeeper. You could probably cobble 
together whatever bits of information you wanted to stash them together.

Andrew
Martin Sebor Oct. 5, 2020, 5:12 p.m. UTC | #3
On 10/5/20 3:51 AM, Aldy Hernandez via Gcc-patches wrote:
> The walloca pass is a mess.  It has all sorts of heuristics to divine 
> problematic ranges fed into alloca, none of them very good, and all of 
> them unreadable.  The mess therein was actually one of the original 
> motivators for the ranger project (along with array bounds checking).
> 
> The attached patch is a conversion of the pass to ranger.  It's mostly 
> an exercise in removing code.  The entire pass almost reduces to:
> 
> +  // If the user specified a limit, use it.
> +  int_range_max r;
> +  if (warn_limit_specified_p (is_vla)
> +      && TREE_CODE (len) == SSA_NAME
> +      && query.range_of_expr (r, len, stmt)
> +      && !r.varying_p ())
> +    {
> +      // The invalid bits are anything outside of [0, MAX_SIZE].
> +      static int_range<2> invalid_range (build_int_cst (size_type_node, 
> 0),
> +                                        build_int_cst (size_type_node,
> +                                                       max_size),
> +                                        VR_ANTI_RANGE);
> +
> +      r.intersect (invalid_range);
> +      if (r.undefined_p ())
> +       return alloca_type_and_limit (ALLOCA_OK);
> +
> +      return alloca_type_and_limit (ALLOCA_BOUND_MAYBE_LARGE,
> +                                   wi::to_wide (integer_zero_node));
>       }
> 
> That is, if the range of the integer passed to alloca is outside of 
> [0,MAX_SIZE], warn, otherwise it's ok.  Plain and simple.

It looks like a nice simplification to me!  You're the author
of the alloca pass so I have no concerns with the changes (but
I appreciate the heads up).  That said, I do want to respond
to your commentary and add a few notes of my own.

Eventually, I'd like all the -Wfoo-larger-than= warnings to use
the same "core logic" when deciding whether to warn or not.  As it
is, they're not completely consistent with each other and some warn
when others don't and vice versa.  For instance:

$ cat z.c && gcc -O2 -S -Wall -Walloca-larger-than=1000 
-Walloc-size-larger-than=1000 z.c
void f0 (void*);

void f1 (int n)
{
   f0 (__builtin_alloca (n * sizeof (int)));   // warning
}

void f2 (int n)
{
   f0 (__builtin_malloc (n * sizeof (int)));   // silence
}

z.c: In function ‘f1’:
z.c:5:3: warning: argument to ‘alloca’ may be too large 
[-Walloca-larger-than=]
     5 |   f0 (__builtin_alloca (n * sizeof (int)));   // warning
       |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
z.c:5:3: note: limit is 1000 bytes, but argument may be as large as 
18446744073709551612

That's because -Walloca-larger-than= (and -Wvla-larger-than=) is
designed to warn when it can't prove that the argument isn't too
large, while the others use the conventional approach of warning
only when they can prove that the argument is excessive.

On the other hand, without -Walloca-larger-than=, for the example
below GCC issues -Walloc-size-larger-than= but not the former, even
though the alloca call is more likely to cause serious trouble than
the one to malloc.

$ cat z.c && gcc -O2 -S -Wall z.c
void f0 (void*);

void f1 (int n)
{
   if (n >= 0) n = -1;
   f0 (__builtin_alloca (n * sizeof (int)));
}

void f2 (int n)
{
   if (n >= 0) n = -1;
   f0 (__builtin_malloc (n * sizeof (int)));
}

z.c: In function ‘f2’:
z.c:12:3: warning: argument 1 range [18446744065119617024, 
18446744073709551612] exceeds maximum object size 9223372036854775807 
[-Walloc-size-larger-than=]
    12 |   f0 (__builtin_malloc (n * sizeof (int)));
       |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
z.c:12:3: note: in a call to built-in allocation function ‘__builtin_malloc’

(This seems like a bug/missing feature in -Walloca-larger-than=.)

> You will notice I removed the nuanced errors we gave before-- like 
> trying to guess whether the problematic range came by virtue of a signed 
> cast conversion.  These specific errors were never part of the original 
> design, they were just stuff we could guess by how the IL looked.  It 
> was non-exact and fragile.  Now we just say the alloca argument may be 
> too large, period.

That makes sense to me, as long as the note following the warning
still mentions the limit in effect and the range (or at least
the bound in excess of the limit).

> 
> It the future, I would even like to remove the specific range the ranger 
> was able to compute from the error message itself.  As will become 
> obvious, the ranger can get pretty outrageous ranges that are entirely 
> non-obvious by looking at the code.  Peppering the error messages with 
> these ranges will ultimately just confuse the user.  But alas, that's a 
> problem for another patch to solve.

I agree that when it comes to sizes where just one bound of the range
is used to decide whether or not to warn (the lower bound in the case
of most warnings but, as the example above shows, the upper bound for
-Walloca-larger-than=), printing multiple subranges is unnecessary
and could easily be confusing.  Even printing the very large bounds
(in decimal) in the warning above may be too much.  At the same time,
simply printing:

   warning: argument to ‘alloca’ may be too large

and nothing else wouldn't be helpful either.  (Though it would make
the alloca pass real simple ;-)  It's a matter of deciding what
the right amount of detail is in each instance and choosing the best
representation for the values included in it (small values are okay
in decimal, larger values may be better formatted in hex, or involving
well-known symbolic constants like INT_MAX or PTRDIFF_MAX).  But
different people have different ideas about how much detail is enough
and what presentation is best.  Rather than each of us imposing our
individual preference on all users and ending up with arbitrary
inconsistencies between warnings we should design them so that their
output can be customized.  If I like to see the full ranges in warnings
in decimal I should be able to ask GCC to do that. If I just care about
the high level details (say just the more important bound) and prefer
hex for large numbers there should be a way to do that too.  It's just
a matter of adding a %R or some such to the pretty-printer to format
a range and exposing an option that will let users choose the format.
(It still leaves room for us to argue about the defaults ;)

Martin

> 
> This patch goes on top of the ranger which Andrew just posted.  It's 
> likely to be adjusted as the ranger is committed.
> 
>      gcc/ChangeLog:
> 
>              * gimple-ssa-warn-alloca.c (enum alloca_type): Remove
>              ALLOCA_BOUND_UNKNOWN and ALLOCA_CAST_FROM_SIGNED.
>              (warn_limit_specified_p): New.
>              (alloca_call_type_by_arg): Remove.
>              (cast_from_signed_p): Remove.
>              (is_max): Remove.
>              (alloca_call_type): Remove heuristics and replace with call 
> into
>              ranger.
>              (pass_walloca::execute): Instantiate ranger.
> 
>      gcc/testsuite/ChangeLog:
> 
>              * gcc.dg/Walloca-1.c: Adjust for ranger.
>              * gcc.dg/Walloca-12.c: Same.
>              * gcc.dg/Walloca-13.c: Same.
>              * gcc.dg/Walloca-2.c: Same.
>              * gcc.dg/Walloca-3.c: Same.
>              * gcc.dg/Walloca-6.c: Same.
> 
> diff --git a/gcc/gimple-ssa-warn-alloca.c b/gcc/gimple-ssa-warn-alloca.c
> index 9e80e5dbbd9..33824a7a091 100644
> --- a/gcc/gimple-ssa-warn-alloca.c
> +++ b/gcc/gimple-ssa-warn-alloca.c
> @@ -36,6 +36,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "calls.h"
>   #include "cfgloop.h"
>   #include "intl.h"
> +#include "gimple-range.h"
> 
>   static unsigned HOST_WIDE_INT adjusted_warn_limit (bool);
> 
> @@ -99,12 +100,6 @@ enum alloca_type {
>     // Alloca argument may be too large.
>     ALLOCA_BOUND_MAYBE_LARGE,
> 
> -  // Alloca argument is bounded but of an indeterminate size.
> -  ALLOCA_BOUND_UNKNOWN,
> -
> -  // Alloca argument was casted from a signed integer.
> -  ALLOCA_CAST_FROM_SIGNED,
> -
>     // Alloca appears in a loop.
>     ALLOCA_IN_LOOP,
> 
> @@ -135,6 +130,15 @@ public:
>     }
>   };
> 
> +/* Return TRUE if the user specified a limit for either VLAs or 
> ALLOCAs.  */
> +
> +static bool
> +warn_limit_specified_p (bool is_vla)
> +{
> +  unsigned HOST_WIDE_INT max = is_vla ? warn_vla_limit : 
> warn_alloca_limit;
> +  return max != HOST_WIDE_INT_MAX;
> +}
> +
>   /* Return the value of the argument N to -Walloca-larger-than= or
>      -Wvla-larger-than= adjusted for the target data model so that
>      when N == HOST_WIDE_INT_MAX, the adjusted value is set to
> @@ -158,183 +162,15 @@ adjusted_warn_limit (bool idx)
>     return limits[idx];
>   }
> 
> -
> -// NOTE: When we get better range info, this entire function becomes
> -// irrelevant, as it should be possible to get range info for an SSA
> -// name at any point in the program.
> -//
> -// We have a few heuristics up our sleeve to determine if a call to
> -// alloca() is within bounds.  Try them out and return the type of
> -// alloca call with its assumed limit (if applicable).
> -//
> -// Given a known argument (ARG) to alloca() and an EDGE (E)
> -// calculating said argument, verify that the last statement in the BB
> -// in E->SRC is a gate comparing ARG to an acceptable bound for
> -// alloca().  See examples below.
> -//
> -// If set, ARG_CASTED is the possible unsigned argument to which ARG
> -// was casted to.  This is to handle cases where the controlling
> -// predicate is looking at a casted value, not the argument itself.
> -//    arg_casted = (size_t) arg;
> -//    if (arg_casted < N)
> -//      goto bb3;
> -//    else
> -//      goto bb5;
> -//
> -// MAX_SIZE is WARN_ALLOCA= adjusted for VLAs.  It is the maximum size
> -// in bytes we allow for arg.
> -
> -static class alloca_type_and_limit
> -alloca_call_type_by_arg (tree arg, tree arg_casted, edge e,
> -             unsigned HOST_WIDE_INT max_size)
> -{
> -  basic_block bb = e->src;
> -  gimple_stmt_iterator gsi = gsi_last_bb (bb);
> -  gimple *last = gsi_stmt (gsi);
> -
> -  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
> -
> -  /* When MAX_SIZE is greater than or equal to PTRDIFF_MAX treat
> -     allocations that aren't visibly constrained as OK, otherwise
> -     report them as (potentially) unbounded.  */
> -  alloca_type unbounded_result = (max_size < maxobjsize.to_uhwi ()
> -                  ? ALLOCA_UNBOUNDED : ALLOCA_OK);
> -
> -  if (!last || gimple_code (last) != GIMPLE_COND)
> -    {
> -      return alloca_type_and_limit (unbounded_result);
> -    }
> -
> -  enum tree_code cond_code = gimple_cond_code (last);
> -  if (e->flags & EDGE_TRUE_VALUE)
> -    ;
> -  else if (e->flags & EDGE_FALSE_VALUE)
> -    cond_code = invert_tree_comparison (cond_code, false);
> -  else
> -      return alloca_type_and_limit (unbounded_result);
> -
> -  // Check for:
> -  //   if (ARG .COND. N)
> -  //     goto <bb 3>;
> -  //   else
> -  //     goto <bb 4>;
> -  //   <bb 3>:
> -  //   alloca(ARG);
> -  if ((cond_code == LE_EXPR
> -       || cond_code == LT_EXPR
> -       || cond_code == GT_EXPR
> -       || cond_code == GE_EXPR)
> -      && (gimple_cond_lhs (last) == arg
> -      || gimple_cond_lhs (last) == arg_casted))
> -    {
> -      if (TREE_CODE (gimple_cond_rhs (last)) == INTEGER_CST)
> -    {
> -      tree rhs = gimple_cond_rhs (last);
> -      int tst = wi::cmpu (wi::to_widest (rhs), max_size);
> -      if ((cond_code == LT_EXPR && tst == -1)
> -          || (cond_code == LE_EXPR && (tst == -1 || tst == 0)))
> -        return alloca_type_and_limit (ALLOCA_OK);
> -      else
> -        {
> -          // Let's not get too specific as to how large the limit
> -          // may be.  Someone's clearly an idiot when things
> -          // degrade into "if (N > Y) alloca(N)".
> -          if (cond_code == GT_EXPR || cond_code == GE_EXPR)
> -        rhs = integer_zero_node;
> -          return alloca_type_and_limit (ALLOCA_BOUND_MAYBE_LARGE,
> -                        wi::to_wide (rhs));
> -        }
> -    }
> -      else
> -    {
> -      /* Analogous to ALLOCA_UNBOUNDED, when MAX_SIZE is greater
> -         than or equal to PTRDIFF_MAX, treat allocations with
> -         an unknown bound as OK.  */
> -      alloca_type unknown_result
> -        = (max_size < maxobjsize.to_uhwi ()
> -           ? ALLOCA_BOUND_UNKNOWN : ALLOCA_OK);
> -      return alloca_type_and_limit (unknown_result);
> -    }
> -    }
> -
> -  // Similarly, but check for a comparison with an unknown LIMIT.
> -  //   if (LIMIT .COND. ARG)
> -  //     alloca(arg);
> -  //
> -  //   Where LIMIT has a bound of unknown range.
> -  //
> -  // Note: All conditions of the form (ARG .COND. XXXX) where covered
> -  // by the previous check above, so we only need to look for (LIMIT
> -  // .COND. ARG) here.
> -  tree limit = gimple_cond_lhs (last);
> -  if ((gimple_cond_rhs (last) == arg
> -       || gimple_cond_rhs (last) == arg_casted)
> -      && TREE_CODE (limit) == SSA_NAME)
> -    {
> -      wide_int min, max;
> -      value_range_kind range_type = get_range_info (limit, &min, &max);
> -
> -      if (range_type == VR_UNDEFINED || range_type == VR_VARYING)
> -    return alloca_type_and_limit (ALLOCA_BOUND_UNKNOWN);
> -
> -      // ?? It looks like the above `if' is unnecessary, as we never
> -      // get any VR_RANGE or VR_ANTI_RANGE here.  If we had a range
> -      // for LIMIT, I suppose we would have taken care of it in
> -      // alloca_call_type(), or handled above where we handle (ARG 
> .COND. N).
> -      //
> -      // If this ever triggers, we should probably figure out why and
> -      // handle it, though it is likely to be just an ALLOCA_UNBOUNDED.
> -      return alloca_type_and_limit (unbounded_result);
> -    }
> -
> -  return alloca_type_and_limit (unbounded_result);
> -}
> -
> -// Return TRUE if SSA's definition is a cast from a signed type.
> -// If so, set *INVALID_CASTED_TYPE to the signed type.
> -
> -static bool
> -cast_from_signed_p (tree ssa, tree *invalid_casted_type)
> -{
> -  gimple *def = SSA_NAME_DEF_STMT (ssa);
> -  if (def
> -      && !gimple_nop_p (def)
> -      && gimple_assign_cast_p (def)
> -      && !TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (def))))
> -    {
> -      *invalid_casted_type = TREE_TYPE (gimple_assign_rhs1 (def));
> -      return true;
> -    }
> -  return false;
> -}
> -
> -// Return TRUE if X has a maximum range of MAX, basically covering the
> -// entire domain, in which case it's no range at all.
> -
> -static bool
> -is_max (tree x, wide_int max)
> -{
> -  return wi::max_value (TREE_TYPE (x)) == max;
> -}
> -
>   // Analyze the alloca call in STMT and return the alloca type with its
>   // corresponding limit (if applicable).  IS_VLA is set if the alloca
>   // call was created by the gimplifier for a VLA.
> -//
> -// If the alloca call may be too large because of a cast from a signed
> -// type to an unsigned type, set *INVALID_CASTED_TYPE to the
> -// problematic signed type.
> 
>   static class alloca_type_and_limit
> -alloca_call_type (gimple *stmt, bool is_vla, tree *invalid_casted_type)
> +alloca_call_type (range_query &query, gimple *stmt, bool is_vla)
>   {
>     gcc_assert (gimple_alloca_call_p (stmt));
> -  bool tentative_cast_from_signed = false;
>     tree len = gimple_call_arg (stmt, 0);
> -  tree len_casted = NULL;
> -  wide_int min, max;
> -  edge_iterator ei;
> -  edge e;
> 
>     gcc_assert (!is_vla || warn_vla_limit >= 0);
>     gcc_assert (is_vla || warn_alloca_limit >= 0);
> @@ -361,118 +197,9 @@ alloca_call_type (gimple *stmt, bool is_vla, tree 
> *invalid_casted_type)
>         return alloca_type_and_limit (ALLOCA_OK);
>       }
> 
> -  // Check the range info if available.
> -  if (TREE_CODE (len) == SSA_NAME)
> -    {
> -      value_range_kind range_type = get_range_info (len, &min, &max);
> -      if (range_type == VR_RANGE)
> -    {
> -      if (wi::leu_p (max, max_size))
> -        return alloca_type_and_limit (ALLOCA_OK);
> -      else
> -        {
> -          // A cast may have created a range we don't care
> -          // about.  For instance, a cast from 16-bit to
> -          // 32-bit creates a range of 0..65535, even if there
> -          // is not really a determinable range in the
> -          // underlying code.  In this case, look through the
> -          // cast at the original argument, and fall through
> -          // to look at other alternatives.
> -          //
> -          // We only look at through the cast when its from
> -          // unsigned to unsigned, otherwise we may risk
> -          // looking at SIGNED_INT < N, which is clearly not
> -          // what we want.  In this case, we'd be interested
> -          // in a VR_RANGE of [0..N].
> -          //
> -          // Note: None of this is perfect, and should all go
> -          // away with better range information.  But it gets
> -          // most of the cases.
> -          gimple *def = SSA_NAME_DEF_STMT (len);
> -          if (gimple_assign_cast_p (def))
> -        {
> -          tree rhs1 = gimple_assign_rhs1 (def);
> -          tree rhs1type = TREE_TYPE (rhs1);
> -
> -          // Bail if the argument type is not valid.
> -          if (!INTEGRAL_TYPE_P (rhs1type))
> -            return alloca_type_and_limit (ALLOCA_OK);
> -
> -          if (TYPE_UNSIGNED (rhs1type))
> -            {
> -              len_casted = rhs1;
> -              range_type = get_range_info (len_casted, &min, &max);
> -            }
> -        }
> -          // An unknown range or a range of the entire domain is
> -          // really no range at all.
> -          if (range_type == VR_VARYING
> -          || (!len_casted && is_max (len, max))
> -          || (len_casted && is_max (len_casted, max)))
> -        {
> -          // Fall through.
> -        }
> -          else if (range_type == VR_ANTI_RANGE)
> -        return alloca_type_and_limit (ALLOCA_UNBOUNDED);
> -
> -          if (range_type != VR_VARYING)
> -        {
> -          const offset_int maxobjsize
> -            = wi::to_offset (max_object_size ());
> -          alloca_type result = (max_size < maxobjsize
> -                    ? ALLOCA_BOUND_MAYBE_LARGE : ALLOCA_OK);
> -          return alloca_type_and_limit (result, max);
> -        }
> -        }
> -    }
> -      else if (range_type == VR_ANTI_RANGE)
> -    {
> -      // There may be some wrapping around going on.  Catch it
> -      // with this heuristic.  Hopefully, this VR_ANTI_RANGE
> -      // nonsense will go away, and we won't have to catch the
> -      // sign conversion problems with this crap.
> -      //
> -      // This is here to catch things like:
> -      // void foo(signed int n) {
> -      //   if (n < 100)
> -      //     alloca(n);
> -      //   ...
> -      // }
> -      if (cast_from_signed_p (len, invalid_casted_type))
> -        {
> -          // Unfortunately this also triggers:
> -          //
> -          // __SIZE_TYPE__ n = (__SIZE_TYPE__)blah;
> -          // if (n < 100)
> -          //   alloca(n);
> -          //
> -          // ...which is clearly bounded.  So, double check that
> -          // the paths leading up to the size definitely don't
> -          // have a bound.
> -          tentative_cast_from_signed = true;
> -        }
> -    }
> -      // No easily determined range and try other things.
> -    }
> -
> -  // If we couldn't find anything, try a few heuristics for things we
> -  // can easily determine.  Check these misc cases but only accept
> -  // them if all predecessors have a known bound.
> -  class alloca_type_and_limit ret = alloca_type_and_limit (ALLOCA_OK);
> -  FOR_EACH_EDGE (e, ei, gimple_bb (stmt)->preds)
> -    {
> -      gcc_assert (!len_casted || TYPE_UNSIGNED (TREE_TYPE (len_casted)));
> -      ret = alloca_call_type_by_arg (len, len_casted, e, max_size);
> -      if (ret.type != ALLOCA_OK)
> -    break;
> -    }
> -
> -  if (ret.type != ALLOCA_OK && tentative_cast_from_signed)
> -    ret = alloca_type_and_limit (ALLOCA_CAST_FROM_SIGNED);
> -
> +  struct alloca_type_and_limit ret = alloca_type_and_limit (ALLOCA_OK);
>     // If we have a declared maximum size, we can take it into account.
> -  if (ret.type != ALLOCA_OK
> -      && gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX))
> +  if (gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX))
>       {
>         tree arg = gimple_call_arg (stmt, 2);
>         if (compare_tree_int (arg, max_size) <= 0)
> @@ -485,9 +212,37 @@ alloca_call_type (gimple *stmt, bool is_vla, tree 
> *invalid_casted_type)
>                   ? ALLOCA_BOUND_MAYBE_LARGE : ALLOCA_OK);
>         ret = alloca_type_and_limit (result, wi::to_wide (arg));
>       }
> +      return ret;
> +    }
> +
> +  // If the user specified a limit, use it.
> +  int_range_max r;
> +  if (warn_limit_specified_p (is_vla)
> +      && TREE_CODE (len) == SSA_NAME
> +      && query.range_of_expr (r, len, stmt)
> +      && !r.varying_p ())
> +    {
> +      // The invalid bits are anything outside of [0, MAX_SIZE].
> +      static int_range<2> invalid_range (build_int_cst (size_type_node, 
> 0),
> +                     build_int_cst (size_type_node,
> +                            max_size),
> +                     VR_ANTI_RANGE);
> +
> +      r.intersect (invalid_range);
> +      if (r.undefined_p ())
> +    return alloca_type_and_limit (ALLOCA_OK);
> +
> +      return alloca_type_and_limit (ALLOCA_BOUND_MAYBE_LARGE,
> +                    wi::to_wide (integer_zero_node));
>       }
> 
> -  return ret;
> +  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
> +  /* When MAX_SIZE is greater than or equal to PTRDIFF_MAX treat
> +     allocations that aren't visibly constrained as OK, otherwise
> +     report them as (potentially) unbounded.  */
> +  alloca_type unbounded_result = (max_size < maxobjsize.to_uhwi ()
> +                  ? ALLOCA_UNBOUNDED : ALLOCA_OK);
> +  return alloca_type_and_limit (unbounded_result);
>   }
> 
>   // Return TRUE if STMT is in a loop, otherwise return FALSE.
> @@ -503,6 +258,7 @@ in_loop_p (gimple *stmt)
>   unsigned int
>   pass_walloca::execute (function *fun)
>   {
> +  gimple_ranger ranger;
>     basic_block bb;
>     FOR_EACH_BB_FN (bb, fun)
>       {
> @@ -535,9 +291,8 @@ pass_walloca::execute (function *fun)
>         else if (warn_alloca_limit < 0)
>           continue;
> 
> -      tree invalid_casted_type = NULL;
>         class alloca_type_and_limit t
> -        = alloca_call_type (stmt, is_vla, &invalid_casted_type);
> +        = alloca_call_type (ranger, stmt, is_vla);
> 
>         unsigned HOST_WIDE_INT adjusted_alloca_limit
>           = adjusted_warn_limit (false);
> @@ -599,13 +354,6 @@ pass_walloca::execute (function *fun)
>             }
>             }
>             break;
> -        case ALLOCA_BOUND_UNKNOWN:
> -          warning_at (loc, wcode,
> -              (is_vla
> -               ? G_("%Gvariable-length array bound is unknown")
> -               : G_("%G%<alloca%> bound is unknown")),
> -              stmt);
> -          break;
>           case ALLOCA_UNBOUNDED:
>             warning_at (loc, wcode,
>                 (is_vla
> @@ -618,17 +366,6 @@ pass_walloca::execute (function *fun)
>             warning_at (loc, wcode,
>                 "%Guse of %<alloca%> within a loop", stmt);
>             break;
> -        case ALLOCA_CAST_FROM_SIGNED:
> -          gcc_assert (invalid_casted_type != NULL_TREE);
> -          warning_at (loc, wcode,
> -              (is_vla
> -               ? G_("%Gargument to variable-length array "
> -                "may be too large due to "
> -                "conversion from %qT to %qT")
> -               : G_("%Gargument to %<alloca%> may be too large "
> -                "due to conversion from %qT to %qT")),
> -              stmt, invalid_casted_type, size_type_node);
> -          break;
>           case ALLOCA_ARG_IS_ZERO:
>             warning_at (loc, wcode,
>                 (is_vla
> diff --git a/gcc/testsuite/gcc.dg/Walloca-1.c 
> b/gcc/testsuite/gcc.dg/Walloca-1.c
> index 85e9160e845..ed1fa929398 100644
> --- a/gcc/testsuite/gcc.dg/Walloca-1.c
> +++ b/gcc/testsuite/gcc.dg/Walloca-1.c
> @@ -24,8 +24,7 @@ void foo1 (size_t len, size_t len2, size_t len3)
>     char *s = alloca (123);
>     useit (s);            // OK, constant argument to alloca
> 
> -  s = alloca (num);        // { dg-warning "large due to conversion" "" 
> { target lp64 } }
> -  // { dg-warning "unbounded use of 'alloca'" "" { target { ! lp64 } } 
> .-1 }
> +  s = alloca (num);        // { dg-warning "may be too large" }
>     useit (s);
> 
>     s = alloca (30000);        /* { dg-warning "is too large" } */
> diff --git a/gcc/testsuite/gcc.dg/Walloca-12.c 
> b/gcc/testsuite/gcc.dg/Walloca-12.c
> index 059c5f32129..d2d9413ab1e 100644
> --- a/gcc/testsuite/gcc.dg/Walloca-12.c
> +++ b/gcc/testsuite/gcc.dg/Walloca-12.c
> @@ -8,5 +8,5 @@ void g (unsigned int n)
>   {
>     if (n == 7)
>       n = 11;
> -  f (__builtin_alloca (n)); /* { dg-warning "unbounded use of 'alloca'" 
> } */
> +  f (__builtin_alloca (n)); /* { dg-warning "may be too large" } */
>   }
> diff --git a/gcc/testsuite/gcc.dg/Walloca-13.c 
> b/gcc/testsuite/gcc.dg/Walloca-13.c
> index 12e9f6c9281..99d62065e26 100644
> --- a/gcc/testsuite/gcc.dg/Walloca-13.c
> +++ b/gcc/testsuite/gcc.dg/Walloca-13.c
> @@ -8,5 +8,5 @@ void g (int *p, int *q)
>   {
>     __SIZE_TYPE__ n = (__SIZE_TYPE__)(p - q);
>     if (n < 100)
> -    f (__builtin_alloca (n)); // { dg-bogus "may be too large due to 
> conversion" "" { xfail { *-*-* } } }
> +    f (__builtin_alloca (n)); // { dg-bogus "may be too large" "" { 
> xfail { *-*-* } } }
>   }
> diff --git a/gcc/testsuite/gcc.dg/Walloca-2.c 
> b/gcc/testsuite/gcc.dg/Walloca-2.c
> index 766ff8d8af3..1cf9165c59f 100644
> --- a/gcc/testsuite/gcc.dg/Walloca-2.c
> +++ b/gcc/testsuite/gcc.dg/Walloca-2.c
> @@ -24,7 +24,7 @@ g2 (int n)
>   {
>     void *p;
>     if (n < 2000)
> -    p = __builtin_alloca (n); // { dg-warning "large due to conversion" }
> +    p = __builtin_alloca (n); // { dg-warning "may be too large" }
>     else
>       p = __builtin_malloc (n);
>     f (p);
> @@ -36,9 +36,7 @@ g3 (int n)
>     void *p;
>     if (n > 0 && n < 3000)
>       {
> -      p = __builtin_alloca (n); // { dg-warning "'alloca' may be too 
> large" "" { target lp64} }
> -      // { dg-message "note:.*argument may be as large as 2999" "note" 
> { target lp64 } .-1 }
> -      // { dg-warning "unbounded use of 'alloca'" "" { target { ! lp64 
> } } .-2 }
> +      p = __builtin_alloca (n); // { dg-warning "may be too large" }
>       }
>     else
>       p = __builtin_malloc (n);
> diff --git a/gcc/testsuite/gcc.dg/Walloca-3.c 
> b/gcc/testsuite/gcc.dg/Walloca-3.c
> index f5840673da0..b8000ff1249 100644
> --- a/gcc/testsuite/gcc.dg/Walloca-3.c
> +++ b/gcc/testsuite/gcc.dg/Walloca-3.c
> @@ -13,7 +13,7 @@ g1 (__SIZE_TYPE__ n)
>   {
>     void *p;
>     if (n < LIMIT)
> -    p = __builtin_alloca (n); // { dg-warning "'alloca' bound is 
> unknown" }
> +    p = __builtin_alloca (n); // { dg-warning "may be too large" }
>     else
>       p = __builtin_malloc (n);
>     f (p);
> @@ -27,7 +27,7 @@ g2 (unsigned short n)
>   {
>     void *p;
>     if (n < SHORT_LIMIT)
> -    p = __builtin_alloca (n); // { dg-warning "'alloca' bound is 
> unknown" }
> +    p = __builtin_alloca (n); // { dg-warning "may be too large" }
>     else
>       p = __builtin_malloc (n);
>     f (p);
> diff --git a/gcc/testsuite/gcc.dg/Walloca-6.c 
> b/gcc/testsuite/gcc.dg/Walloca-6.c
> index 16b5d6f729d..ebe08aec838 100644
> --- a/gcc/testsuite/gcc.dg/Walloca-6.c
> +++ b/gcc/testsuite/gcc.dg/Walloca-6.c
> @@ -1,7 +1,6 @@
>   /* { dg-do compile } */
>   /* { dg-require-effective-target alloca } */
>   /* { dg-options "-Walloca-larger-than=256 -O2" } */
> -/* { dg-xfail-if "Currently broken but Andrew's work should fix this" { 
> *-*-* } } */
> 
>   void f (void*);
>   void g (__SIZE_TYPE__ n)
>
Aldy Hernandez Oct. 6, 2020, 9:25 a.m. UTC | #4
On 10/5/20 7:12 PM, Martin Sebor wrote:

>> It the future, I would even like to remove the specific range the 
>> ranger was able to compute from the error message itself.  As will 
>> become obvious, the ranger can get pretty outrageous ranges that are 
>> entirely non-obvious by looking at the code.  Peppering the error 
>> messages with these ranges will ultimately just confuse the user.  But 
>> alas, that's a problem for another patch to solve.
> 
> I agree that when it comes to sizes where just one bound of the range
> is used to decide whether or not to warn (the lower bound in the case
> of most warnings but, as the example above shows, the upper bound for
> -Walloca-larger-than=), printing multiple subranges is unnecessary
> and could easily be confusing.  Even printing the very large bounds
> (in decimal) in the warning above may be too much.  At the same time,
> simply printing:
> 
>    warning: argument to ‘alloca’ may be too large
> 
> and nothing else wouldn't be helpful either.  (Though it would make
> the alloca pass real simple ;-)  It's a matter of deciding what
> the right amount of detail is in each instance and choosing the best
> representation for the values included in it (small values are okay
> in decimal, larger values may be better formatted in hex, or involving
> well-known symbolic constants like INT_MAX or PTRDIFF_MAX).  But
> different people have different ideas about how much detail is enough
> and what presentation is best.  Rather than each of us imposing our
> individual preference on all users and ending up with arbitrary
> inconsistencies between warnings we should design them so that their
> output can be customized.  If I like to see the full ranges in warnings
> in decimal I should be able to ask GCC to do that. If I just care about
> the high level details (say just the more important bound) and prefer
> hex for large numbers there should be a way to do that too.  It's just
> a matter of adding a %R or some such to the pretty-printer to format
> a range and exposing an option that will let users choose the format.
> (It still leaves room for us to argue about the defaults ;)

Sure, some general shared infrastructure for reporting errors involving 
ranges would be nice, inasmuch as the testcases do not test for specific 
ranges since those are liable to change from release to release.

Aldy
diff mbox series

Patch

diff --git a/gcc/gimple-ssa-warn-alloca.c b/gcc/gimple-ssa-warn-alloca.c
index 9e80e5dbbd9..33824a7a091 100644
--- a/gcc/gimple-ssa-warn-alloca.c
+++ b/gcc/gimple-ssa-warn-alloca.c
@@ -36,6 +36,7 @@  along with GCC; see the file COPYING3.  If not see
  #include "calls.h"
  #include "cfgloop.h"
  #include "intl.h"
+#include "gimple-range.h"

  static unsigned HOST_WIDE_INT adjusted_warn_limit (bool);

@@ -99,12 +100,6 @@  enum alloca_type {
    // Alloca argument may be too large.
    ALLOCA_BOUND_MAYBE_LARGE,

-  // Alloca argument is bounded but of an indeterminate size.
-  ALLOCA_BOUND_UNKNOWN,
-
-  // Alloca argument was casted from a signed integer.
-  ALLOCA_CAST_FROM_SIGNED,
-
    // Alloca appears in a loop.
    ALLOCA_IN_LOOP,

@@ -135,6 +130,15 @@  public:
    }
  };

+/* Return TRUE if the user specified a limit for either VLAs or 
ALLOCAs.  */
+
+static bool
+warn_limit_specified_p (bool is_vla)
+{
+  unsigned HOST_WIDE_INT max = is_vla ? warn_vla_limit : warn_alloca_limit;
+  return max != HOST_WIDE_INT_MAX;
+}
+
  /* Return the value of the argument N to -Walloca-larger-than= or
     -Wvla-larger-than= adjusted for the target data model so that
     when N == HOST_WIDE_INT_MAX, the adjusted value is set to
@@ -158,183 +162,15 @@  adjusted_warn_limit (bool idx)
    return limits[idx];
  }

-
-// NOTE: When we get better range info, this entire function becomes
-// irrelevant, as it should be possible to get range info for an SSA
-// name at any point in the program.
-//
-// We have a few heuristics up our sleeve to determine if a call to
-// alloca() is within bounds.  Try them out and return the type of
-// alloca call with its assumed limit (if applicable).
-//
-// Given a known argument (ARG) to alloca() and an EDGE (E)
-// calculating said argument, verify that the last statement in the BB
-// in E->SRC is a gate comparing ARG to an acceptable bound for
-// alloca().  See examples below.
-//
-// If set, ARG_CASTED is the possible unsigned argument to which ARG
-// was casted to.  This is to handle cases where the controlling
-// predicate is looking at a casted value, not the argument itself.
-//    arg_casted = (size_t) arg;
-//    if (arg_casted < N)
-//      goto bb3;
-//    else
-//      goto bb5;
-//
-// MAX_SIZE is WARN_ALLOCA= adjusted for VLAs.  It is the maximum size
-// in bytes we allow for arg.
-
-static class alloca_type_and_limit
-alloca_call_type_by_arg (tree arg, tree arg_casted, edge e,
-			 unsigned HOST_WIDE_INT max_size)
-{
-  basic_block bb = e->src;
-  gimple_stmt_iterator gsi = gsi_last_bb (bb);
-  gimple *last = gsi_stmt (gsi);
-
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
-
-  /* When MAX_SIZE is greater than or equal to PTRDIFF_MAX treat
-     allocations that aren't visibly constrained as OK, otherwise
-     report them as (potentially) unbounded.  */
-  alloca_type unbounded_result = (max_size < maxobjsize.to_uhwi ()
-				  ? ALLOCA_UNBOUNDED : ALLOCA_OK);
-
-  if (!last || gimple_code (last) != GIMPLE_COND)
-    {
-      return alloca_type_and_limit (unbounded_result);
-    }
-
-  enum tree_code cond_code = gimple_cond_code (last);
-  if (e->flags & EDGE_TRUE_VALUE)
-    ;
-  else if (e->flags & EDGE_FALSE_VALUE)
-    cond_code = invert_tree_comparison (cond_code, false);
-  else
-      return alloca_type_and_limit (unbounded_result);
-
-  // Check for:
-  //   if (ARG .COND. N)
-  //     goto <bb 3>;
-  //   else
-  //     goto <bb 4>;
-  //   <bb 3>:
-  //   alloca(ARG);
-  if ((cond_code == LE_EXPR
-       || cond_code == LT_EXPR
-       || cond_code == GT_EXPR
-       || cond_code == GE_EXPR)
-      && (gimple_cond_lhs (last) == arg
-	  || gimple_cond_lhs (last) == arg_casted))
-    {
-      if (TREE_CODE (gimple_cond_rhs (last)) == INTEGER_CST)
-	{
-	  tree rhs = gimple_cond_rhs (last);
-	  int tst = wi::cmpu (wi::to_widest (rhs), max_size);
-	  if ((cond_code == LT_EXPR && tst == -1)
-	      || (cond_code == LE_EXPR && (tst == -1 || tst == 0)))
-	    return alloca_type_and_limit (ALLOCA_OK);
-	  else
-	    {
-	      // Let's not get too specific as to how large the limit
-	      // may be.  Someone's clearly an idiot when things
-	      // degrade into "if (N > Y) alloca(N)".
-	      if (cond_code == GT_EXPR || cond_code == GE_EXPR)
-		rhs = integer_zero_node;
-	      return alloca_type_and_limit (ALLOCA_BOUND_MAYBE_LARGE,
-					    wi::to_wide (rhs));
-	    }
-	}
-      else
-	{
-	  /* Analogous to ALLOCA_UNBOUNDED, when MAX_SIZE is greater
-	     than or equal to PTRDIFF_MAX, treat allocations with
-	     an unknown bound as OK.  */
-	  alloca_type unknown_result
-	    = (max_size < maxobjsize.to_uhwi ()
-	       ? ALLOCA_BOUND_UNKNOWN : ALLOCA_OK);
-	  return alloca_type_and_limit (unknown_result);
-	}
-    }
-
-  // Similarly, but check for a comparison with an unknown LIMIT.
-  //   if (LIMIT .COND. ARG)
-  //     alloca(arg);
-  //
-  //   Where LIMIT has a bound of unknown range.
-  //
-  // Note: All conditions of the form (ARG .COND. XXXX) where covered
-  // by the previous check above, so we only need to look for (LIMIT
-  // .COND. ARG) here.
-  tree limit = gimple_cond_lhs (last);
-  if ((gimple_cond_rhs (last) == arg
-       || gimple_cond_rhs (last) == arg_casted)
-      && TREE_CODE (limit) == SSA_NAME)
-    {
-      wide_int min, max;
-      value_range_kind range_type = get_range_info (limit, &min, &max);
-
-      if (range_type == VR_UNDEFINED || range_type == VR_VARYING)
-	return alloca_type_and_limit (ALLOCA_BOUND_UNKNOWN);
-
-      // ?? It looks like the above `if' is unnecessary, as we never
-      // get any VR_RANGE or VR_ANTI_RANGE here.  If we had a range
-      // for LIMIT, I suppose we would have taken care of it in
-      // alloca_call_type(), or handled above where we handle (ARG 
.COND. N).
-      //
-      // If this ever triggers, we should probably figure out why and
-      // handle it, though it is likely to be just an ALLOCA_UNBOUNDED.
-      return alloca_type_and_limit (unbounded_result);
-    }
-
-  return alloca_type_and_limit (unbounded_result);
-}
-
-// Return TRUE if SSA's definition is a cast from a signed type.
-// If so, set *INVALID_CASTED_TYPE to the signed type.
-
-static bool
-cast_from_signed_p (tree ssa, tree *invalid_casted_type)
-{
-  gimple *def = SSA_NAME_DEF_STMT (ssa);
-  if (def
-      && !gimple_nop_p (def)
-      && gimple_assign_cast_p (def)
-      && !TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (def))))
-    {
-      *invalid_casted_type = TREE_TYPE (gimple_assign_rhs1 (def));
-      return true;
-    }
-  return false;
-}
-
-// Return TRUE if X has a maximum range of MAX, basically covering the
-// entire domain, in which case it's no range at all.
-
-static bool
-is_max (tree x, wide_int max)
-{
-  return wi::max_value (TREE_TYPE (x)) == max;
-}
-
  // Analyze the alloca call in STMT and return the alloca type with its
  // corresponding limit (if applicable).  IS_VLA is set if the alloca
  // call was created by the gimplifier for a VLA.
-//
-// If the alloca call may be too large because of a cast from a signed
-// type to an unsigned type, set *INVALID_CASTED_TYPE to the
-// problematic signed type.

  static class alloca_type_and_limit
-alloca_call_type (gimple *stmt, bool is_vla, tree *invalid_casted_type)
+alloca_call_type (range_query &query, gimple *stmt, bool is_vla)
  {
    gcc_assert (gimple_alloca_call_p (stmt));
-  bool tentative_cast_from_signed = false;
    tree len = gimple_call_arg (stmt, 0);
-  tree len_casted = NULL;
-  wide_int min, max;
-  edge_iterator ei;
-  edge e;

    gcc_assert (!is_vla || warn_vla_limit >= 0);
    gcc_assert (is_vla || warn_alloca_limit >= 0);
@@ -361,118 +197,9 @@  alloca_call_type (gimple *stmt, bool is_vla, tree 
*invalid_casted_type)
        return alloca_type_and_limit (ALLOCA_OK);
      }

-  // Check the range info if available.
-  if (TREE_CODE (len) == SSA_NAME)
-    {
-      value_range_kind range_type = get_range_info (len, &min, &max);
-      if (range_type == VR_RANGE)
-	{
-	  if (wi::leu_p (max, max_size))
-	    return alloca_type_and_limit (ALLOCA_OK);
-	  else
-	    {
-	      // A cast may have created a range we don't care
-	      // about.  For instance, a cast from 16-bit to
-	      // 32-bit creates a range of 0..65535, even if there
-	      // is not really a determinable range in the
-	      // underlying code.  In this case, look through the
-	      // cast at the original argument, and fall through
-	      // to look at other alternatives.
-	      //
-	      // We only look at through the cast when its from
-	      // unsigned to unsigned, otherwise we may risk
-	      // looking at SIGNED_INT < N, which is clearly not
-	      // what we want.  In this case, we'd be interested
-	      // in a VR_RANGE of [0..N].
-	      //
-	      // Note: None of this is perfect, and should all go
-	      // away with better range information.  But it gets
-	      // most of the cases.
-	      gimple *def = SSA_NAME_DEF_STMT (len);
-	      if (gimple_assign_cast_p (def))
-		{
-		  tree rhs1 = gimple_assign_rhs1 (def);
-		  tree rhs1type = TREE_TYPE (rhs1);
-
-		  // Bail if the argument type is not valid.
-		  if (!INTEGRAL_TYPE_P (rhs1type))
-		    return alloca_type_and_limit (ALLOCA_OK);
-
-		  if (TYPE_UNSIGNED (rhs1type))
-		    {
-		      len_casted = rhs1;
-		      range_type = get_range_info (len_casted, &min, &max);
-		    }
-		}
-	      // An unknown range or a range of the entire domain is
-	      // really no range at all.
-	      if (range_type == VR_VARYING
-		  || (!len_casted && is_max (len, max))
-		  || (len_casted && is_max (len_casted, max)))
-		{
-		  // Fall through.
-		}
-	      else if (range_type == VR_ANTI_RANGE)
-		return alloca_type_and_limit (ALLOCA_UNBOUNDED);
-
-	      if (range_type != VR_VARYING)
-		{
-		  const offset_int maxobjsize
-		    = wi::to_offset (max_object_size ());
-		  alloca_type result = (max_size < maxobjsize
-					? ALLOCA_BOUND_MAYBE_LARGE : ALLOCA_OK);
-		  return alloca_type_and_limit (result, max);
-		}
-	    }
-	}
-      else if (range_type == VR_ANTI_RANGE)
-	{
-	  // There may be some wrapping around going on.  Catch it
-	  // with this heuristic.  Hopefully, this VR_ANTI_RANGE
-	  // nonsense will go away, and we won't have to catch the
-	  // sign conversion problems with this crap.
-	  //
-	  // This is here to catch things like:
-	  // void foo(signed int n) {
-	  //   if (n < 100)
-	  //     alloca(n);
-	  //   ...
-	  // }
-	  if (cast_from_signed_p (len, invalid_casted_type))
-	    {
-	      // Unfortunately this also triggers:
-	      //
-	      // __SIZE_TYPE__ n = (__SIZE_TYPE__)blah;
-	      // if (n < 100)
-	      //   alloca(n);
-	      //
-	      // ...which is clearly bounded.  So, double check that
-	      // the paths leading up to the size definitely don't
-	      // have a bound.
-	      tentative_cast_from_signed = true;
-	    }
-	}
-      // No easily determined range and try other things.
-    }
-
-  // If we couldn't find anything, try a few heuristics for things we
-  // can easily determine.  Check these misc cases but only accept
-  // them if all predecessors have a known bound.
-  class alloca_type_and_limit ret = alloca_type_and_limit (ALLOCA_OK);
-  FOR_EACH_EDGE (e, ei, gimple_bb (stmt)->preds)
-    {
-      gcc_assert (!len_casted || TYPE_UNSIGNED (TREE_TYPE (len_casted)));
-      ret = alloca_call_type_by_arg (len, len_casted, e, max_size);
-      if (ret.type != ALLOCA_OK)
-	break;
-    }
-
-  if (ret.type != ALLOCA_OK && tentative_cast_from_signed)
-    ret = alloca_type_and_limit (ALLOCA_CAST_FROM_SIGNED);
-
+  struct alloca_type_and_limit ret = alloca_type_and_limit (ALLOCA_OK);
    // If we have a declared maximum size, we can take it into account.
-  if (ret.type != ALLOCA_OK
-      && gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX))
+  if (gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX))
      {
        tree arg = gimple_call_arg (stmt, 2);
        if (compare_tree_int (arg, max_size) <= 0)
@@ -485,9 +212,37 @@  alloca_call_type (gimple *stmt, bool is_vla, tree 
*invalid_casted_type)
  				? ALLOCA_BOUND_MAYBE_LARGE : ALLOCA_OK);
  	  ret = alloca_type_and_limit (result, wi::to_wide (arg));
  	}
+      return ret;
+    }
+
+  // If the user specified a limit, use it.
+  int_range_max r;
+  if (warn_limit_specified_p (is_vla)
+      && TREE_CODE (len) == SSA_NAME
+      && query.range_of_expr (r, len, stmt)
+      && !r.varying_p ())
+    {
+      // The invalid bits are anything outside of [0, MAX_SIZE].
+      static int_range<2> invalid_range (build_int_cst (size_type_node, 0),
+					 build_int_cst (size_type_node,
+							max_size),
+					 VR_ANTI_RANGE);
+
+      r.intersect (invalid_range);
+      if (r.undefined_p ())
+	return alloca_type_and_limit (ALLOCA_OK);
+
+      return alloca_type_and_limit (ALLOCA_BOUND_MAYBE_LARGE,
+				    wi::to_wide (integer_zero_node));
      }

-  return ret;
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  /* When MAX_SIZE is greater than or equal to PTRDIFF_MAX treat
+     allocations that aren't visibly constrained as OK, otherwise
+     report them as (potentially) unbounded.  */
+  alloca_type unbounded_result = (max_size < maxobjsize.to_uhwi ()
+				  ? ALLOCA_UNBOUNDED : ALLOCA_OK);
+  return alloca_type_and_limit (unbounded_result);
  }

  // Return TRUE if STMT is in a loop, otherwise return FALSE.
@@ -503,6 +258,7 @@  in_loop_p (gimple *stmt)
  unsigned int
  pass_walloca::execute (function *fun)
  {
+  gimple_ranger ranger;
    basic_block bb;
    FOR_EACH_BB_FN (bb, fun)
      {
@@ -535,9 +291,8 @@  pass_walloca::execute (function *fun)
  	  else if (warn_alloca_limit < 0)
  	    continue;

-	  tree invalid_casted_type = NULL;
  	  class alloca_type_and_limit t
-	    = alloca_call_type (stmt, is_vla, &invalid_casted_type);
+	    = alloca_call_type (ranger, stmt, is_vla);

  	  unsigned HOST_WIDE_INT adjusted_alloca_limit
  	    = adjusted_warn_limit (false);
@@ -599,13 +354,6 @@  pass_walloca::execute (function *fun)
  		  }
  	      }
  	      break;
-	    case ALLOCA_BOUND_UNKNOWN:
-	      warning_at (loc, wcode,
-			  (is_vla
-			   ? G_("%Gvariable-length array bound is unknown")
-			   : G_("%G%<alloca%> bound is unknown")),
-			  stmt);
-	      break;
  	    case ALLOCA_UNBOUNDED:
  	      warning_at (loc, wcode,
  			  (is_vla
@@ -618,17 +366,6 @@  pass_walloca::execute (function *fun)
  	      warning_at (loc, wcode,
  			  "%Guse of %<alloca%> within a loop", stmt);
  	      break;
-	    case ALLOCA_CAST_FROM_SIGNED:
-	      gcc_assert (invalid_casted_type != NULL_TREE);
-	      warning_at (loc, wcode,
-			  (is_vla
-			   ? G_("%Gargument to variable-length array "
-				"may be too large due to "
-				"conversion from %qT to %qT")
-			   : G_("%Gargument to %<alloca%> may be too large "
-				"due to conversion from %qT to %qT")),
-			  stmt, invalid_casted_type, size_type_node);
-	      break;
  	    case ALLOCA_ARG_IS_ZERO:
  	      warning_at (loc, wcode,
  			  (is_vla
diff --git a/gcc/testsuite/gcc.dg/Walloca-1.c 
b/gcc/testsuite/gcc.dg/Walloca-1.c
index 85e9160e845..ed1fa929398 100644
--- a/gcc/testsuite/gcc.dg/Walloca-1.c
+++ b/gcc/testsuite/gcc.dg/Walloca-1.c
@@ -24,8 +24,7 @@  void foo1 (size_t len, size_t len2, size_t len3)
    char *s = alloca (123);
    useit (s);			// OK, constant argument to alloca

-  s = alloca (num);		// { dg-warning "large due to conversion" "" { 
target lp64 } }
-  // { dg-warning "unbounded use of 'alloca'" "" { target { ! lp64 } } 
.-1 }
+  s = alloca (num);		// { dg-warning "may be too large" }
    useit (s);

    s = alloca (30000);		/* { dg-warning "is too large" } */
diff --git a/gcc/testsuite/gcc.dg/Walloca-12.c 
b/gcc/testsuite/gcc.dg/Walloca-12.c
index 059c5f32129..d2d9413ab1e 100644
--- a/gcc/testsuite/gcc.dg/Walloca-12.c
+++ b/gcc/testsuite/gcc.dg/Walloca-12.c
@@ -8,5 +8,5 @@  void g (unsigned int n)
  {
    if (n == 7)
      n = 11;
-  f (__builtin_alloca (n)); /* { dg-warning "unbounded use of 'alloca'" 
} */
+  f (__builtin_alloca (n)); /* { dg-warning "may be too large" } */
  }
diff --git a/gcc/testsuite/gcc.dg/Walloca-13.c 
b/gcc/testsuite/gcc.dg/Walloca-13.c
index 12e9f6c9281..99d62065e26 100644
--- a/gcc/testsuite/gcc.dg/Walloca-13.c
+++ b/gcc/testsuite/gcc.dg/Walloca-13.c
@@ -8,5 +8,5 @@  void g (int *p, int *q)
  {
    __SIZE_TYPE__ n = (__SIZE_TYPE__)(p - q);
    if (n < 100)
-    f (__builtin_alloca (n)); // { dg-bogus "may be too large due to 
conversion" "" { xfail { *-*-* } } }
+    f (__builtin_alloca (n)); // { dg-bogus "may be too large" "" { 
xfail { *-*-* } } }
  }
diff --git a/gcc/testsuite/gcc.dg/Walloca-2.c 
b/gcc/testsuite/gcc.dg/Walloca-2.c
index 766ff8d8af3..1cf9165c59f 100644
--- a/gcc/testsuite/gcc.dg/Walloca-2.c
+++ b/gcc/testsuite/gcc.dg/Walloca-2.c
@@ -24,7 +24,7 @@  g2 (int n)
  {
    void *p;
    if (n < 2000)
-    p = __builtin_alloca (n); // { dg-warning "large due to conversion" }
+    p = __builtin_alloca (n); // { dg-warning "may be too large" }
    else
      p = __builtin_malloc (n);
    f (p);
@@ -36,9 +36,7 @@  g3 (int n)
    void *p;
    if (n > 0 && n < 3000)
      {
-      p = __builtin_alloca (n); // { dg-warning "'alloca' may be too 
large" "" { target lp64} }
-      // { dg-message "note:.*argument may be as large as 2999" "note" 
{ target lp64 } .-1 }
-      // { dg-warning "unbounded use of 'alloca'" "" { target { ! lp64 
} } .-2 }
+      p = __builtin_alloca (n); // { dg-warning "may be too large" }
      }
    else
      p = __builtin_malloc (n);
diff --git a/gcc/testsuite/gcc.dg/Walloca-3.c 
b/gcc/testsuite/gcc.dg/Walloca-3.c
index f5840673da0..b8000ff1249 100644
--- a/gcc/testsuite/gcc.dg/Walloca-3.c
+++ b/gcc/testsuite/gcc.dg/Walloca-3.c
@@ -13,7 +13,7 @@  g1 (__SIZE_TYPE__ n)
  {
    void *p;
    if (n < LIMIT)
-    p = __builtin_alloca (n); // { dg-warning "'alloca' bound is unknown" }
+    p = __builtin_alloca (n); // { dg-warning "may be too large" }
    else
      p = __builtin_malloc (n);
    f (p);
@@ -27,7 +27,7 @@  g2 (unsigned short n)
  {
    void *p;
    if (n < SHORT_LIMIT)
-    p = __builtin_alloca (n); // { dg-warning "'alloca' bound is unknown" }
+    p = __builtin_alloca (n); // { dg-warning "may be too large" }
    else
      p = __builtin_malloc (n);
    f (p);
diff --git a/gcc/testsuite/gcc.dg/Walloca-6.c 
b/gcc/testsuite/gcc.dg/Walloca-6.c
index 16b5d6f729d..ebe08aec838 100644
--- a/gcc/testsuite/gcc.dg/Walloca-6.c
+++ b/gcc/testsuite/gcc.dg/Walloca-6.c
@@ -1,7 +1,6 @@ 
  /* { dg-do compile } */
  /* { dg-require-effective-target alloca } */
  /* { dg-options "-Walloca-larger-than=256 -O2" } */
-/* { dg-xfail-if "Currently broken but Andrew's work should fix this" { 
*-*-* } } */

  void f (void*);