@@ -842,6 +842,29 @@ is_host_cpu_not_armv8_base (int argc, const char **argv)
return "";
}
+#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
+
+ /* Prefer SEH exceptions over anything else. */
+ if (opts->x_flag_unwind_tables)
+ return UI_SEH;
+
+ if (DWARF2_UNWIND_INFO)
+ return UI_DWARF2;
+
+ return UI_SJLJ;
+}
+#endif
+
struct gcc_targetm_common targetm_common = TARGETM_COMMON_INITIALIZER;
#undef AARCH64_CPU_NAME_LENGTH
@@ -28,6 +28,8 @@ extern tree aarch64_ms_variadic_abi_fn_abi_va_list (tree fndecl);
extern tree aarch64_ms_variadic_abi_canonical_va_list_type (tree type);
+extern void aarch64_pe_seh_unwind_emit (FILE *, rtx_insn *);
+
extern int aarch64_arg_partial_bytes (cumulative_args_t,
const function_arg_info &);
@@ -32,6 +32,7 @@
#include "regs.h"
#include "function-abi.h"
#include "builtins.h"
+#include "diagnostic-core.h"
#include "aarch64-abi-ms-protos.h"
/* Iterate through the target-specific builtin types for va_list.
@@ -104,3 +105,312 @@ aarch64_arg_partial_bytes (cumulative_args_t pcum_v,
return 0;
}
+
+/* Emit SEH directive to adjust the stack pointer by offset. */
+
+static void
+aarch64_seh_cfa_adjust_cfa (FILE *f, struct seh_frame_state *seh, rtx pat)
+{
+ rtx src = SET_SRC (pat);
+ if (GET_CODE (src) != PLUS)
+ gcc_unreachable ();
+
+ const HOST_WIDE_INT reg_offset = INTVAL (XEXP (src, 1));
+ src = XEXP (src, 0);
+
+ gcc_assert (src == stack_pointer_rtx);
+ gcc_assert (seh->cfa_reg == stack_pointer_rtx);
+
+ rtx dest = SET_DEST (pat);
+ if (REGNO (dest) != STACK_POINTER_REGNUM)
+ gcc_unreachable ();
+
+ mingw_pe_seh_emit_stackalloc (f, seh, reg_offset);
+}
+
+/* Emit SEH directive to save a REG. */
+
+static void
+aarch64_seh_emit_save (FILE *f, rtx reg, HOST_WIDE_INT cfa_offset)
+{
+ const unsigned int regno = REGNO (reg);
+ fputs ((FP_REGNUM_P (regno) ? " \t.seh_save_freg\t"
+ : GP_REGNUM_P (regno) ? " \t.seh_save_reg\t"
+ : (gcc_unreachable (), "")), f);
+ if (REGNO (reg) >= V0_REGNUM)
+ fprintf (f, "d%d", REGNO (reg) - V0_REGNUM);
+ else
+ fprintf (f, "x%d", REGNO (reg));
+ fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC " \n", abs (cfa_offset));
+ return;
+}
+
+/* Emit SEH directive to save REG or REG pair. */
+
+static void
+aarch64_seh_emit_save_pair (FILE *f, HOST_WIDE_INT regno[2],
+ HOST_WIDE_INT offset)
+{
+ const char *unwind_code_prefix = ".seh_save_";
+ const char *unwind_code_infix = "";
+ bool negative_offset = offset < 0;
+ const char *unwind_code_suffix = negative_offset ? "_x": "";
+ offset = abs (offset);
+
+ if (regno[0] == FP_REGNUM && regno[1] == LR_REGNUM)
+ {
+ unwind_code_infix = "fplr";
+ fprintf (f, "\t%s%s%s\t%ld\n",
+ unwind_code_prefix,
+ unwind_code_infix,
+ unwind_code_suffix,
+ offset);
+ return;
+ }
+
+ const unsigned save_r19r20_x_max_offset = 248;
+ if (regno[0] == R19_REGNUM && regno[1] == R20_REGNUM && negative_offset
+ && offset <= save_r19r20_x_max_offset)
+ {
+ unwind_code_infix = "r19r20";
+ fprintf (f, "\t%s%s%s\t%ld\n",
+ unwind_code_prefix,
+ unwind_code_infix,
+ unwind_code_suffix,
+ offset);
+ return;
+ }
+
+ bool callee_save_reg1 = CALLEE_SAVED_REG_NUMBER (regno[0]);
+ bool callee_save_reg2 = CALLEE_SAVED_REG_NUMBER (regno[1]);
+
+ if (!callee_save_reg1 && !callee_save_reg2)
+ return;
+
+ bool emit_single_register = false;
+ unsigned reg_count = 1;
+ if (callee_save_reg1 && callee_save_reg2)
+ {
+ emit_single_register = (regno[1] - regno[0]) != 1;
+ reg_count += emit_single_register ? 1: 0;
+ }
+ else
+ {
+ emit_single_register = true;
+ if (!callee_save_reg1)
+ regno[0] = regno[1];
+ }
+
+ unwind_code_infix = emit_single_register ? "reg": "regp";
+ char reg_prefix = 'x';
+ unsigned regno_offset = 0;
+ HOST_WIDE_INT offset_increment = 8;
+
+ if (FP_REGNUM_P (regno[0]))
+ {
+ unwind_code_prefix = ".seh_save_f";
+ reg_prefix = 'd';
+ regno_offset = V0_REGNUM;
+ }
+
+ for (unsigned i = 0; i < reg_count; ++i)
+ fprintf (f, "\t%s%s%s\t%c%ld, %ld\n",
+ unwind_code_prefix,
+ unwind_code_infix,
+ unwind_code_suffix,
+ reg_prefix,
+ regno[i] - regno_offset,
+ offset + i * offset_increment);
+}
+
+/* Emit SEH directive if the pattern is recognized. */
+
+static void
+aarch64_seh_pattern_emit (FILE *f, struct seh_frame_state *seh, const rtx pat)
+{
+ rtx dest, src;
+
+ if (GET_CODE (pat) == PARALLEL)
+ {
+ HOST_WIDE_INT regno[2];
+ HOST_WIDE_INT offsets[2];
+ unsigned reg_count = 0;
+
+ for (unsigned i = 0, n = XVECLEN (pat, 0); i < n; ++i)
+ {
+ rtx ele = XVECEXP (pat, 0, i);
+
+ if (GET_CODE (ele) != SET)
+ continue;
+
+ src = SET_SRC (ele);
+ dest = SET_DEST (ele);
+ if (GET_CODE (src) != REG || GET_CODE (dest) != MEM)
+ continue;
+
+ dest = XEXP (dest, 0);
+ if (dest == stack_pointer_rtx)
+ offsets[reg_count] = 0;
+ else if (GET_CODE (dest) == PLUS
+ && XEXP (dest, 0) == stack_pointer_rtx)
+ {
+ if (GET_CODE (XEXP (dest, 1)) != CONST_INT)
+ {
+ sorry ("unexpected offset type");
+ return;
+ }
+ offsets[reg_count] = INTVAL (XEXP (dest, 1));
+ }
+ else
+ continue;
+
+
+ if (reg_count == 2)
+ gcc_unreachable ();
+ regno[reg_count] = REGNO (src);
+
+ if (CALLEE_SAVED_REG_NUMBER (regno[reg_count]))
+ ++reg_count;
+ }
+
+ if (reg_count == 1)
+ gcc_unreachable ();
+
+ if (reg_count == 2)
+ {
+ if (offsets[0] > offsets[1])
+ {
+ std::swap (offsets[0], offsets[1]);
+ std::swap (regno[0], regno[1]);
+ }
+ if (offsets[1]- offsets[0] != UNITS_PER_WORD)
+ gcc_unreachable ();
+
+ aarch64_seh_emit_save_pair (f, regno, offsets[0]);
+ }
+
+ return;
+ }
+
+ if (GET_CODE (pat) != SET)
+ return;
+
+ src = SET_SRC (pat);
+ 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)
+ fprintf (f, "\t.seh_set_fp\n");
+ break;
+
+ case PLUS:
+ if (dest == stack_pointer_rtx)
+ {
+ if (GET_CODE (XEXP (src, 1)) != CONST_INT)
+ {
+ sorry ("unexpected offset type");
+ return;
+ }
+ const HOST_WIDE_INT offset = abs (INTVAL (XEXP (src, 1)));
+ mingw_pe_seh_emit_stackalloc (f, seh, -offset);
+ }
+ break;
+
+ case MEM:
+ src = XEXP (src, 0);
+ if (GET_CODE (src) == PLUS
+ && XEXP (src, 0) == stack_pointer_rtx
+ && CALLEE_SAVED_REG_NUMBER (REGNO (dest)))
+ {
+ if (GET_CODE (XEXP (src, 1)) != CONST_INT)
+ {
+ sorry ("unexpected offset type");
+ return;
+ }
+ aarch64_seh_emit_save (f, dest, INTVAL (XEXP (src, 1)));
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case MEM:
+ dest = XEXP (dest, 0);
+
+ if (GET_CODE (dest) == PLUS && XEXP (dest, 0) == stack_pointer_rtx
+ && GET_CODE (src) == REG && CALLEE_SAVED_REG_NUMBER (REGNO (src)))
+ {
+ if (GET_CODE (XEXP (dest, 1)) != CONST_INT)
+ {
+ sorry ("unexpected offset type");
+ return;
+ }
+
+ aarch64_seh_emit_save (f, src, INTVAL (XEXP (dest, 1)));
+ }
+ else if (GET_CODE (dest) == PLUS
+ && XEXP (dest, 0) == hard_frame_pointer_rtx
+ && GET_CODE (src) == UNSPEC
+ && XINT (src, 1) == UNSPEC_STP)
+ {
+ if (GET_CODE (XEXP (dest, 1)) != CONST_INT)
+ {
+ sorry ("unexpected offset type");
+ return;
+ }
+
+ HOST_WIDE_INT offset = INTVAL (XEXP (dest, 1));
+ rtvec vec = XVEC (src, 0);
+ HOST_WIDE_INT regno[2] = { REGNO (RTVEC_ELT (vec, 0)),
+ REGNO (RTVEC_ELT (vec, 1))};
+ aarch64_seh_emit_save_pair (f, regno, offset);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* This function looks at a single insn and emits any SEH directives
+ required to unwind of this insn. */
+
+void
+aarch64_pe_seh_unwind_emit (FILE *out_file, rtx_insn *insn)
+{
+ if (!TARGET_SEH || NOTE_P (insn) || !RTX_FRAME_RELATED_P (insn))
+ return;
+
+ struct seh_frame_state *seh = cfun->machine->seh;
+ if (!seh || seh->after_prologue)
+ return;
+
+ rtx pat = PATTERN (insn);
+ for (rtx note = REG_NOTES (insn); note ; note = XEXP (note, 1))
+ {
+ switch (REG_NOTE_KIND (note))
+ {
+ case REG_FRAME_RELATED_EXPR:
+ pat = XEXP (note, 0);
+ break;
+
+ case REG_CFA_ADJUST_CFA:
+ if (XEXP (note, 0))
+ pat = XEXP (note, 0);
+ if (GET_CODE (pat) == PARALLEL)
+ pat = XVECEXP (pat, 0, 0);
+ aarch64_seh_cfa_adjust_cfa (out_file, seh, pat);
+ return;
+ default:
+ break;
+ }
+ }
+
+ aarch64_seh_pattern_emit (out_file, seh, pat);
+}
@@ -31,6 +31,10 @@ along with GCC; see the file COPYING3. If not see
#undef STATIC_CHAIN_REGNUM
#define STATIC_CHAIN_REGNUM R17_REGNUM
+#define CALLEE_SAVED_REG_NUMBER(r) \
+ (((r) >= R19_REGNUM && (r) <= R30_REGNUM) \
+ || ((r) >= V8_REGNUM && (r) <= V15_REGNUM))
+
#define ASM_COMMENT_START "//"
/* ASM_OUTPUT_TYPE_DIRECTIVE is not yet supported by binutils for the
@@ -124,6 +124,11 @@
#define TARGET_AARCH64_MS_ABI 0
#endif
+/* Not on SEH unless explicitly set. */
+#ifndef TARGET_SEH
+#define TARGET_SEH 0
+#endif
+
/* Flags that describe how a function shares certain architectural state
with its callers.
@@ -9915,6 +9920,9 @@ offset_12bit_unsigned_scaled_p (machine_mode mode, poly_int64 offset)
static sbitmap
aarch64_get_separate_components (void)
{
+ if (TARGET_SEH)
+ return NULL;
+
aarch64_frame &frame = cfun->machine->frame;
sbitmap components = sbitmap_alloc (LAST_SAVED_REGNUM + 1);
bitmap_clear (components);
@@ -14036,7 +14044,7 @@ aarch64_frame_pointer_required ()
{
/* If the function needs to record the incoming value of PSTATE.SM,
make sure that the slot is accessible from the frame pointer. */
- return aarch64_need_old_pstate_sm ();
+ return TARGET_SEH || aarch64_need_old_pstate_sm ();
}
static bool
@@ -20127,6 +20135,10 @@ aarch64_override_options_internal (struct gcc_options *opts,
aarch64_autovec_preference,
opts->x_autovec_preference);
+ /* Enable unwind tables for MS ABI. */
+ if (TARGET_AARCH64_MS_ABI)
+ SET_OPTION_IF_UNSET (opts, &global_options_set, flag_unwind_tables, 1);
+
aarch64_override_options_after_change_1 (opts);
}
@@ -26766,6 +26778,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;
@@ -45,14 +45,29 @@ 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
-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 TARGET_EXCEPT_UNWIND_INFO
+#define TARGET_EXCEPT_UNWIND_INFO aarch64_except_unwind_info
+#undef SUBTARGET_ASM_UNWIND_INIT
+#define SUBTARGET_ASM_UNWIND_INIT mingw_pe_seh_init
+
+#define SEH_MAX_FRAME_SIZE (1 << 28)
#undef TARGET_PECOFF
#define TARGET_PECOFF 1
@@ -118,6 +133,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 +219,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 }, \
@@ -231,6 +255,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_init (FILE); \
+ ASM_OUTPUT_LABEL (FILE, NAME); \
+ } \
+ while (0)
/* Define this to be nonzero if static stack checking is supported. */
#define STACK_CHECK_STATIC_BUILTIN 1
@@ -30,9 +30,9 @@
#if defined (__SEH__) && !defined (__USING_SJLJ_EXCEPTIONS__)
-/* 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__
+/* At the moment everything is written for x64 and aarch64, but in theory this
+ could also be used for i386, arm, mips and other extant embedded Windows. */
+#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;
}
}
@@ -23,6 +23,7 @@
// <http://www.gnu.org/licenses/>.
#include <bits/c++config.h>
+#include <cstdint>
#include <cstdlib>
#include <bits/exception_defines.h>
#include <cxxabi.h>
@@ -54,6 +55,21 @@ parse_lsda_header (_Unwind_Context *context, const unsigned char *p,
info->Start = (context ? _Unwind_GetRegionStart (context) : 0);
+#if defined (__SEH__) && defined (__aarch64__)
+ /* On AArch64, a single function may be split into multiple function
+ fragments, each with it's own .pdata and (if required) .xdata records.
+ The language-specific exception handler data in .xdata records used by GCC
+ consists of two values:
+ - The offset of the fragment within the full function;
+ - The RVA of a .seh_handlerdata record, which is shared by all
+ fragments of the function. */
+ uint32_t fragment_offset = *(uint32_t *) p;
+ p += sizeof (uint32_t);
+ info->Start -= fragment_offset;
+ uint32_t handler_data_rva = *(uint32_t *) p;
+ p = (unsigned char *) _Unwind_GetTextRelBase (context) + handler_data_rva;
+#endif
+
// Find @LPStart, the base to which landing pad offsets are relative.
lpstart_encoding = *p++;
if (lpstart_encoding != DW_EH_PE_omit)