From patchwork Tue Aug 10 21:07:24 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Henderson X-Patchwork-Id: 61412 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 94CD1B6F0E for ; Wed, 11 Aug 2010 07:07:58 +1000 (EST) Received: (qmail 9427 invoked by alias); 10 Aug 2010 21:07:45 -0000 Received: (qmail 9024 invoked by uid 22791); 10 Aug 2010 21:07:39 -0000 X-SWARE-Spam-Status: No, hits=-0.6 required=5.0 tests=AWL, BAYES_50, RCVD_IN_DNSWL_NONE X-Spam-Check-By: sourceware.org Received: from a.mail.sonic.net (HELO a.mail.sonic.net) (64.142.16.245) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Tue, 10 Aug 2010 21:07:28 +0000 Received: from are.twiddle.net (are.twiddle.net [75.101.38.216]) by a.mail.sonic.net (8.13.8.Beta0-Sonic/8.13.7) with ESMTP id o7AL7QBG011680 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Tue, 10 Aug 2010 14:07:26 -0700 Received: from are.twiddle.net (localhost [127.0.0.1]) by are.twiddle.net (8.14.4/8.14.4) with ESMTP id o7AL7PTs008822; Tue, 10 Aug 2010 14:07:25 -0700 Received: (from rth@localhost) by are.twiddle.net (8.14.4/8.14.4/Submit) id o7AL7PNr008821; Tue, 10 Aug 2010 14:07:25 -0700 From: rth@redhat.com To: gcc-patches@gcc.gnu.org Cc: Richard Henderson , ktietz70@googlemail.com Subject: [PATCH 3/3] First take at SEH. Date: Tue, 10 Aug 2010 14:07:24 -0700 Message-Id: <1281474444-8772-4-git-send-email-rth@redhat.com> In-Reply-To: <1281474444-8772-1-git-send-email-rth@redhat.com> References: <1281474444-8772-1-git-send-email-rth@redhat.com> To: gcc-patches@gcc.gnu.org X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org From: Richard Henderson Horrid parsing code manages to build 2 SEH frames for DRAP functions. Which doesn't work during the epilogues, and it is unknown if it will even work for the body of the function. --- gcc/config/i386/cygming.h | 24 ++- gcc/config/i386/i386-protos.h | 7 + gcc/config/i386/i386.c | 56 +---- gcc/config/i386/i386.h | 52 ++++ gcc/config/i386/winnt.c | 561 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 641 insertions(+), 59 deletions(-) diff --git a/gcc/config/i386/cygming.h b/gcc/config/i386/cygming.h index 4962921..9bd04c7 100644 --- a/gcc/config/i386/cygming.h +++ b/gcc/config/i386/cygming.h @@ -36,6 +36,15 @@ along with GCC; see the file COPYING3. If not see #undef TARGET_SEH #define TARGET_SEH TARGET_64BIT_MS_ABI +/* Support hooks for SEH. */ +#undef TARGET_ASM_UNWIND_EMIT +#define TARGET_ASM_UNWIND_EMIT i386_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 i386_pe_seh_end_prologue +#define SUBTARGET_ASM_UNWIND_INIT i386_pe_seh_init + #undef DEFAULT_ABI #define DEFAULT_ABI (TARGET_64BIT ? MS_ABI : SYSV_ABI) @@ -270,15 +279,12 @@ do { \ properly. If we are generating SDB debugging information, this will happen automatically, so we only need to handle other cases. */ #undef ASM_DECLARE_FUNCTION_NAME -#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \ - do \ - { \ - i386_pe_maybe_record_exported_symbol (DECL, NAME, 0); \ - if (write_symbols != SDB_DEBUG) \ - i386_pe_declare_function_type (FILE, NAME, TREE_PUBLIC (DECL)); \ - ASM_OUTPUT_FUNCTION_LABEL (FILE, NAME, DECL); \ - } \ - while (0) +#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \ + i386_pe_start_function (FILE, NAME, DECL) + +#undef ASM_DECLARE_FUNCTION_SIZE +#define ASM_DECLARE_FUNCTION_SIZE(FILE,NAME,DECL) \ + i386_pe_end_function (FILE, NAME, DECL) /* Add an external function to the list of functions to be declared at the end of the file. */ diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h index e41d810..cad9ee3 100644 --- a/gcc/config/i386/i386-protos.h +++ b/gcc/config/i386/i386-protos.h @@ -31,6 +31,7 @@ extern void ix86_setup_frame_addresses (void); extern HOST_WIDE_INT ix86_initial_elimination_offset (int, int); extern void ix86_expand_prologue (void); extern void ix86_expand_epilogue (int); +extern void ix86_compute_frame_layout (struct ix86_frame *); extern void ix86_output_addr_vec_elt (FILE *, int); extern void ix86_output_addr_diff_elt (FILE *, int, int); @@ -230,8 +231,14 @@ extern void i386_pe_asm_output_aligned_decl_common (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT); extern void i386_pe_file_end (void); +extern void i386_pe_start_function (FILE *, const char *, tree); +extern void i386_pe_end_function (FILE *, const char *, tree); extern tree i386_pe_mangle_decl_assembler_name (tree, tree); +extern void i386_pe_seh_init (FILE *); +extern void i386_pe_seh_end_prologue (FILE *); +extern void i386_pe_seh_unwind_emit (FILE *, rtx); + /* In winnt-cxx.c and winnt-stubs.c */ extern void i386_pe_adjust_class_at_definition (tree); extern bool i386_pe_type_dllimport_p (tree); diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 938a686..006611b 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -1818,53 +1818,6 @@ struct GTY(()) stack_local_entry { struct stack_local_entry *next; }; -/* Structure describing stack frame layout. - Stack grows downward: - - [arguments] - <- ARG_POINTER - saved pc - - saved static chain if ix86_static_chain_on_stack - - saved frame pointer if frame_pointer_needed - <- HARD_FRAME_POINTER - [saved regs] - <- regs_save_offset - [padding0] - - [saved SSE regs] - <- sse_regs_save_offset - [padding1] | - | <- FRAME_POINTER - [va_arg registers] | - | - [frame] | - | - [padding2] | = to_allocate - <- STACK_POINTER - */ -struct ix86_frame -{ - int nsseregs; - int nregs; - int va_arg_size; - int red_zone_size; - int outgoing_arguments_size; - HOST_WIDE_INT frame; - - /* The offsets relative to ARG_POINTER. */ - HOST_WIDE_INT frame_pointer_offset; - HOST_WIDE_INT hard_frame_pointer_offset; - HOST_WIDE_INT stack_pointer_offset; - HOST_WIDE_INT reg_save_offset; - HOST_WIDE_INT sse_reg_save_offset; - - /* When save_regs_using_mov is set, emit prologue using - move instead of push instructions. */ - bool save_regs_using_mov; -}; - /* Code model option. */ enum cmodel ix86_cmodel; /* Asm dialect. */ @@ -1976,7 +1929,6 @@ static rtx ix86_function_value (const_tree, const_tree, bool); static bool ix86_function_value_regno_p (const unsigned int); static rtx ix86_static_chain (const_tree, bool); static int ix86_function_regparm (const_tree, const_tree); -static void ix86_compute_frame_layout (struct ix86_frame *); static bool ix86_expand_vector_init_one_nonzero (bool, enum machine_mode, rtx, rtx, int); static void ix86_add_new_builtins (int); @@ -2005,7 +1957,6 @@ static void ix86_set_current_function (tree); static unsigned int ix86_minimum_incoming_stack_boundary (bool); static enum calling_abi ix86_function_abi (const_tree); - #ifndef SUBTARGET32_DEFAULT_CPU #define SUBTARGET32_DEFAULT_CPU "i386" @@ -5166,6 +5117,10 @@ ix86_asm_output_function_label (FILE *asm_out_file, const char *fname, fprintf (asm_out_file, ASM_LONG " %#x\n", filler_cc); } +#ifdef SUBTARGET_ASM_UNWIND_INIT + SUBTARGET_ASM_UNWIND_INIT (asm_out_file); +#endif + ASM_OUTPUT_LABEL (asm_out_file, fname); /* Output magic byte marker, if hot-patch attribute is set. */ @@ -8318,7 +8273,7 @@ ix86_builtin_setjmp_frame_value (void) /* Fill structure ix86_frame about frame of currently computed function. */ -static void +void ix86_compute_frame_layout (struct ix86_frame *frame) { unsigned int stack_alignment_needed; @@ -31476,6 +31431,7 @@ ix86_enum_va_list (int idx, const char **pname, tree *ptree) return 0; } + /* Initialize the GCC target structure. */ #undef TARGET_RETURN_IN_MEMORY diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index eb73df7..e998d15 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -2333,6 +2333,55 @@ struct GTY(()) machine_frame_state BOOL_BITFIELD realigned : 1; }; +/* Structure describing stack frame layout. + Stack grows downward: + + [arguments] + <- ARG_POINTER + saved pc + + saved static chain if ix86_static_chain_on_stack + + saved frame pointer if frame_pointer_needed + <- HARD_FRAME_POINTER + [saved regs] + <- regs_save_offset + [padding0] + + [saved SSE regs] + <- sse_regs_save_offset + [padding1] | + | <- FRAME_POINTER + [va_arg registers] | + | + [frame] | + | + [padding2] | = to_allocate + <- STACK_POINTER + */ +struct ix86_frame +{ + int nsseregs; + int nregs; + int va_arg_size; + int red_zone_size; + int outgoing_arguments_size; + HOST_WIDE_INT frame; + + /* The offsets relative to ARG_POINTER. */ + HOST_WIDE_INT frame_pointer_offset; + HOST_WIDE_INT hard_frame_pointer_offset; + HOST_WIDE_INT stack_pointer_offset; + HOST_WIDE_INT reg_save_offset; + HOST_WIDE_INT sse_reg_save_offset; + + /* When save_regs_using_mov is set, emit prologue using + move instead of push instructions. */ + bool save_regs_using_mov; +}; + +struct seh_frame_state; + struct GTY(()) machine_function { struct stack_local_entry *stack_locals; const char *some_ld_name; @@ -2376,6 +2425,9 @@ struct GTY(()) machine_function { /* During prologue/epilogue generation, the current frame state. Otherwise, the frame state at the end of the prologue. */ struct machine_frame_state fs; + + /* During SEH output, this is non-null. */ + struct seh_frame_state * GTY((skip(""))) seh; }; #endif diff --git a/gcc/config/i386/winnt.c b/gcc/config/i386/winnt.c index 60a8b79..f432fda 100644 --- a/gcc/config/i386/winnt.c +++ b/gcc/config/i386/winnt.c @@ -730,4 +730,565 @@ i386_pe_file_end (void) } } + +/* x64 Structured Exception Handling unwind info. */ + +struct seh_frame_state +{ + /* The frame data for this function. */ + struct ix86_frame frame; + + /* SEH uses unsigned offsets for all register saves. When using a frame + pointer, these offsets are relative to FP_BASE = FP_REG - FP_OFFSET. + Not to be confusing with too many "offset" values, but this variable + contains the CFA offset of the FP_BASE we've chosen for this function. + This makes it easy fo compare with other CFA offsets we'll be + extracting from the RTX_FRAME_RELATED expressions. */ + HOST_WIDE_INT fp_base_offset; + + /* The CFA is located at CFA_REG + CFA_OFFSET. */ + HOST_WIDE_INT cfa_offset; + unsigned int cfa_regno; + + /* From the SEH docs: "If an FP reg is used, then any unwind code + taking an offset must only be used after the FP reg is established + in the prolog." This records if such an event has ocurred. */ + bool reg_save_emitted; + + /* This is all kinds of tricky: + + In order to support functions with a DRAP, we actually emit two SEH + regions; the first covers from the function entry point until we've + re-aligned the stack. The second covers the balance of the function. + In the first region we set up DRAP as the FP. This keeps track of + the entry stack value while we perform the re-alignment. In the + second region we set up RBP as the FP as normal, and use the saved + CFA within the stack frame as the saved RSP value. + + This is complicated by the fact that we can't begin this second SEH + frame until we've performed the DRAP store into the re-aligned frame, + because we have to immediately record the RSP store. Thus we must + queue other register stores that happen beforehand. */ + bool saw_realign; + bool saw_realign_frame; + + /* If we see a register store while SAW_REALIGN, then record the CFA + offset of the store here. */ +#define MAX_SEH_SAVE 32 + int realign_save_count; + rtx realign_save_reg[MAX_SEH_SAVE]; + int realign_save_off[MAX_SEH_SAVE]; +}; + +static void seh_emit_realign_frame (FILE *, struct seh_frame_state *, + HOST_WIDE_INT); + +/* Set up data structures beginning output for SEH. */ + +void +i386_pe_seh_init (FILE *f) +{ + struct seh_frame_state *seh; + + if (!TARGET_SEH) + return; + + seh = XCNEW (struct seh_frame_state); + cfun->machine->seh = seh; + + ix86_compute_frame_layout (&seh->frame); + + if (frame_pointer_needed) + { + /* Careful: FP_OFFSET must be a multiple of 16. If we have no + SSE registers to save, then we won't have aligned the SSE + register save area, and so this offset might not be aligned. */ + if (seh->frame.nsseregs == 0) + seh->frame.sse_reg_save_offset += seh->frame.sse_reg_save_offset & 8; + gcc_checking_assert ((seh->frame.sse_reg_save_offset & 15) == 0); + } + + seh->cfa_offset = INCOMING_FRAME_SP_OFFSET; + seh->cfa_regno = STACK_POINTER_REGNUM; + + fputs ("\t.seh_proc\t", f); + assemble_name (f, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (cfun->decl))); + fputc ('\n', f); +} + +void +i386_pe_seh_end_prologue (FILE *f) +{ + if (!TARGET_SEH) + return; + + XDELETE (cfun->machine->seh); + cfun->machine->seh = NULL; + + fputs ("\t.seh_endprologue\n", f); +} + +static void +i386_pe_seh_fini (FILE *f) +{ + if (!TARGET_SEH) + return; + + fputs ("\t.seh_endproc\n", f); +} + +/* Emit an assembler directive to save REG via a PUSH. */ + +static void +seh_emit_push (FILE *f, struct seh_frame_state *seh, rtx reg) +{ + unsigned int regno = REGNO (reg); + + gcc_checking_assert (GENERAL_REGNO_P (regno)); + + if (seh->cfa_regno == STACK_POINTER_REGNUM) + seh->cfa_offset += UNITS_PER_WORD; + + if (seh->saw_realign && !seh->saw_realign_frame) + { + gcc_checking_assert (seh->cfa_regno == STACK_POINTER_REGNUM); + if (regno == REGNO (crtl->drap_reg)) + seh_emit_realign_frame (f, seh, seh->cfa_offset); + else + { + int i = seh->realign_save_count++; + seh->realign_save_reg[i] = reg; + seh->realign_save_off[i] = seh->cfa_offset; + } + } + else + { + fputs ("\t.seh_pushreg\t", f); + print_reg (reg, 0, f); + fputc ('\n', f); + } +} + +/* Emit an assembler directive to save REG at CFA - CFA_OFFSET. */ + +static void +seh_emit_save (FILE *f, struct seh_frame_state *seh, + rtx reg, HOST_WIDE_INT cfa_offset) +{ + unsigned int regno = REGNO (reg); + + if (seh->saw_realign && !seh->saw_realign_frame) + { + if (regno == REGNO (crtl->drap_reg)) + seh_emit_realign_frame (f, seh, cfa_offset); + else + { + int i = seh->realign_save_count++; + seh->realign_save_reg[i] = reg; + seh->realign_save_off[i] = cfa_offset; + } + } + else + { + HOST_WIDE_INT base_offset, offset; + + if (seh->cfa_regno == STACK_POINTER_REGNUM) + base_offset = seh->cfa_offset; + else + base_offset = seh->fp_base_offset; + + /* Negative save offsets are not supported. */ + gcc_assert (base_offset >= cfa_offset); + offset = base_offset - cfa_offset; + + fputs ((SSE_REGNO_P (regno) ? "\t.seh_savexmm\t" + : GENERAL_REGNO_P (regno) ? "\t.seh_savereg\t" + : (gcc_unreachable (), "")), f); + print_reg (reg, 0, f); + fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC "\n", offset); + + seh->reg_save_emitted = true; + } +} + +/* Emit an assembler directive to adjust RSP by OFFSET. */ + +static void +seh_emit_stackalloc (FILE *f, struct seh_frame_state *seh, + HOST_WIDE_INT offset) +{ + /* We're only concerned with prologue stack allocations, which all + are subtractions from the stack pointer. */ + gcc_assert (offset < 0); + offset = -offset; + + if (seh->cfa_regno == STACK_POINTER_REGNUM) + seh->cfa_offset += offset; + + if (!seh->saw_realign || seh->saw_realign_frame) + fprintf (f, "\t.seh_stackalloc\t" HOST_WIDE_INT_PRINT_DEC "\n", offset); +} + +/* Emit an assembler directive to set up the frame pointer. */ + +static void +seh_emit_setframe (FILE *f, struct seh_frame_state *seh, + rtx reg, HOST_WIDE_INT cfa_offset, + HOST_WIDE_INT max_offset) +{ + HOST_WIDE_INT offset; + + if (seh->saw_realign && !seh->saw_realign_frame) + return; + + offset = max_offset - seh->cfa_offset; + gcc_assert (seh->cfa_regno == STACK_POINTER_REGNUM); + gcc_assert (offset >= 0); + gcc_assert ((offset & 15) == 0); + + seh->cfa_regno = REGNO (reg); + seh->cfa_offset = cfa_offset; + seh->fp_base_offset = max_offset; + + fputs ("\t.seh_setframe\t", f); + print_reg (reg, 0, f); + fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC "\n", offset); +} + +/* Emit assembler directives to establish a realigned stack frame. */ + +static void +seh_emit_realign_frame (FILE *f, struct seh_frame_state *seh, + HOST_WIDE_INT drap_cfa_offset) +{ + int i; + + gcc_assert (seh->saw_realign); + gcc_assert (!seh->saw_realign_frame); + seh->saw_realign_frame = true; + + /* End the first SEH frame and begin another. For some reason the + syntax of .seh_proc requires a symbol; invent a new "r.foo" symbol + to describe the second SEH frame. */ + fputs ("\t.seh_endproc\n", f); + fputs ("\t.seh_proc\tr.", f); + assemble_name (f, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (cfun->decl))); + fputc ('\n', f); + + /* We've been tracking the current stack offset via CFA_OFFSET, + but we havn't been able to record anything so far. Do so now. */ + gcc_assert (seh->cfa_regno == STACK_POINTER_REGNUM); + fprintf (f, "\t.seh_stackalloc\t" HOST_WIDE_INT_PRINT_DEC "\n", + seh->cfa_offset); + + /* Establish the frame pointer for the realigned frame. */ + seh_emit_setframe (f, seh, hard_frame_pointer_rtx, + seh->frame.hard_frame_pointer_offset, + seh->frame.sse_reg_save_offset); + + /* Record all of the register saves we've seen to this point. */ + for (i = seh->realign_save_count - 1; i >= 0; --i) + seh_emit_save (f, seh, seh->realign_save_reg[i], seh->realign_save_off[i]); + + /* Record the save of the DRAP as a save of RSP. */ + seh_emit_save (f, seh, stack_pointer_rtx, drap_cfa_offset); +} + +/* Process REG_CFA_DEF_CFA for SEH. */ + +static void +seh_cfa_def_cfa (FILE *f, struct seh_frame_state *seh, rtx pat) +{ + HOST_WIDE_INT addend = 0; + + /* Another bit of a hack; see ix86_emit_save_reg_using_mov for + the original. We've stored the DRAP register into the frame. + For dwarf2 we want to establish a new CFA. For SEH we want + to record this as a save of the original RSP. + + The other instances of REG_CFA_DEF_CFA are in the epilogue, + which are not relevant here. */ + gcc_assert (MEM_P (pat)); + pat = XEXP (pat, 0); + if (GET_CODE (pat) == PLUS) + { + addend = INTVAL (XEXP (pat, 1)); + pat = XEXP (pat, 0); + } + gcc_assert (pat == hard_frame_pointer_rtx); + + seh_emit_realign_frame (f, seh, + seh->frame.hard_frame_pointer_offset - addend); +} + +/* Process REG_CFA_ADJUST_CFA for SEH. */ + +static void +seh_cfa_adjust_cfa (FILE *f, struct seh_frame_state *seh, rtx pat) +{ + rtx dest, src; + HOST_WIDE_INT reg_offset = 0; + unsigned int dest_regno; + + dest = SET_DEST (pat); + src = SET_SRC (pat); + + if (GET_CODE (src) == PLUS) + { + reg_offset = INTVAL (XEXP (src, 1)); + src = XEXP (src, 0); + } + gcc_assert (src == stack_pointer_rtx); + gcc_assert (seh->cfa_regno == STACK_POINTER_REGNUM); + dest_regno = REGNO (dest); + + if (dest_regno == STACK_POINTER_REGNUM) + seh_emit_stackalloc (f, seh, reg_offset); + else if (dest_regno == HARD_FRAME_POINTER_REGNUM) + seh_emit_setframe (f, seh, dest, seh->cfa_offset - reg_offset, + seh->frame.sse_reg_save_offset); + else if (crtl->drap_reg && dest_regno == REGNO (crtl->drap_reg)) + seh_emit_setframe (f, seh, dest, seh->cfa_offset - reg_offset, + seh->cfa_offset); + else + gcc_unreachable (); +} + +/* Process REG_CFA_OFFSET for SEH. */ + +static void +seh_cfa_offset (FILE *f, struct seh_frame_state *seh, rtx pat) +{ + rtx dest, src; + HOST_WIDE_INT reg_offset; + + dest = SET_DEST (pat); + src = SET_SRC (pat); + + gcc_assert (MEM_P (dest)); + dest = XEXP (dest, 0); + if (REG_P (dest)) + reg_offset = 0; + else + { + gcc_assert (GET_CODE (dest) == PLUS); + reg_offset = INTVAL (XEXP (dest, 1)); + dest = XEXP (dest, 0); + } + gcc_assert (REGNO (dest) == seh->cfa_regno); + + seh_emit_save (f, seh, src, seh->cfa_offset - reg_offset); +} + +/* Process REG_CFA_EXPRESSION for SEH. */ + +static void +seh_cfa_expression (FILE *f, struct seh_frame_state *seh, rtx pat) +{ + /* ??? This is valid only under extremely limited circumstances. + This should be a re-aligned stack frame with DRAP. For dwarf2, + the CFA is still up with the DRAP and we need the CFA_expression + in order to save a register relative to RBP instead of relative + to the CFA. For SEH, we should be in the (second) aligned frame + with the frame pointer established. */ + gcc_checking_assert (seh->saw_realign); + seh_cfa_offset (f, seh, pat); +} + +/* Process a FRAME_RELATED_EXPR for SEH. */ + +static void +seh_frame_related_expr (FILE *f, struct seh_frame_state *seh, rtx pat) +{ + rtx dest, src; + HOST_WIDE_INT addend; + + /* See the full loop in dwarf2out_frame_debug_expr. */ + if (GET_CODE (pat) == PARALLEL || GET_CODE (pat) == SEQUENCE) + { + int i, n = XVECLEN (pat, 0), pass, npass; + + npass = (GET_CODE (pat) == PARALLEL ? 2 : 1); + for (pass = 0; pass < npass; ++pass) + for (i = 0; i < n; ++i) + { + rtx ele = XVECEXP (pat, 0, i); + + if (GET_CODE (ele) != SET) + continue; + dest = SET_DEST (ele); + + /* Process each member of the PARALLEL independently. The first + member is always processed; others only if they are marked. */ + if (i == 0 || RTX_FRAME_RELATED_P (ele)) + { + /* Evaluate all register saves in the first pass and all + register updates in the second pass. */ + if (MEM_P (dest) ^ pass) + seh_frame_related_expr (f, seh, ele); + } + } + return; + } + + dest = SET_DEST (pat); + src = SET_SRC (pat); + + switch (GET_CODE (dest)) + { + case REG: + switch (GET_CODE (src)) + { + case REG: + /* REG = REG: This should be establishing a frame pointer. */ + gcc_assert (src == stack_pointer_rtx); + gcc_assert (dest == hard_frame_pointer_rtx); + seh_emit_setframe (f, seh, dest, seh->cfa_offset, + seh->frame.sse_reg_save_offset); + break; + + case PLUS: + addend = INTVAL (XEXP (src, 1)); + src = XEXP (src, 0); + if (REGNO (src) == seh->cfa_regno) + seh_cfa_adjust_cfa (f, seh, pat); + else if (dest == stack_pointer_rtx) + { + gcc_assert (src == stack_pointer_rtx); + seh_emit_stackalloc (f, seh, addend); + } + else + gcc_unreachable (); + break; + + case AND: + gcc_assert (dest == stack_pointer_rtx); + if (stack_realign_drap) + { + seh->saw_realign = true; + seh->cfa_regno = STACK_POINTER_REGNUM; + seh->cfa_offset = 0; + + fputs ("\t.seh_endprologue\n", f); + } + break; + + default: + gcc_unreachable (); + } + break; + + case MEM: + /* A save of some kind. */ + dest = XEXP (dest, 0); + if (GET_CODE (dest) == PRE_DEC) + { + gcc_checking_assert (GET_MODE (src) == Pmode); + + /* During drap, we push a copy of the return address so that + it's in the aligned stack frame. We need do nothing except + account for the stack usage. */ + if (MEM_P (src)) + seh_emit_stackalloc (f, seh, -UNITS_PER_WORD); + else + seh_emit_push (f, seh, src); + } + else + seh_cfa_offset (f, seh, pat); + break; + + default: + gcc_unreachable (); + } +} + +/* This function looks at a single insn and emits any SEH directives + required for unwind of this insn. */ + +void +i386_pe_seh_unwind_emit (FILE *asm_out_file, rtx insn) +{ + rtx note, pat; + bool handled_one = false; + struct seh_frame_state *seh; + + if (!TARGET_SEH) + return; + if (NOTE_P (insn) || !RTX_FRAME_RELATED_P (insn)) + return; + + /* We free the SEH data once done with the prologue. Ignore those + RTX_FRAME_RELATED_P insns that are associated with the epilogue. */ + seh = cfun->machine->seh; + if (seh == NULL) + return; + + for (note = REG_NOTES (insn); note ; note = XEXP (note, 1)) + { + pat = XEXP (note, 0); + switch (REG_NOTE_KIND (note)) + { + case REG_FRAME_RELATED_EXPR: + goto found; + + case REG_CFA_DEF_CFA: + seh_cfa_def_cfa (asm_out_file, seh, pat); + handled_one = true; + break; + + case REG_CFA_ADJUST_CFA: + if (pat == NULL) + { + pat = PATTERN (insn); + if (GET_CODE (pat) == PARALLEL) + pat = XVECEXP (pat, 0, 0); + } + seh_cfa_adjust_cfa (asm_out_file, seh, pat); + handled_one = true; + break; + + case REG_CFA_OFFSET: + if (pat == NULL) + pat = single_set (insn); + seh_cfa_offset (asm_out_file, seh, pat); + handled_one = true; + break; + + case REG_CFA_REGISTER: + gcc_unreachable (); + + case REG_CFA_EXPRESSION: + seh_cfa_expression (asm_out_file, seh, pat); + handled_one = true; + break; + + default: + break; + } + } + if (handled_one) + return; + pat = PATTERN (insn); + found: + seh_frame_related_expr (asm_out_file, seh, pat); +} + + +void +i386_pe_start_function (FILE *f, const char *name, tree decl) +{ + i386_pe_maybe_record_exported_symbol (decl, name, 0); + if (write_symbols != SDB_DEBUG) + i386_pe_declare_function_type (f, name, TREE_PUBLIC (decl)); + ASM_OUTPUT_FUNCTION_LABEL (f, name, decl); +} + +void +i386_pe_end_function (FILE *f, const char *name ATTRIBUTE_UNUSED, + tree decl ATTRIBUTE_UNUSED) +{ + i386_pe_seh_fini (f); +} + + #include "gt-winnt.h"