Evgeny Karpov <evgeny.kar...@microsoft.com> writes:
> From 69ce2026b10711b32595d58e23f92f54e6c718c2 Mon Sep 17 00:00:00 2001
> From: Evgeny Karpov <evgeny.kar...@microsoft.com>
> Date: Fri, 15 Nov 2024 13:14:18 +0100
> Subject: [PATCH v1 4/4] aarch64: Add SEH, stack unwinding and C++ exceptions
>
> This patch reuses the existing SEH, stack unwinding and C++ exceptions
> from ix86 and implements the required changes for AArch64.
>
> gcc/ChangeLog:
>
>       * common/config/aarch64/aarch64-common.cc (aarch64_except_unwind_info):
>       Add unwind info for AArch64.
>       (defined): Likewise.
>       (TARGET_EXCEPT_UNWIND_INFO): Likewise.
>       * config/aarch64/aarch64.cc (aarch64_print_reg):
>       Print a reg.
>       (defined): Enable unwind tables.
>       (aarch64_declare_function_name): Add unwinding.
>       * config/aarch64/cygming.h (SYMBOL_REF_STUBVAR_P):
>       Enable SEH, declare required functions and parameters.
>       (TARGET_SEH): Likewise.
>       (SEH_MAX_FRAME_SIZE): Likewise.
>       (TARGET_ASM_UNWIND_EMIT): Likewise.
>       (TARGET_ASM_UNWIND_EMIT_BEFORE_INSN): Likewise.
>       (TARGET_ASM_FUNCTION_END_PROLOGUE): Likewise.
>       (TARGET_ASM_EMIT_EXCEPT_PERSONALITY): Likewise.
>       (TARGET_ASM_INIT_SECTIONS): Likewise.
>       (SUBTARGET_ASM_UNWIND_INIT): Likewise.
>       (aarch64_pe_seh_unwind_emit): Likewise.
>       (aarch64_print_reg): Likewise.
>       (ASM_DECLARE_FUNCTION_SIZE): Likewise.
>       (ASM_DECLARE_COLD_FUNCTION_SIZE): Likewise.
>       (ASM_DECLARE_COLD_FUNCTION_NAME): Likewise.
>       * config/mingw/winnt.cc (defined):
>       Add AArch64 implmentation for SEH.
>       (CALLEE_SAVED_REG_NUMBER): Likewise.
>       (seh_parallel_offset): Likewise.
>       (seh_pattern_emit): Likewise.
>       (aarch64_pe_seh_unwind_emit): Likewise.
>
> libgcc/ChangeLog:
>
>       * config.host: Support AArch64.
>       * unwind-seh.c (defined): Likewise.
>       (_Unwind_Backtrace): Likewise.
> ---
>  gcc/common/config/aarch64/aarch64-common.cc |  30 +++
>  gcc/config/aarch64/aarch64.cc               |  15 ++
>  gcc/config/aarch64/cygming.h                |  51 +++-
>  gcc/config/mingw/winnt.cc                   | 260 +++++++++++++++++++-
>  libgcc/config.host                          |   2 +-
>  libgcc/unwind-seh.c                         |  37 ++-
>  6 files changed, 382 insertions(+), 13 deletions(-)
>
> diff --git a/gcc/common/config/aarch64/aarch64-common.cc 
> b/gcc/common/config/aarch64/aarch64-common.cc
> index 2bfc597e333..c46c0a8547d 100644
> --- a/gcc/common/config/aarch64/aarch64-common.cc
> +++ b/gcc/common/config/aarch64/aarch64-common.cc
> @@ -447,6 +447,36 @@ aarch64_rewrite_mcpu (int argc, const char **argv)
>    return aarch64_rewrite_selected_cpu (argv[argc - 1]);
>  }
>  
> +#if TARGET_AARCH64_MS_ABI
> +
> +/* Implement TARGET_EXCEPT_UNWIND_INFO.  */
> +
> +static enum unwind_info_type
> +aarch64_except_unwind_info (struct gcc_options *opts)
> +{
> +  /* Honor the --enable-sjlj-exceptions configure switch.  */
> +#ifdef CONFIG_SJLJ_EXCEPTIONS
> +  if (CONFIG_SJLJ_EXCEPTIONS)
> +    return UI_SJLJ;
> +#endif
> +
> +  /* On windows 64, prefer SEH exceptions over anything else.  */
> +#if defined (TARGET_AARCH64_MS_ABI)
> +  if (opts->x_flag_unwind_tables)
> +    return UI_SEH;
> +#endif
> +
> +  if (DWARF2_UNWIND_INFO)
> +    return UI_DWARF2;
> +
> +  return UI_SJLJ;
> +}
> +
> +#undef  TARGET_EXCEPT_UNWIND_INFO
> +#define TARGET_EXCEPT_UNWIND_INFO  aarch64_except_unwind_info
> +
> +#endif // TARGET_AARCH64_MS_ABI
> +
>  struct gcc_targetm_common targetm_common = TARGETM_COMMON_INITIALIZER;
>  
>  #undef AARCH64_CPU_NAME_LENGTH
> diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
> index f02f9c88b6e..6dd1ba8f085 100644
> --- a/gcc/config/aarch64/aarch64.cc
> +++ b/gcc/config/aarch64/aarch64.cc
> @@ -12773,6 +12773,12 @@ aarch64_label_mentioned_p (rtx x)
>    return 0;
>  }
>  
> +void
> +aarch64_print_reg (rtx x, int code, FILE *file)
> +{
> +  aarch64_print_operand (file, x, code);
> +}

Missing comment above function (to describe the parameters).  But since
aarch64_print_operand's parameter order is the standard one (for
TARGET_PRINT_OPERAND), could we instead adjust the callers of
aarch64_print_reg to use that order too, perhaps via

      targetm.asm_out.print_operand

?

> +
>  /* Implement REGNO_REG_CLASS.  */
>  
>  enum reg_class
> @@ -18468,6 +18474,12 @@ aarch64_override_options_after_change_1 (struct 
> gcc_options *opts)
>       intermediary step for the former.  */
>    if (flag_mlow_precision_sqrt)
>      flag_mrecip_low_precision_sqrt = true;
> +
> +  /* Enable unwind tables for MS.  */
> +#if defined (TARGET_AARCH64_MS_ABI)
> +  if (opts->x_flag_unwind_tables == 0)
> +    opts->x_flag_unwind_tables = 1;
> +#endif // TARGET_AARCH64_MS_ABI

The usual way to do this is via:

        SET_OPTION_IF_UNSET (opts, opts_set, flag_unwind_tables, 1);

(which is also what x86 seems to use).  This means that an explicit
-fno-unwind-tables still works.

Would that work here too, or were you deliberately trying to override
-fno-unwind-tables?

>  }
>  
>  /* 'Unpack' up the internal tuning structs and update the options
> @@ -24903,6 +24915,9 @@ aarch64_declare_function_name (FILE *stream, const 
> char* name,
>  
>    /* Don't forget the type directive for ELF.  */
>    ASM_OUTPUT_TYPE_DIRECTIVE (stream, name, "function");
> +#ifdef SUBTARGET_ASM_UNWIND_INIT
> +  SUBTARGET_ASM_UNWIND_INIT (stream);
> +#endif
>    ASM_OUTPUT_FUNCTION_LABEL (stream, name, fndecl);
>  
>    cfun->machine->label_is_assembled = true;
> diff --git a/gcc/config/aarch64/cygming.h b/gcc/config/aarch64/cygming.h
> index 8b9038ccf88..d4f4775ddab 100644
> --- a/gcc/config/aarch64/cygming.h
> +++ b/gcc/config/aarch64/cygming.h
> @@ -45,14 +45,39 @@ along with GCC; see the file COPYING3.  If not see
>  #define SYMBOL_REF_STUBVAR_P(X) \
>       ((SYMBOL_REF_FLAGS (X) & SYMBOL_FLAG_STUBVAR) != 0)
>  
> -/* Disable SEH and declare the required SEH-related macros that are
> +/* Declare the required SEH-related macros that are
>  still needed for compilation.  */
>  #undef TARGET_SEH
> -#define TARGET_SEH 0
> +#define TARGET_SEH 1
>  
>  #define SSE_REGNO_P(N) (gcc_unreachable (), 0)
>  #define GENERAL_REGNO_P(N) (gcc_unreachable (), 0)
> -#define SEH_MAX_FRAME_SIZE (gcc_unreachable (), 0)
> +
> +/* Support hooks for SEH.  */
> +#undef  TARGET_ASM_UNWIND_EMIT
> +#define TARGET_ASM_UNWIND_EMIT  aarch64_pe_seh_unwind_emit
> +#undef  TARGET_ASM_UNWIND_EMIT_BEFORE_INSN
> +#define TARGET_ASM_UNWIND_EMIT_BEFORE_INSN  false
> +#undef  TARGET_ASM_FUNCTION_END_PROLOGUE
> +#define TARGET_ASM_FUNCTION_END_PROLOGUE  mingw_pe_seh_end_prologue
> +#undef  TARGET_ASM_EMIT_EXCEPT_PERSONALITY
> +#define TARGET_ASM_EMIT_EXCEPT_PERSONALITY 
> mingw_pe_seh_emit_except_personality
> +#undef  TARGET_ASM_INIT_SECTIONS
> +#define TARGET_ASM_INIT_SECTIONS  mingw_pe_seh_init_sections
> +#undef  SUBTARGET_ASM_UNWIND_INIT
> +#define SUBTARGET_ASM_UNWIND_INIT  mingw_pe_seh_init

Could we move more of this into a common header, shared with x86?
Many of the definitions seem to be largely the same.

> +
> +/* According to Windows x64 software convention, the maximum stack 
> allocatable
> +   in the prologue is 4G - 8 bytes.  Furthermore, there is a limited set of
> +   instructions allowed to adjust the stack pointer in the epilog, forcing 
> the
> +   use of frame pointer for frames larger than 2 GB.  This theorical limit
> +   is reduced by 256, an over-estimated upper bound for the stack use by the
> +   prologue.
> +   We define only one threshold for both the prolog and the epilog.  When the
> +   frame size is larger than this threshold, we allocate the area to save SSE
> +   regs, then save them, and then allocate the remaining.  There is no SEH
> +   unwind info for this later allocation.  */
> +#define SEH_MAX_FRAME_SIZE ((2U << 30) - 256)
>  
>  #undef TARGET_PECOFF
>  #define TARGET_PECOFF 1
> @@ -70,6 +95,9 @@ still needed for compilation.  */
>  #define TARGET_ASM_UNIQUE_SECTION mingw_pe_unique_section
>  #define TARGET_ENCODE_SECTION_INFO  mingw_pe_encode_section_info
>  
> +extern void aarch64_pe_seh_unwind_emit (FILE *, rtx_insn *);
> +extern void aarch64_print_reg (rtx, int, FILE*);
> +
>  #define TARGET_VALID_DLLIMPORT_ATTRIBUTE_P 
> mingw_pe_valid_dllimport_attribute_p
>  
>  /* Output function declarations at the end of the file.  */
> @@ -118,6 +146,7 @@ still needed for compilation.  */
>        builtin_define ("__MSVCRT__");                                 \
>        builtin_define ("__MINGW32__");                                        
> \
>        builtin_define ("_WIN32");                                     \
> +      builtin_define ("__SEH__");                                    \
>        builtin_define_std ("WIN32");                                  \
>        builtin_define_std ("WINNT");                                  \
>        builtin_define_with_int_value ("_INTEGRAL_MAX_BITS",           \
> @@ -203,6 +232,14 @@ still needed for compilation.  */
>      flag_stack_check = STATIC_BUILTIN_STACK_CHECK;   \
>    } while (0)
>  
> +#undef ASM_DECLARE_FUNCTION_SIZE
> +#define ASM_DECLARE_FUNCTION_SIZE(FILE,NAME,DECL) \
> +  mingw_pe_end_function (FILE, NAME, DECL)
> +
> +#undef ASM_DECLARE_COLD_FUNCTION_SIZE
> +#define ASM_DECLARE_COLD_FUNCTION_SIZE(FILE,NAME,DECL) \
> +  mingw_pe_end_cold_function (FILE, NAME, DECL)
> +
>  #define SUBTARGET_ATTRIBUTE_TABLE \
>    { "selectany", 0, 0, true, false, false, false, \
>      mingw_handle_selectany_attribute, NULL }
> @@ -228,6 +265,14 @@ still needed for compilation.  */
>      aarch64_declare_function_name (STREAM, NAME, DECL);                      
> \
>    } while (0)
>  
> +#define ASM_DECLARE_COLD_FUNCTION_NAME(FILE, NAME, DECL)     \
> +  do                                                         \
> +    {                                                                \
> +      mingw_pe_declare_type (FILE, NAME, 0, 1);              \
> +      mingw_pe_seh_cold_init (FILE, NAME);                   \
> +      ASM_OUTPUT_LABEL (FILE, NAME);                         \
> +    }                                                                \
> +  while (0)

Same comment for these three definitions.

>  
>  /* Define this to be nonzero if static stack checking is supported.  */
>  #define STACK_CHECK_STATIC_BUILTIN 1
> diff --git a/gcc/config/mingw/winnt.cc b/gcc/config/mingw/winnt.cc
> index ca5da652b8b..6b8ebcf6297 100644
> --- a/gcc/config/mingw/winnt.cc
> +++ b/gcc/config/mingw/winnt.cc
> @@ -170,6 +170,8 @@ mingw_pe_valid_dllimport_attribute_p (const_tree decl)
>     return true;
>  }
>  
> +#if !defined (TARGET_AARCH64_MS_ABI)
> +
>  /* Return string which is the function name, identified by ID, modified
>     with a suffix consisting of an atsign (@) followed by the number of
>     bytes of arguments.  If ID is NULL use the DECL_NAME as base. If
> @@ -225,8 +227,6 @@ gen_stdcall_or_fastcall_suffix (tree decl, tree id, bool 
> fastcall)
>    return get_identifier (new_str);
>  }
>  
> -#if !defined (TARGET_AARCH64_MS_ABI)
> -
>  /* Maybe decorate and get a new identifier for the DECL of a stdcall or
>     fastcall function. The original identifier is supplied in ID. */
>  
> @@ -925,6 +925,11 @@ mingw_pe_seh_end_prologue (FILE *f)
>  void
>  mingw_pe_seh_cold_init (FILE *f, const char *name)
>  {
> +#if defined (TARGET_AARCH64_MS_ABI)
> +  mingw_pe_seh_init (f);
> +  return;
> +#endif
> +
>    struct seh_frame_state *seh;
>    HOST_WIDE_INT alloc_offset, offset;
>  
> @@ -1048,6 +1053,15 @@ seh_emit_save (FILE *f, struct seh_frame_state *seh,
>              rtx reg, HOST_WIDE_INT cfa_offset)
>  {
>    const unsigned int regno = REGNO (reg);
> +#if defined (TARGET_AARCH64_MS_ABI)
> +  fputs ((FP_REGNUM_P (regno) ? " \t.seh_save_freg\t"
> +      : GP_REGNUM_P (regno) ?  " \t.seh_save_reg\t"
> +      : (gcc_unreachable (), "")), f);
> +  aarch64_print_reg (reg, 0, f);
> +  fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC " \n", abs (cfa_offset));
> +  return;
> +#endif
> +
>    HOST_WIDE_INT offset;
>  
>    seh->reg_offset[regno] = cfa_offset;
> @@ -1072,8 +1086,12 @@ seh_emit_stackalloc (FILE *f, struct seh_frame_state 
> *seh,
>  {
>    /* We're only concerned with prologue stack allocations, which all
>       are subtractions from the stack pointer.  */
> +#if defined (TARGET_AARCH64_MS_ABI)
> +  offset = abs (offset);
> +#else
>    gcc_assert (offset < 0);
>    offset = -offset;
> +#endif
>  
>    if (seh->cfa_reg == stack_pointer_rtx)
>      seh->cfa_offset += offset;
> @@ -1329,6 +1347,244 @@ i386_pe_seh_unwind_emit (FILE *out_file, rtx_insn 
> *insn)
>    seh_frame_related_expr (out_file, seh, pat);
>  }
>  
> +#if defined (TARGET_AARCH64_MS_ABI)
> +#define CALLEE_SAVED_REG_NUMBER(r)           \
> +  (((r) >= R19_REGNUM && (r) <= R30_REGNUM)  \
> +   || ((r) >= V8_REGNUM && (r) <= V15_REGNUM))
> +#else
> +#define CALLEE_SAVED_REG_NUMBER(r) 0
> +#endif
> +
> +static HOST_WIDE_INT
> +seh_parallel_offset (rtx pat, HOST_WIDE_INT wanted_regnum)

There should be a comment above the function describing what it does
and what the parameters are.  Same for the others in the file.

> +{
> +  rtx dest, src;
> +  HOST_WIDE_INT result = 0;
> +
> +  if (GET_CODE (pat) == PARALLEL)
> +    {
> +      int i, n = XVECLEN (pat, 0);
> +
> +      for (i = 0; i < n; ++i)
> +     {
> +       rtx ele = XVECEXP (pat, 0, i);
> +
> +       if (GET_CODE (ele) != SET)
> +         continue;
> +
> +       dest = SET_DEST (ele);
> +       src = SET_SRC (ele);
> +
> +       if (GET_CODE (dest) == REG
> +           && REGNO (dest) == wanted_regnum
> +           && GET_CODE (src) == MEM
> +           && GET_CODE (XEXP (src, 0)) == PLUS
> +           && XEXP (XEXP (src, 0), 0) == stack_pointer_rtx)
> +       {
> +         result = INTVAL (XEXP (XEXP (src, 0), 1));
> +       }
> +
> +       if (GET_CODE (src) == REG
> +           && REGNO (src) == wanted_regnum
> +           && GET_CODE (dest) == MEM
> +           && GET_CODE (XEXP (dest, 0)) == PLUS
> +           && XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx)
> +       {
> +         result = INTVAL (XEXP (XEXP (dest, 0), 1));
> +       }
> +     }
> +    }
> +
> +  return result;
> +}
> +
> +static void
> +seh_pattern_emit (FILE *f, struct seh_frame_state *seh, rtx pat)
> +{
> +  rtx dest, src;
> +
> +   if (GET_CODE (pat) == PARALLEL)
> +    {
> +      int i, n = XVECLEN (pat, 0);
> +      HOST_WIDE_INT regno, min_regno = 32;
> +      int reg_count = 0;
> +      HOST_WIDE_INT increment = 0;
> +
> +      for (i = 0; i < n; ++i)
> +     {
> +       rtx ele = XVECEXP (pat, 0, i);
> +
> +       if (GET_CODE (ele) != SET)
> +         continue;
> +
> +       dest = SET_DEST (ele);
> +       src = SET_SRC (ele);
> +
> +       if (GET_CODE (dest) == REG
> +           && GET_CODE (src) == PLUS
> +           && XEXP (src, 0) == stack_pointer_rtx)
> +       {
> +         increment = INTVAL (XEXP (src, 1));

We can't assume without checking that XEXP (src, 1) is a CONST_INT.
E.g. it might be a CONST_POLY_INT for functions with SVE spills.
It's OK to punt on that for now, but I think it at least needs
a sorry() diagnostic.  As things stand, we'd ICE for development
builds and generate silent wrong code for release builds.

Same comment elsewhere in the file.  Perhaps we could add a helper that
checks whether something is (plus (sp) (const_int N)) and if so returns
the const_int (otherwise it returns null).  It could then issue the
sorry() diagnostic for SVE offsets.

> +       }
> +
> +       if (!seh->after_prologue && GET_CODE (src) == REG)
> +       {
> +         regno = REGNO (src);
> +
> +         if (CALLEE_SAVED_REG_NUMBER (regno))
> +           {
> +             reg_count += 1;
> +             min_regno = MIN (regno, min_regno);
> +           }
> +       }
> +
> +       if (seh->after_prologue && GET_CODE (dest) == REG)
> +         {
> +           regno = REGNO (dest);
> +
> +           if (CALLEE_SAVED_REG_NUMBER (regno))
> +           {
> +             reg_count += 1;
> +             min_regno = MIN (regno, min_regno);
> +           }
> +         }
> +     }
> +
> +      if (reg_count == 2)
> +      {
> +     fprintf (f, "\t.seh_save_%s     x%ld, %ld\n",
> +       increment != 0 ? "regp_x" : "regp",
> +       min_regno,
> +       increment != 0 ? abs (increment) :
> +                    seh_parallel_offset (pat, min_regno));
> +      }

There seems to be an implicit assumption here that the two registers
are consecutive, but that isn't necessarily the case.  E.g.:

void foo()
{
  asm volatile ("" ::: "x19", "x22");
}

generates:

        .cfi_startproc
        stp     x19, x22, [sp, -16]!
        .cfi_def_cfa_offset 16
        .cfi_offset 19, -16
        .cfi_offset 22, -8
        ldp     x19, x22, [sp], 16
        .cfi_restore 22
        .cfi_restore 19
        .cfi_def_cfa_offset 0
        ret

> +    }
> +  else
> +    {
> +      src = SET_SRC (pat);
> +
> +      if (GET_CODE (pat) == SET)
> +      {
> +     HOST_WIDE_INT increment = 0;
> +     dest = SET_DEST (pat);
> +
> +     switch (GET_CODE (dest))
> +       {
> +       case REG:
> +         switch (GET_CODE (src))
> +           {
> +           case REG:
> +             if (dest == hard_frame_pointer_rtx
> +                 && src == stack_pointer_rtx)
> +               fputs ("\t.seh_set_fp\n", f);
> +             else if (CALLEE_SAVED_REG_NUMBER (REGNO (dest))
> +                      && src == stack_pointer_rtx)
> +               seh_emit_save (f, seh, dest, INTVAL (XEXP (src, 1)));
> +             break;
> +
> +           case PLUS:
> +             increment = INTVAL (XEXP (src, 1));
> +             src = XEXP (src, 0);
> +             if (dest == stack_pointer_rtx)
> +               seh_emit_stackalloc (f, seh, increment);
> +             break;
> +
> +           case MEM:
> +             src = XEXP (src, 0);
> +             if (GET_CODE (src) == PLUS
> +                 && GET_CODE (XEXP (src, 0)) == REG
> +                 && CALLEE_SAVED_REG_NUMBER (REGNO (dest))
> +                 && XEXP (src, 0) == stack_pointer_rtx)
> +               seh_emit_save (f, seh, dest, INTVAL (XEXP (src, 1)));
> +             break;
> +
> +           default:
> +             break;
> +           }
> +         break;
> +
> +       case MEM: // Save
> +         dest = XEXP (dest, 0);
> +         if (GET_CODE (dest) == PRE_DEC
> +             && CALLEE_SAVED_REG_NUMBER (REGNO (src))
> +             && XEXP (dest, 0) == stack_pointer_rtx)
> +           seh_emit_save (f, seh, src, INTVAL (XEXP (dest, 1)));

PRE_DEC doesn't have a second operand.  But wouldn't this call
to seh_emit_save lose the pre-decrement anyway?

I think the prologue save code would normally use PRE_MODIFY rather
than PRE_DEC.

> +         else if (GET_CODE (dest) == PLUS
> +                  && CALLEE_SAVED_REG_NUMBER (REGNO (src))
> +                  && XEXP (dest, 0) == stack_pointer_rtx)
> +           seh_emit_save (f, seh, src, INTVAL (XEXP (dest, 1)));
> +         break;
> +
> +       default:
> +         break;
> +       }
> +      }
> +      else if (seh->after_prologue
> +            && (GET_CODE (pat) == RETURN || GET_CODE (pat) == JUMP_INSN))
> +     fputs ("\t.seh_endepilogue\n", f);
> +    }
> +}
> +
> +/* This function looks at a single insn and emits any SEH directives
> +   required for unwind of this insn.  */
> +
> +void
> +aarch64_pe_seh_unwind_emit (FILE *out_file, rtx_insn *insn)
> +{
> +  rtx note, pat;
> +  struct seh_frame_state *seh;
> +
> +  if (!TARGET_SEH)
> +    return;
> +
> +  if (NOTE_P (insn))
> +    return;
> +
> +  seh = cfun->machine->seh;
> +
> +  if (!seh || seh->after_prologue)
> +    return;
> +
> +  pat = PATTERN (insn);
> +
> +  if (GET_CODE (pat) == SET)
> +    {
> +       rtx dest = SET_DEST (pat);
> +       if (GET_CODE (dest) == MEM && GET_CODE (XEXP (dest, 0)) == SCRATCH)
> +      return;
> +    }

It looks like this hook is called for all instructions, but I would
expect only RTX_FRAME_RELATED_P instructions to be relevant.  As it stands,
it looks like we could pick up stores of registers that aren't relevant
to EH.

For example, not every store of x19 to sp+16 is a prologue save and
not every load of x19 from sp+16 is an epilogue restore.

> +
> +  bool related_exp_needed = true;
> +
> +  for (note = REG_NOTES (insn); note ; note = XEXP (note, 1))
> +    {
> +      switch (REG_NOTE_KIND (note))
> +     {
> +     case REG_FRAME_RELATED_EXPR:
> +       pat = XEXP (note, 0);
> +       seh_pattern_emit (out_file, seh, pat);
> +       related_exp_needed = false;
> +       break;
> +
> +     case REG_CFA_EXPRESSION:
> +     case REG_CFA_REGISTER:
> +     case REG_CFA_ADJUST_CFA:
> +     case REG_CFA_OFFSET:
> +       related_exp_needed = false;
> +       break;

Could you explain the handling of these notes?  E.g. the code seems to
have the effect of ignoring instructions that have REG_CFA_OFFSET notes,
but I would have expected those to matter in a similar way to
REG_FRAME_RELATED_EXPR.

Does the code support shrink-wrapping, or does it require all EH-relevant
stores to happen in the prologue?  If the latter, we should disable
shrink-wrapping of the relevant components.

Thanks,
Richard

> +
> +     default:
> +       break;
> +     }
> +    }
> +
> +  if (related_exp_needed)
> +    {
> +      pat = PATTERN (insn);
> +      seh_pattern_emit (out_file, seh, pat);
> +    }
> +}
> +
>  void
>  mingw_pe_seh_emit_except_personality (rtx personality)
>  {
> diff --git a/libgcc/config.host b/libgcc/config.host
> index 35f882d94e1..112e3d7ff82 100644
> --- a/libgcc/config.host
> +++ b/libgcc/config.host
> @@ -476,7 +476,7 @@ aarch64-*-mingw*)
>           tmake_thr_file="mingw/t-mingw-pthread"
>           ;;
>       esac
> -     tmake_file="${tmake_file} ${cpu_type}/t-no-eh ${tmake_thr_file}"
> +     tmake_file="${tmake_file} ${tmake_thr_file} mingw/t-seh-eh"
>       tmake_file="${tmake_file} t-dfprules"
>       tmake_file="${tmake_file} ${cpu_type}/t-aarch64"
>       tmake_file="${tmake_file} ${cpu_type}/t-mingw"
> diff --git a/libgcc/unwind-seh.c b/libgcc/unwind-seh.c
> index f1b8f5a8519..219d20ad108 100644
> --- a/libgcc/unwind-seh.c
> +++ b/libgcc/unwind-seh.c
> @@ -32,7 +32,7 @@
>  
>  /* At the moment everything is written for x64, but in theory this could
>     also be used for i386, arm, mips and other extant embedded Windows.  */
> -#ifndef __x86_64__
> +#if !defined (__x86_64__) && !defined (__aarch64__)
>  #error "Unsupported architecture."
>  #endif
>  
> @@ -209,7 +209,12 @@ _GCC_specific_handler (PEXCEPTION_RECORD ms_exc, void 
> *this_frame,
>           "installed" the target_ip and RAX value via the arguments
>           to RtlUnwindEx.  All that's left is to set the RDX value
>           and "continue" to have the context installed.  */
> +#ifdef __x86_64__
>        ms_disp->ContextRecord->Rdx = ms_exc->ExceptionInformation[3];
> +#elif defined (__aarch64__)
> +      ms_disp->ContextRecord->X1 = ms_exc->ExceptionInformation[3];
> +#endif
> +
>        return ExceptionContinueSearch;
>      }
>  
> @@ -229,7 +234,11 @@ _GCC_specific_handler (PEXCEPTION_RECORD ms_exc, void 
> *this_frame,
>        return ExceptionContinueSearch;
>      }
>  
> +#ifdef __x86_64__
>    gcc_context.cfa = ms_disp->ContextRecord->Rsp;
> +#elif defined (__aarch64__)
> +    gcc_context.cfa = ms_disp->ContextRecord->Sp;
> +#endif
>    gcc_context.ra = ms_disp->ControlPc;
>    gcc_context.reg[0] = 0xdeadbeef;   /* These are write-only.  */
>    gcc_context.reg[1] = 0xdeadbeef;
> @@ -438,6 +447,8 @@ _Unwind_Backtrace(_Unwind_Trace_Fn trace,
>    CONTEXT ms_context;
>    struct _Unwind_Context gcc_context;
>    DISPATCHER_CONTEXT disp_context;
> +  ULONG64 ip;
> +  ULONG64 sp;
>  
>    memset (&ms_history, 0, sizeof(ms_history));
>    memset (&gcc_context, 0, sizeof(gcc_context));
> @@ -452,31 +463,43 @@ _Unwind_Backtrace(_Unwind_Trace_Fn trace,
>  
>    while (1)
>      {
> -      gcc_context.disp->ControlPc = ms_context.Rip;
> +#ifdef __x86_64__
> +      ip = ms_context.Rip;
> +#elif defined (__aarch64__)
> +      ip = ms_context.Pc;
> +#endif
> +      gcc_context.disp->ControlPc = ip;
>        gcc_context.disp->FunctionEntry
> -     = RtlLookupFunctionEntry (ms_context.Rip, &gcc_context.disp->ImageBase,
> +     = RtlLookupFunctionEntry (ip, &gcc_context.disp->ImageBase,
>                                 &ms_history);
>  
>        if (!gcc_context.disp->FunctionEntry)
>       return _URC_END_OF_STACK;
>  
>        gcc_context.disp->LanguageHandler
> -     = RtlVirtualUnwind (0, gcc_context.disp->ImageBase, ms_context.Rip,
> +     = RtlVirtualUnwind (0, gcc_context.disp->ImageBase, ip,
>                           gcc_context.disp->FunctionEntry, &ms_context,
>                           &gcc_context.disp->HandlerData,
>                           &gcc_context.disp->EstablisherFrame, NULL);
>  
>        /* Set values that the callback can inspect via _Unwind_GetIP
>         * and _Unwind_GetCFA. */
> -      gcc_context.ra = ms_context.Rip;
> -      gcc_context.cfa = ms_context.Rsp;
> +#ifdef __x86_64__
> +      ip = ms_context.Rip;
> +      sp = ms_context.Rsp;
> +#elif defined (__aarch64__)
> +      ip = ms_context.Pc;
> +      sp = ms_context.Sp;
> +#endif
> +      gcc_context.ra = ip;
> +      gcc_context.cfa = sp;
>  
>        /* Call trace function.  */
>        if (trace (&gcc_context, trace_argument) != _URC_NO_REASON)
>       return _URC_FATAL_PHASE1_ERROR;
>  
>        /* ??? Check for invalid stack pointer.  */
> -      if (ms_context.Rip == 0)
> +      if (ip == 0)
>       return _URC_END_OF_STACK;
>      }
>  }

Reply via email to