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];