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); +} + /* 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 } /* '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 + +/* 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) /* 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) +{ + 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)); + } + + 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)); + } + } + 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))); + 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; + } + + 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; + + 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; } } -- 2.34.1