On Tue, Feb 27, 2024 at 9:42 AM Jakub Jelinek <ja...@redhat.com> wrote:
>
> Hi!
>
> As mentioned in the PR, on x86_64 currently a lot of ICEs end up
> with crashes in the unwinder like:
> during RTL pass: expand
> pr114044-2.c: In function ‘foo’:
> pr114044-2.c:5:3: internal compiler error: in expand_fn_using_insn, at 
> internal-fn.cc:208
>     5 |   __builtin_clzg (a);
>       |   ^~~~~~~~~~~~~~~~~~
> 0x7d9246 expand_fn_using_insn
>         ../../gcc/internal-fn.cc:208
>
> pr114044-2.c:5:3: internal compiler error: Segmentation fault
> 0x1554262 crash_signal
>         ../../gcc/toplev.cc:319
> 0x2b20320 x86_64_fallback_frame_state
>         ./md-unwind-support.h:63
> 0x2b20320 uw_frame_state_for
>         ../../../libgcc/unwind-dw2.c:1013
> 0x2b2165d _Unwind_Backtrace
>         ../../../libgcc/unwind.inc:303
> 0x2acbd69 backtrace_full
>         ../../libbacktrace/backtrace.c:127
> 0x2a32fa6 diagnostic_context::action_after_output(diagnostic_t)
>         ../../gcc/diagnostic.cc:781
> 0x2a331bb diagnostic_action_after_output(diagnostic_context*, diagnostic_t)
>         ../../gcc/diagnostic.h:1002
> 0x2a331bb diagnostic_context::report_diagnostic(diagnostic_info*)
>         ../../gcc/diagnostic.cc:1633
> 0x2a33543 diagnostic_impl
>         ../../gcc/diagnostic.cc:1767
> 0x2a33c26 internal_error(char const*, ...)
>         ../../gcc/diagnostic.cc:2225
> 0xe232c8 fancy_abort(char const*, int, char const*)
>         ../../gcc/diagnostic.cc:2336
> 0x7d9246 expand_fn_using_insn
>         ../../gcc/internal-fn.cc:208
> Segmentation fault (core dumped)
>
> The problem are the PR38534 r14-8470 changes which avoid saving call-saved
> registers in noreturn functions.  If such functions ever touch the
> bp register but because of the r14-8470 changes don't save it in the
> prologue, the caller or any other function in the backtrace uses a frame
> pointer and the noreturn function or anything it calls directly or
> indirectly calls backtrace, then the unwinder crashes, because bp register
> contains some unrelated value, but in the frames which do use frame pointer
> CFA is based on the bp register.
>
> In theory this could happen with any other call-saved register, e.g. code
> written by hand in assembly with .cfi_* directives could use any other
> call-saved register as register into which store the CFA or something
> related to that, but in reality at least compiler generated code and usual
> assembly probably just making sure bp doesn't contain garbage could be
> enough for backtrace purposes.  In the debugger of course it will not be
> enough, the values of the arguments etc. can be lost (if DW_CFA_undefined
> is emitted) or garbage.
>
> So, I think for noreturn function we should at least save the bp register
> if we use it.  If user asks for it using no_callee_saved_registers
> attribute, let's honor what is asked for (but then it is up to the user
> to make sure e.g. backtrace isn't called from the function or anything it
> calls).  As discussed in the PR, whether to save bp or not shouldn't be
> based on whether compiling with -g or -g0, because we don't want code
> generation changes without/with debugging, it would also break
> -fcompare-debug, and users can call backtrace(3), that doesn't use debug
> info, just unwind info, even backtrace_symbols{,_fd}(3) don't use debug info
> but just looks at dynamic symbol table.
>
> The patch also adds check for no_caller_saved_registers
> attribute in the implicit addition of not saving callee saved register
> in noreturn functions, because on I think
> __attribute__((no_caller_saved_registers, noreturn)) will otherwise
> error that no_caller_saved_registers and no_callee_saved_registers
> attributes are incompatible (but user didn't specify anything like that).
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

Just to add we've in the past opted to avoid tail-calling abort () and friends
exactly to help debugging.  So treating noreturn functions specially with
respect to caller saves looks inconsistent in this regard - it makes debugging
harder since a lot of info in the calling frame is going to be lost?

I hope we at least avoid that at -O0, possibly also with -Og?

> 2024-02-27  Jakub Jelinek  <ja...@redhat.com>
>
>         PR target/114116
>         * config/i386/i386.h (enum call_saved_registers_type): Add
>         TYPE_NO_CALLEE_SAVED_REGISTERS_EXCEPT_BP enumerator.
>         * config/i386/i386-options.cc (ix86_set_func_type): Remove
>         has_no_callee_saved_registers variable, add no_callee_saved_registers
>         instead, initialize it depending on whether it is
>         no_callee_saved_registers function or not.  Don't set it if
>         no_caller_saved_registers attribute is present.  Adjust users.
>         * config/i386/i386.cc (ix86_function_ok_for_sibcall): Handle
>         TYPE_NO_CALLEE_SAVED_REGISTERS_EXCEPT_BP like
>         TYPE_NO_CALLEE_SAVED_REGISTERS.
>         (ix86_save_reg): Handle TYPE_NO_CALLEE_SAVED_REGISTERS_EXCEPT_BP.
>
>         * gcc.target/i386/pr38534-1.c: Allow push/pop of bp.
>         * gcc.target/i386/pr38534-4.c: Likewise.
>         * gcc.target/i386/pr38534-2.c: Likewise.
>         * gcc.target/i386/pr38534-3.c: Likewise.
>         * gcc.target/i386/pr114097-1.c: Likewise.
>         * gcc.target/i386/stack-check-17.c: Expect no pop on ! ia32.
>
> --- gcc/config/i386/i386.h.jj   2024-01-27 22:59:53.121319813 +0100
> +++ gcc/config/i386/i386.h      2024-02-26 16:33:14.644745362 +0100
> @@ -2730,9 +2730,12 @@ enum call_saved_registers_type
>    /* The current function is a function specified with the "interrupt"
>       or "no_caller_saved_registers" attribute.  */
>    TYPE_NO_CALLER_SAVED_REGISTERS,
> +  /* The current function is a function specified with the
> +     "no_callee_saved_registers" attribute.  */
> +  TYPE_NO_CALLEE_SAVED_REGISTERS,
>    /* The current function is a function specified with the "noreturn"
> -     or "no_callee_saved_registers" attribute.  */
> -  TYPE_NO_CALLEE_SAVED_REGISTERS
> +     attribute.  */
> +  TYPE_NO_CALLEE_SAVED_REGISTERS_EXCEPT_BP,
>  };
>
>  enum queued_insn_type
> --- gcc/config/i386/i386-options.cc.jj  2024-02-26 16:29:07.471201524 +0100
> +++ gcc/config/i386/i386-options.cc     2024-02-26 16:42:01.730358609 +0100
> @@ -3384,7 +3384,9 @@ ix86_set_func_type (tree fndecl)
>  {
>    /* No need to save and restore callee-saved registers for a noreturn
>       function with nothrow or compiled with -fno-exceptions unless when
> -     compiling with -O0 or -Og.
> +     compiling with -O0 or -Og.  So that backtrace works for those at least
> +     in most cases, save the bp register if it is used, because it often
> +     is used in callers to compute CFA.
>
>       NB: Can't use just TREE_THIS_VOLATILE to check if this is a noreturn
>       function.  The local-pure-const pass turns an interrupt function
> @@ -3394,15 +3396,20 @@ ix86_set_func_type (tree fndecl)
>       function is marked with TREE_THIS_VOLATILE in the IR output, which
>       leads to the incompatible attribute error in LTO1.  Ignore the
>       interrupt function in this case.  */
> -  bool has_no_callee_saved_registers
> -    = ((TREE_THIS_VOLATILE (fndecl)
> -       && !lookup_attribute ("interrupt",
> -                             TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))
> -       && optimize
> -       && !optimize_debug
> -       && (TREE_NOTHROW (fndecl) || !flag_exceptions))
> -       || lookup_attribute ("no_callee_saved_registers",
> -                           TYPE_ATTRIBUTES (TREE_TYPE (fndecl))));
> +  enum call_saved_registers_type no_callee_saved_registers
> +    = TYPE_DEFAULT_CALL_SAVED_REGISTERS;
> +  if (lookup_attribute ("no_callee_saved_registers",
> +                       TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
> +    no_callee_saved_registers = TYPE_NO_CALLEE_SAVED_REGISTERS;
> +  else if (TREE_THIS_VOLATILE (fndecl)
> +          && optimize
> +          && !optimize_debug
> +          && (TREE_NOTHROW (fndecl) || !flag_exceptions)
> +          && !lookup_attribute ("interrupt",
> +                                TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))
> +          && !lookup_attribute ("no_caller_saved_registers",
> +                                TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
> +    no_callee_saved_registers = TYPE_NO_CALLEE_SAVED_REGISTERS_EXCEPT_BP;
>
>    if (cfun->machine->func_type == TYPE_UNKNOWN)
>      {
> @@ -3413,7 +3420,7 @@ ix86_set_func_type (tree fndecl)
>             error_at (DECL_SOURCE_LOCATION (fndecl),
>                       "interrupt and naked attributes are not compatible");
>
> -         if (has_no_callee_saved_registers)
> +         if (no_callee_saved_registers)
>             error_at (DECL_SOURCE_LOCATION (fndecl),
>                       "%qs and %qs attributes are not compatible",
>                       "interrupt", "no_callee_saved_registers");
> @@ -3442,7 +3449,7 @@ ix86_set_func_type (tree fndecl)
>                                 TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
>             cfun->machine->call_saved_registers
>               = TYPE_NO_CALLER_SAVED_REGISTERS;
> -         if (has_no_callee_saved_registers)
> +         if (no_callee_saved_registers)
>             {
>               if (cfun->machine->call_saved_registers
>                   == TYPE_NO_CALLER_SAVED_REGISTERS)
> @@ -3451,7 +3458,7 @@ ix86_set_func_type (tree fndecl)
>                           "no_caller_saved_registers",
>                           "no_callee_saved_registers");
>               cfun->machine->call_saved_registers
> -               = TYPE_NO_CALLEE_SAVED_REGISTERS;
> +               = no_callee_saved_registers;
>             }
>         }
>      }
> --- gcc/config/i386/i386.cc.jj  2024-02-26 11:12:36.275453513 +0100
> +++ gcc/config/i386/i386.cc     2024-02-26 17:24:50.701602352 +0100
> @@ -984,8 +984,9 @@ ix86_function_ok_for_sibcall (tree decl,
>
>    /* Sibling call isn't OK if callee has no callee-saved registers
>       and the calling function has callee-saved registers.  */
> -  if ((cfun->machine->call_saved_registers
> -       != TYPE_NO_CALLEE_SAVED_REGISTERS)
> +  if (cfun->machine->call_saved_registers != TYPE_NO_CALLEE_SAVED_REGISTERS
> +      && (cfun->machine->call_saved_registers
> +         != TYPE_NO_CALLEE_SAVED_REGISTERS_EXCEPT_BP)
>        && lookup_attribute ("no_callee_saved_registers",
>                            TYPE_ATTRIBUTES (type)))
>      return false;
> @@ -6649,6 +6650,11 @@ ix86_save_reg (unsigned int regno, bool
>
>      case TYPE_NO_CALLEE_SAVED_REGISTERS:
>        return false;
> +
> +    case TYPE_NO_CALLEE_SAVED_REGISTERS_EXCEPT_BP:
> +      if (regno != HARD_FRAME_POINTER_REGNUM)
> +       return false;
> +      break;
>      }
>
>    if (regno == REAL_PIC_OFFSET_TABLE_REGNUM
> --- gcc/testsuite/gcc.target/i386/pr38534-1.c.jj        2024-02-01 
> 16:00:37.443735635 +0100
> +++ gcc/testsuite/gcc.target/i386/pr38534-1.c   2024-02-27 08:59:08.421878166 
> +0100
> @@ -22,5 +22,5 @@ no_return_to_caller (void)
>    while (1);
>  }
>
> -/* { dg-final { scan-assembler-not "push" } } */
> -/* { dg-final { scan-assembler-not "pop" } } */
> +/* { dg-final { scan-assembler-not 
> "push\[^\n\r\]*(?:\[abcd\]x|\[sd\]i|sp|r\[0-9\]|\[xyz\]mm)" } } */
> +/* { dg-final { scan-assembler-not 
> "pop\[^\n\r\]*(?:\[abcd\]x|\[sd\]i|sp|r\[0-9\]|\[xyz\]mm)" } } */
> --- gcc/testsuite/gcc.target/i386/pr38534-4.c.jj        2024-02-01 
> 16:00:37.443735635 +0100
> +++ gcc/testsuite/gcc.target/i386/pr38534-4.c   2024-02-27 09:00:09.869021464 
> +0100
> @@ -12,7 +12,7 @@ foo (fn_t bar)
>    fn ();
>  }
>
> -/* { dg-final { scan-assembler-not "push" } } */
> -/* { dg-final { scan-assembler-not "pop" } } */
> +/* { dg-final { scan-assembler-not 
> "push\[^\n\r\]*(?:\[abcd\]x|\[sd\]i|sp|r\[0-9\]|\[xyz\]mm)" } } */
> +/* { dg-final { scan-assembler-not 
> "pop\[^\n\r\]*(?:\[abcd\]x|\[sd\]i|sp|r\[0-9\]|\[xyz\]mm)" } } */
>  /* { dg-final { scan-assembler-not "jmp" } } */
>  /* { dg-final { scan-assembler "call\[\\t \]+" } } */
> --- gcc/testsuite/gcc.target/i386/pr38534-2.c.jj        2024-02-01 
> 16:00:37.443735635 +0100
> +++ gcc/testsuite/gcc.target/i386/pr38534-2.c   2024-02-27 08:59:35.723497526 
> +0100
> @@ -12,7 +12,7 @@ foo (void)
>    fn ();
>  }
>
> -/* { dg-final { scan-assembler-not "push" } } */
> -/* { dg-final { scan-assembler-not "pop" } } */
> +/* { dg-final { scan-assembler-not 
> "push\[^\n\r\]*(?:\[abcd\]x|\[sd\]i|sp|r\[0-9\]|\[xyz\]mm)" } } */
> +/* { dg-final { scan-assembler-not 
> "pop\[^\n\r\]*(?:\[abcd\]x|\[sd\]i|sp|r\[0-9\]|\[xyz\]mm)" } } */
>  /* { dg-final { scan-assembler-not "jmp\[\\t \]+_?bar" } } */
>  /* { dg-final { scan-assembler "call\[\\t \]+_?bar" } } */
> --- gcc/testsuite/gcc.target/i386/pr38534-3.c.jj        2024-02-01 
> 16:00:37.443735635 +0100
> +++ gcc/testsuite/gcc.target/i386/pr38534-3.c   2024-02-27 08:59:49.669303090 
> +0100
> @@ -13,7 +13,7 @@ foo (void)
>    fn ();
>  }
>
> -/* { dg-final { scan-assembler-not "push" } } */
> -/* { dg-final { scan-assembler-not "pop" } } */
> +/* { dg-final { scan-assembler-not 
> "push\[^\n\r\]*(?:\[abcd\]x|\[sd\]i|sp|r\[0-9\]|\[xyz\]mm)" } } */
> +/* { dg-final { scan-assembler-not 
> "pop\[^\n\r\]*(?:\[abcd\]x|\[sd\]i|sp|r\[0-9\]|\[xyz\]mm)" } } */
>  /* { dg-final { scan-assembler-not "jmp" } } */
>  /* { dg-final { scan-assembler "call\[\\t \]+" } } */
> --- gcc/testsuite/gcc.target/i386/pr114097-1.c.jj       2024-02-26 
> 16:29:07.529200714 +0100
> +++ gcc/testsuite/gcc.target/i386/pr114097-1.c  2024-02-27 09:01:37.876794453 
> +0100
> @@ -22,5 +22,5 @@ no_return_to_caller (void)
>    while (1);
>  }
>
> -/* { dg-final { scan-assembler-not "push" } } */
> -/* { dg-final { scan-assembler-not "pop" } } */
> +/* { dg-final { scan-assembler-not 
> "push\[^\n\r\]*(?:\[abcd\]x|\[sd\]i|sp|r\[0-9\]|\[xyz\]mm)" } } */
> +/* { dg-final { scan-assembler-not 
> "pop\[^\n\r\]*(?:\[abcd\]x|\[sd\]i|sp|r\[0-9\]|\[xyz\]mm)" } } */
> --- gcc/testsuite/gcc.target/i386/stack-check-17.c.jj   2024-01-27 
> 22:59:53.186318915 +0100
> +++ gcc/testsuite/gcc.target/i386/stack-check-17.c      2024-02-27 
> 09:06:10.805989259 +0100
> @@ -32,5 +32,5 @@ f3 (void)
>     register on ia32 for a noreturn function.  */
>  /* { dg-final { scan-assembler-times "push\[ql\]" 1 { target { ! ia32 } } } 
> }  */
>  /* { dg-final { scan-assembler-times "push\[ql\]" 3 { target ia32 } } }  */
> -/* { dg-final { scan-assembler-times "pop" 1 } } */
> -
> +/* { dg-final { scan-assembler-not "pop" { target { ! ia32 } } } } */
> +/* { dg-final { scan-assembler-times "pop" 1 { target ia32 } } } */
>
>         Jakub
>

Reply via email to