https://gcc.gnu.org/bugzilla/show_bug.cgi?id=122184

Jakub Jelinek <jakub at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |ebotcazou at gcc dot gnu.org

--- Comment #4 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
Can be also -O -std=c++26 -fstack-check=generic

This is unfortunate, but am not really sure what can be done, it seems to me
that -fstack-check=generic is fundamentally incompatible with C++26, but
clearly with valid C and older C++ too.

If you compile
void
foo (int i)
{
  goto fail;
  char sha1_buf[i];
fail:;
}
with even older g++ in any -std= mode, it is rejected:
pr122184-2.C: In function ‘void foo(int)’:
pr122184-2.C:6:1: error: jump to label ‘fail’
    6 | fail:;
      | ^~~~
pr122184-2.C:4:8: note:   from here
    4 |   goto fail;
      |        ^~~~
pr122184-2.C:5:8: note:   crosses initialization of ‘char sha1_buf [i]’
    5 |   char sha1_buf[i];
      |        ^~~~~~~~
We don't consider a VLA declaration (in C++ a GNU extension) as something with
vacuous initialization, obviously it needs some code to actually initialize it
(allocate the stack).
But char sha1_buf[41]; has vacuous initialization, so C++ allows to bypass the
declaration of that with a jump, and so does C.

Consider
__attribute__((noipa)) void
bar (char *p)
{
  *p = 0;
}

int
foo ()
{
  goto fail;
  char sha1_buf[41];
fail:;
  bar (sha1_buf);
  return sha1_buf[0];
}

int
main ()
{
  if (foo () != 0)
    __builtin_abort ();
}
I think this is valid C and valid C++ (sure, noipa attribute and
__builtin_abort are GNU extensions).
This works fine without -fstack-check=generic, but crashes with it, because
-fstack-check=generic changes the vacuous
initialization of the variable (which can be validly jumped over) to a VLA
allocation (which can't, but nothing
checks that).
Note, no idea about the ICE.
The #c0 testcase is in gimple:
  char[41] * sha1_buf.0;
  void * saved_stack.1;
  char sha1_buf[41] [value-expr: *sha1_buf.0];

  saved_stack.1 = __builtin_stack_save ();
  try
    {
      // predicted unlikely by goto predictor.
      goto <D.2662>;
      sha1_buf.0 = __builtin_alloca_with_align (41, 8);
      (*sha1_buf.0) = .DEFERRED_INIT (41, 3, &"sha1_buf"[0]);
      goto <D.2663>;
      <D.2662>:
      (*sha1_buf.0) = .DEFERRED_INIT (41, 3, &"sha1_buf"[0]);
      <D.2663>:
      fail:
    }
  finally
    {
      __builtin_stack_restore (saved_stack.1);
    }
and in cfg
  char[41] * D.2666;
  char sha1_buf[41] [value-expr: *sha1_buf.0];
  void * saved_stack.1;
  char[41] * sha1_buf.0;

  <bb 2> :
  saved_stack.1 = __builtin_stack_save ();
  // predicted unlikely by goto predictor.
  (*sha1_buf.0) = .DEFERRED_INIT (41, 3, &"sha1_buf"[0]);
  __builtin_stack_restore (saved_stack.1);
  return;
so sure, clear use of uninitialized sha1_buf.0 (see above), but don't see why
the ssa pass can't turn that into sha1_buf.0_5(D) or something like that.
The above testcase has in cfg
  char[41] * D.2673;
  char sha1_buf[41] [value-expr: *sha1_buf.0];
  void * saved_stack.1;
  int D.2670;
  char[41] * sha1_buf.0;

  <bb 2> :
  saved_stack.1 = __builtin_stack_save ();
  // predicted unlikely by goto predictor.
  (*sha1_buf.0) = .DEFERRED_INIT (41, 3, &"sha1_buf"[0]);
  bar (sha1_buf.0);

  <bb 3> :
  _1 = (*sha1_buf.0)[0];
  D.2670 = (int) _1;
  __builtin_stack_restore (saved_stack.1);
  return D.2670;

  <bb 4> :
<L5>:
  __builtin_stack_restore (saved_stack.1);
  resx 1
but in that case ssa pass handles it as
  // predicted unlikely by goto predictor.
  (*sha1_buf.0_5(D)) = .DEFERRED_INIT (41, 3, &"sha1_buf"[0]);
  bar (sha1_buf.0_5(D));

  <bb 3> :
  _1 = (*sha1_buf.0_5(D))[0];

Reply via email to