summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>2010-11-01 00:40:33 +0000
committerrth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>2010-11-01 00:40:33 +0000
commit1f2aa021e17bfda02a3f4fdf874c42512d6a43bc (patch)
treec5201d7da44fc0f1911791bc4f786c53bbdb15f3
parent3d80b4dac66fc35a3c80b710285e1b5932bff41b (diff)
downloadgcc-1f2aa021e17bfda02a3f4fdf874c42512d6a43bc.tar.gz
Emit SEH unwind info.
* config/i386/cygming.h (TARGET_SEH): New. (MAX_STACK_ALIGNMENT): New. Disable alignment for SEH. (TARGET_ASM_UNWIND_EMIT, TARGET_ASM_UNWIND_EMIT_BEFORE_INSN, TARGET_ASM_FUNCTION_END_PROLOGUE, SUBTARGET_ASM_UNWIND_INIT): New. (TARGET_OS_CPP_BUILTINS): Define __SEH__ as needed. (ASM_DECLARE_FUNCTION_NAME): Use i386_pe_start_function. (ASM_DECLARE_FUNCTION_SIZE): New. * config/i386/i386-protos.h: Update. * config/i386/i386.c (ix86_option_override_internal): Enable flag_unwind_tables with flag_asynchronous_unwind_tables immediately; restrict -mpreferred-stack-boundary for SEH; enable flag_fentry. (ix86_asm_output_function_label): Use SUBTARGET_ASM_UNWIND_INIT. (ix86_compute_frame_layout): For SEH, disable use_fast_prologue_epilogue, move frame pointer to the end of the frame. Initialize hfp_save_offset. (ix86_expand_prologue): Honor hfp_save_offset. Emit blockage at end of prologue for SEH. (ix86_expand_epilogue): For SEH, use pops, emit a nop if needed, emit blockage at beginning of epilogue. (ix86_expand_binary_operator): After reload, emit LEA if needed. (ix86_output_call_insn): New. * config/i386/i386.h (TARGET_SEH): New. (struct machine_function): Add member seh. * config/i386/i386.md (all call patterns): Use ix86_output_call_insn. * config/i386/winnt.c (struct seh_frame_state): New. (i386_pe_seh_init, i386_pe_seh_end_prologue, i386_pe_seh_fini, seh_emit_push, seh_emit_save, seh_emit_stackalloc, seh_cfa_adjust_cfa, seh_cfa_offset, seh_frame_related_expr, i386_pe_seh_unwind_emit, i386_pe_start_function, i386_pe_end_function): New. * dwarf2out.c (dwarf2out_frame_debug_expr): Accept CFA as well as CFA_STORE in rules 12 and 13. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@166119 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/ChangeLog35
-rw-r--r--gcc/config/i386/cygming.h34
-rw-r--r--gcc/config/i386/i386-protos.h8
-rw-r--r--gcc/config/i386/i386.c217
-rw-r--r--gcc/config/i386/i386.h9
-rw-r--r--gcc/config/i386/i386.md101
-rw-r--r--gcc/config/i386/winnt.c381
-rw-r--r--gcc/dwarf2out.c8
8 files changed, 677 insertions, 116 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 13292d42f41..3c426806617 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,38 @@
+2010-10-31 Richard Henderson <rth@redhat.com>
+
+ * config/i386/cygming.h (TARGET_SEH): New.
+ (MAX_STACK_ALIGNMENT): New. Disable alignment for SEH.
+ (TARGET_ASM_UNWIND_EMIT, TARGET_ASM_UNWIND_EMIT_BEFORE_INSN,
+ TARGET_ASM_FUNCTION_END_PROLOGUE, SUBTARGET_ASM_UNWIND_INIT): New.
+ (TARGET_OS_CPP_BUILTINS): Define __SEH__ as needed.
+ (ASM_DECLARE_FUNCTION_NAME): Use i386_pe_start_function.
+ (ASM_DECLARE_FUNCTION_SIZE): New.
+ * config/i386/i386-protos.h: Update.
+ * config/i386/i386.c (ix86_option_override_internal): Enable
+ flag_unwind_tables with flag_asynchronous_unwind_tables immediately;
+ restrict -mpreferred-stack-boundary for SEH; enable flag_fentry.
+ (ix86_asm_output_function_label): Use SUBTARGET_ASM_UNWIND_INIT.
+ (ix86_compute_frame_layout): For SEH, disable
+ use_fast_prologue_epilogue, move frame pointer to the end of
+ the frame. Initialize hfp_save_offset.
+ (ix86_expand_prologue): Honor hfp_save_offset. Emit blockage
+ at end of prologue for SEH.
+ (ix86_expand_epilogue): For SEH, use pops, emit a nop if needed,
+ emit blockage at beginning of epilogue.
+ (ix86_expand_binary_operator): After reload, emit LEA if needed.
+ (ix86_output_call_insn): New.
+ * config/i386/i386.h (TARGET_SEH): New.
+ (struct machine_function): Add member seh.
+ * config/i386/i386.md (all call patterns): Use ix86_output_call_insn.
+ * config/i386/winnt.c (struct seh_frame_state): New.
+ (i386_pe_seh_init, i386_pe_seh_end_prologue, i386_pe_seh_fini,
+ seh_emit_push, seh_emit_save, seh_emit_stackalloc, seh_cfa_adjust_cfa,
+ seh_cfa_offset, seh_frame_related_expr, i386_pe_seh_unwind_emit,
+ i386_pe_start_function, i386_pe_end_function): New.
+
+ * dwarf2out.c (dwarf2out_frame_debug_expr): Accept CFA as well
+ as CFA_STORE in rules 12 and 13.
+
2010-10-31 Uros Bizjak <ubizjak@gmail.com>
PR tree-optimization/46142
diff --git a/gcc/config/i386/cygming.h b/gcc/config/i386/cygming.h
index bb1a420b1dd..09ea866c0d7 100644
--- a/gcc/config/i386/cygming.h
+++ b/gcc/config/i386/cygming.h
@@ -33,6 +33,23 @@ along with GCC; see the file COPYING3. If not see
#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG
#endif
+#undef TARGET_SEH
+#define TARGET_SEH (TARGET_64BIT_MS_ABI && flag_unwind_tables)
+
+/* Win64 with SEH cannot represent DRAP stack frames. Disable its use.
+ Force the use of different mechanisms to allocate aligned local data. */
+#undef MAX_STACK_ALIGNMENT
+#define MAX_STACK_ALIGNMENT (TARGET_SEH ? 128 : MAX_OFILE_ALIGNMENT)
+
+/* 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)
@@ -104,6 +121,8 @@ along with GCC; see the file COPYING3. If not see
{ \
if (!TARGET_64BIT) \
builtin_define ("_X86_=1"); \
+ if (TARGET_SEH) \
+ builtin_define ("__SEH__"); \
builtin_assert ("system=winnt"); \
builtin_define ("__stdcall=__attribute__((__stdcall__))"); \
builtin_define ("__fastcall=__attribute__((__fastcall__))"); \
@@ -281,15 +300,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 02c2a900824..f6f9071395f 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -225,8 +225,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);
@@ -263,3 +269,5 @@ extern int asm_preferred_eh_data_format (int, int);
#ifdef HAVE_ATTR_cpu
extern enum attr_cpu ix86_schedule;
#endif
+
+extern const char * ix86_output_call_insn (rtx insn, rtx call_op, int addr_op);
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 4cd3f070b91..32d6371a133 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -2151,6 +2151,7 @@ struct ix86_frame
HOST_WIDE_INT frame_pointer_offset;
HOST_WIDE_INT hard_frame_pointer_offset;
HOST_WIDE_INT stack_pointer_offset;
+ HOST_WIDE_INT hfp_save_offset;
HOST_WIDE_INT reg_save_offset;
HOST_WIDE_INT sse_reg_save_offset;
@@ -3573,7 +3574,7 @@ ix86_option_override_internal (bool main_args_p)
if (optimize >= 1 && !global_options_set.x_flag_omit_frame_pointer)
flag_omit_frame_pointer = !USE_X86_64_FRAME_POINTER;
if (flag_asynchronous_unwind_tables == 2)
- flag_asynchronous_unwind_tables = 1;
+ flag_unwind_tables = flag_asynchronous_unwind_tables = 1;
if (flag_pcc_struct_return == 2)
flag_pcc_struct_return = 0;
}
@@ -3777,10 +3778,19 @@ ix86_option_override_internal (bool main_args_p)
ix86_preferred_stack_boundary = PREFERRED_STACK_BOUNDARY_DEFAULT;
if (ix86_preferred_stack_boundary_string)
{
+ int min = (TARGET_64BIT ? 4 : 2);
+ int max = (TARGET_SEH ? 4 : 12);
+
i = atoi (ix86_preferred_stack_boundary_string);
- if (i < (TARGET_64BIT ? 4 : 2) || i > 12)
- error ("%spreferred-stack-boundary=%d%s is not between %d and 12",
- prefix, i, suffix, TARGET_64BIT ? 4 : 2);
+ if (i < min || i > max)
+ {
+ if (min == max)
+ error ("%spreferred-stack-boundary%s is not supported "
+ "for this target", prefix, suffix);
+ else
+ error ("%spreferred-stack-boundary=%d%s is not between %d and %d",
+ prefix, i, suffix, min, max);
+ }
else
ix86_preferred_stack_boundary = (1 << i) * BITS_PER_UNIT;
}
@@ -3987,7 +3997,13 @@ ix86_option_override_internal (bool main_args_p)
sorry ("-mfentry isn't supported for 32-bit in combination with -fpic");
flag_fentry = 0;
}
- if (flag_fentry < 0)
+ else if (TARGET_SEH)
+ {
+ if (flag_fentry == 0)
+ sorry ("-mno-fentry isn't compatible with SEH");
+ flag_fentry = 1;
+ }
+ else if (flag_fentry < 0)
{
#if defined(PROFILE_BEFORE_PROLOGUE)
flag_fentry = 1;
@@ -5536,6 +5552,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. */
@@ -8934,17 +8954,25 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
gcc_assert (preferred_alignment >= STACK_BOUNDARY / BITS_PER_UNIT);
gcc_assert (preferred_alignment <= stack_alignment_needed);
+ /* For SEH we have to limit the amount of code movement into the prologue.
+ At present we do this via a BLOCKAGE, at which point there's very little
+ scheduling that can be done, which means that there's very little point
+ in doing anything except PUSHs. */
+ if (TARGET_SEH)
+ cfun->machine->use_fast_prologue_epilogue = false;
+
/* During reload iteration the amount of registers saved can change.
Recompute the value as needed. Do not recompute when amount of registers
didn't change as reload does multiple calls to the function and does not
expect the decision to change within single iteration. */
- if (!optimize_function_for_size_p (cfun)
- && cfun->machine->use_fast_prologue_epilogue_nregs != frame->nregs)
+ else if (!optimize_function_for_size_p (cfun)
+ && cfun->machine->use_fast_prologue_epilogue_nregs != frame->nregs)
{
int count = frame->nregs;
struct cgraph_node *node = cgraph_node (current_function_decl);
cfun->machine->use_fast_prologue_epilogue_nregs = count;
+
/* The fast prologue uses move instead of push to save registers. This
is significantly longer, but also executes faster as modern hardware
can execute the moves in parallel, but can't do that for push/pop.
@@ -8986,7 +9014,9 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
/* Skip saved base pointer. */
if (frame_pointer_needed)
offset += UNITS_PER_WORD;
+ frame->hfp_save_offset = offset;
+ /* The traditional frame pointer location is at the top of the frame. */
frame->hard_frame_pointer_offset = offset;
/* Register save area */
@@ -9069,6 +9099,27 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
else
frame->red_zone_size = 0;
frame->stack_pointer_offset -= frame->red_zone_size;
+
+ /* The SEH frame pointer location is near the bottom of the frame.
+ This is enforced by the fact that the difference between the
+ stack pointer and the frame pointer is limited to 240 bytes in
+ the unwind data structure. */
+ if (TARGET_SEH)
+ {
+ HOST_WIDE_INT diff;
+
+ /* If we can leave the frame pointer where it is, do so. */
+ diff = frame->stack_pointer_offset - frame->hard_frame_pointer_offset;
+ if (diff > 240 || (diff & 15) != 0)
+ {
+ /* Ideally we'd determine what portion of the local stack frame
+ (within the constraint of the lowest 240) is most heavily used.
+ But without that complication, simply bias the frame pointer
+ by 128 bytes so as to maximize the amount of the local stack
+ frame that is addressable with 8-bit offsets. */
+ frame->hard_frame_pointer_offset = frame->stack_pointer_offset - 128;
+ }
+ }
}
/* This is semi-inlined memory_address_length, but simplified
@@ -10001,7 +10052,8 @@ ix86_expand_prologue (void)
/* Check if profiling is active and we shall use profiling before
prologue variant. If so sorry. */
if (crtl->profile && flag_fentry != 0)
- sorry ("ms_hook_prologue attribute isn't compatible with -mfentry for 32-bit");
+ sorry ("ms_hook_prologue attribute isn't compatible "
+ "with -mfentry for 32-bit");
/* In ix86_asm_output_function_label we emitted:
8b ff movl.s %edi,%edi
@@ -10130,14 +10182,16 @@ ix86_expand_prologue (void)
insn = emit_insn (gen_push (hard_frame_pointer_rtx));
RTX_FRAME_RELATED_P (insn) = 1;
- insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
- RTX_FRAME_RELATED_P (insn) = 1;
+ if (m->fs.sp_offset == frame.hard_frame_pointer_offset)
+ {
+ insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
+ RTX_FRAME_RELATED_P (insn) = 1;
- if (m->fs.cfa_reg == stack_pointer_rtx)
- m->fs.cfa_reg = hard_frame_pointer_rtx;
- gcc_assert (m->fs.sp_offset == frame.hard_frame_pointer_offset);
- m->fs.fp_offset = m->fs.sp_offset;
- m->fs.fp_valid = true;
+ if (m->fs.cfa_reg == stack_pointer_rtx)
+ m->fs.cfa_reg = hard_frame_pointer_rtx;
+ m->fs.fp_offset = m->fs.sp_offset;
+ m->fs.fp_valid = true;
+ }
}
int_registers_saved = (frame.nregs == 0);
@@ -10290,12 +10344,15 @@ ix86_expand_prologue (void)
insn = emit_insn (adjust_stack_insn (stack_pointer_rtx,
stack_pointer_rtx, eax));
- if (m->fs.cfa_reg == stack_pointer_rtx)
+ /* Note that SEH directives need to continue tracking the stack
+ pointer even after the frame pointer has been set up. */
+ if (m->fs.cfa_reg == stack_pointer_rtx || TARGET_SEH)
{
- m->fs.cfa_offset += allocate;
+ if (m->fs.cfa_reg == stack_pointer_rtx)
+ m->fs.cfa_offset += allocate;
RTX_FRAME_RELATED_P (insn) = 1;
- add_reg_note (insn, REG_CFA_ADJUST_CFA,
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR,
gen_rtx_SET (VOIDmode, stack_pointer_rtx,
plus_constant (stack_pointer_rtx,
-allocate)));
@@ -10317,6 +10374,22 @@ ix86_expand_prologue (void)
}
gcc_assert (m->fs.sp_offset == frame.stack_pointer_offset);
+ /* If we havn't already set up the frame pointer, do so now. */
+ if (frame_pointer_needed && !m->fs.fp_valid)
+ {
+ insn = ix86_gen_add3 (hard_frame_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (frame.stack_pointer_offset
+ - frame.hard_frame_pointer_offset));
+ insn = emit_insn (insn);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ add_reg_note (insn, REG_CFA_ADJUST_CFA, NULL);
+
+ if (m->fs.cfa_reg == stack_pointer_rtx)
+ m->fs.cfa_reg = hard_frame_pointer_rtx;
+ m->fs.fp_offset = frame.hard_frame_pointer_offset;
+ m->fs.fp_valid = true;
+ }
+
if (!int_registers_saved)
ix86_emit_save_regs_using_mov (frame.reg_save_offset);
if (frame.nsseregs)
@@ -10386,6 +10459,11 @@ ix86_expand_prologue (void)
/* Emit cld instruction if stringops are used in the function. */
if (TARGET_CLD && ix86_current_function_needs_cld)
emit_insn (gen_cld ());
+
+ /* SEH requires that the prologue end within 256 bytes of the start of
+ the function. Prevent instruction schedules that would extend that. */
+ if (TARGET_SEH)
+ emit_insn (gen_blockage ());
}
/* Emit code to restore REG using a POP insn. */
@@ -10610,13 +10688,16 @@ ix86_expand_epilogue (int style)
if (crtl->calls_eh_return && style != 2)
frame.reg_save_offset -= 2 * UNITS_PER_WORD;
+ /* EH_RETURN requires the use of moves to function properly. */
+ if (crtl->calls_eh_return)
+ restore_regs_via_mov = true;
+ /* SEH requires the use of pops to identify the epilogue. */
+ else if (TARGET_SEH)
+ restore_regs_via_mov = false;
/* If we're only restoring one register and sp is not valid then
using a move instruction to restore the register since it's
less work than reloading sp and popping the register. */
- if (!m->fs.sp_valid && frame.nregs <= 1)
- restore_regs_via_mov = true;
- /* EH_RETURN requires the use of moves to function properly. */
- else if (crtl->calls_eh_return)
+ else if (!m->fs.sp_valid && frame.nregs <= 1)
restore_regs_via_mov = true;
else if (TARGET_EPILOGUE_USING_MOVE
&& cfun->machine->use_fast_prologue_epilogue
@@ -10728,6 +10809,22 @@ ix86_expand_epilogue (int style)
}
else
{
+ /* SEH requires that the function end with (1) a stack adjustment
+ if necessary, (2) a sequence of pops, and (3) a return or
+ jump instruction. Prevent insns from the function body from
+ being scheduled into this sequence. */
+ if (TARGET_SEH)
+ {
+ /* Prevent a catch region from being adjacent to the standard
+ epilogue sequence. Unfortuantely crtl->uses_eh_lsda nor
+ several other flags that would be interesting to test are
+ not yet set up. */
+ if (flag_non_call_exceptions)
+ emit_insn (gen_nops (const1_rtx));
+ else
+ emit_insn (gen_blockage ());
+ }
+
/* First step is to deallocate the stack frame so that we can
pop the registers. */
if (!m->fs.sp_valid)
@@ -10755,7 +10852,7 @@ ix86_expand_epilogue (int style)
{
/* If the stack pointer is valid and pointing at the frame
pointer store address, then we only need a pop. */
- if (m->fs.sp_valid && m->fs.sp_offset == frame.hard_frame_pointer_offset)
+ if (m->fs.sp_valid && m->fs.sp_offset == frame.hfp_save_offset)
ix86_emit_restore_reg_using_pop (hard_frame_pointer_rtx);
/* Leave results in shorter dependency chains on CPUs that are
able to grok it fast. */
@@ -15494,6 +15591,13 @@ ix86_expand_binary_operator (enum rtx_code code, enum machine_mode mode,
gcc_assert (code == PLUS);
emit_insn (op);
}
+ else if (reload_completed
+ && code == PLUS
+ && !rtx_equal_p (dst, src1))
+ {
+ /* This is going to be an LEA; avoid splitting it later. */
+ emit_insn (op);
+ }
else
{
clob = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, FLAGS_REG));
@@ -21417,6 +21521,73 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
return call;
}
+/* Output the assembly for a call instruction. */
+
+const char *
+ix86_output_call_insn (rtx insn, rtx call_op, int addr_op)
+{
+ bool direct_p = constant_call_address_operand (call_op, Pmode);
+ bool seh_nop_p = false;
+
+ gcc_assert (addr_op == 0 || addr_op == 1);
+
+ if (SIBLING_CALL_P (insn))
+ {
+ if (direct_p)
+ return addr_op ? "jmp\t%P1" : "jmp\t%P0";
+ /* SEH epilogue detection requires the indirect branch case
+ to include REX.W. */
+ else if (TARGET_SEH)
+ return addr_op ? "rex.W jmp %A1" : "rex.W jmp %A0";
+ else
+ return addr_op ? "jmp\t%A1" : "jmp\t%A0";
+ }
+
+ /* SEH unwinding can require an extra nop to be emitted in several
+ circumstances. Determine if we have one of those. */
+ if (TARGET_SEH)
+ {
+ rtx i;
+
+ for (i = NEXT_INSN (insn); i ; i = NEXT_INSN (i))
+ {
+ /* If we get to another real insn, we don't need the nop. */
+ if (INSN_P (i))
+ break;
+
+ /* If we get to the epilogue note, prevent a catch region from
+ being adjacent to the standard epilogue sequence. If non-
+ call-exceptions, we'll have done this during epilogue emission. */
+ if (NOTE_P (i) && NOTE_KIND (i) == NOTE_INSN_EPILOGUE_BEG
+ && !flag_non_call_exceptions
+ && !can_throw_internal (insn))
+ {
+ seh_nop_p = true;
+ break;
+ }
+ }
+
+ /* If we didn't find a real insn following the call, prevent the
+ unwinder from looking into the next function. */
+ if (i == NULL)
+ seh_nop_p = true;
+ }
+
+ if (direct_p)
+ {
+ if (seh_nop_p)
+ return addr_op ? "call\t%P1\n\tnop" : "call\t%P0\n\tnop";
+ else
+ return addr_op ? "call\t%P1" : "call\t%P0";
+ }
+ else
+ {
+ if (seh_nop_p)
+ return addr_op ? "call\t%A1\n\tnop" : "call\t%A0\n\tnop";
+ else
+ return addr_op ? "call\t%A1" : "call\t%A0";
+ }
+}
/* Clear stack slot assignments remembered from previous functions.
This is called from INIT_EXPANDERS once before RTL is emitted for each
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 54740481ed8..3c7f9f07ea1 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -490,6 +490,9 @@ extern tree x86_mfence;
/* For the Windows 64-bit ABI. */
#define TARGET_64BIT_MS_ABI (TARGET_64BIT && ix86_cfun_abi () == MS_ABI)
+/* This is re-defined by cygming.h. */
+#define TARGET_SEH 0
+
/* Available call abi. */
enum calling_abi
{
@@ -2244,6 +2247,9 @@ struct GTY(()) machine_frame_state
BOOL_BITFIELD realigned : 1;
};
+/* Private to winnt.c. */
+struct seh_frame_state;
+
struct GTY(()) machine_function {
struct stack_local_entry *stack_locals;
const char *some_ld_name;
@@ -2312,6 +2318,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/i386.md b/gcc/config/i386/i386.md
index c6c32442add..feaf78173fb 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -11325,32 +11325,21 @@
[(call (mem:QI (match_operand 0 "constant_call_address_operand" ""))
(match_operand 1 "" ""))]
""
-{
- if (SIBLING_CALL_P (insn))
- return "jmp\t%P0";
- else
- return "call\t%P0";
-}
+ { return ix86_output_call_insn (insn, operands[0], 0); }
[(set_attr "type" "call")])
(define_insn "*call_1"
[(call (mem:QI (match_operand:SI 0 "call_insn_operand" "lsm"))
(match_operand 1 "" ""))]
"!TARGET_64BIT && !SIBLING_CALL_P (insn)"
-{
- if (constant_call_address_operand (operands[0], Pmode))
- return "call\t%P0";
- return "call\t%A0";
-}
+ { return ix86_output_call_insn (insn, operands[0], 0); }
[(set_attr "type" "call")])
(define_insn "*sibcall_1"
[(call (mem:QI (match_operand:SI 0 "sibcall_insn_operand" "s,U"))
(match_operand 1 "" ""))]
"!TARGET_64BIT && SIBLING_CALL_P (insn)"
- "@
- jmp\t%P0
- jmp\t%A0"
+ { return ix86_output_call_insn (insn, operands[0], 0); }
[(set_attr "type" "call")])
(define_insn "*call_1_rex64"
@@ -11358,11 +11347,7 @@
(match_operand 1 "" ""))]
"TARGET_64BIT && !SIBLING_CALL_P (insn)
&& ix86_cmodel != CM_LARGE && ix86_cmodel != CM_LARGE_PIC"
-{
- if (constant_call_address_operand (operands[0], Pmode))
- return "call\t%P0";
- return "call\t%A0";
-}
+ { return ix86_output_call_insn (insn, operands[0], 0); }
[(set_attr "type" "call")])
(define_insn "*call_1_rex64_ms_sysv"
@@ -11382,27 +11367,21 @@
(clobber (reg:DI SI_REG))
(clobber (reg:DI DI_REG))]
"TARGET_64BIT && !SIBLING_CALL_P (insn)"
-{
- if (constant_call_address_operand (operands[0], Pmode))
- return "call\t%P0";
- return "call\t%A0";
-}
+ { return ix86_output_call_insn (insn, operands[0], 0); }
[(set_attr "type" "call")])
(define_insn "*call_1_rex64_large"
[(call (mem:QI (match_operand:DI 0 "call_insn_operand" "rm"))
(match_operand 1 "" ""))]
"TARGET_64BIT && !SIBLING_CALL_P (insn)"
- "call\t%A0"
+ { return ix86_output_call_insn (insn, operands[0], 0); }
[(set_attr "type" "call")])
(define_insn "*sibcall_1_rex64"
[(call (mem:QI (match_operand:DI 0 "sibcall_insn_operand" "s,U"))
(match_operand 1 "" ""))]
"TARGET_64BIT && SIBLING_CALL_P (insn)"
- "@
- jmp\t%P0
- jmp\t%A0"
+ { return ix86_output_call_insn (insn, operands[0], 0); }
[(set_attr "type" "call")])
;; Call subroutine, returning value in operand 0
@@ -17152,12 +17131,7 @@
(plus:SI (reg:SI SP_REG)
(match_operand:SI 3 "immediate_operand" "")))]
"!TARGET_64BIT"
-{
- if (SIBLING_CALL_P (insn))
- return "jmp\t%P1";
- else
- return "call\t%P1";
-}
+ { return ix86_output_call_insn (insn, operands[1], 1); }
[(set_attr "type" "callv")])
(define_insn "*call_value_pop_1"
@@ -17168,11 +17142,7 @@
(plus:SI (reg:SI SP_REG)
(match_operand:SI 3 "immediate_operand" "i")))]
"!TARGET_64BIT && !SIBLING_CALL_P (insn)"
-{
- if (constant_call_address_operand (operands[1], Pmode))
- return "call\t%P1";
- return "call\t%A1";
-}
+ { return ix86_output_call_insn (insn, operands[1], 1); }
[(set_attr "type" "callv")])
(define_insn "*sibcall_value_pop_1"
@@ -17183,9 +17153,7 @@
(plus:SI (reg:SI SP_REG)
(match_operand:SI 3 "immediate_operand" "i,i")))]
"!TARGET_64BIT && SIBLING_CALL_P (insn)"
- "@
- jmp\t%P1
- jmp\t%A1"
+ { return ix86_output_call_insn (insn, operands[1], 1); }
[(set_attr "type" "callv")])
(define_insn "*call_value_0"
@@ -17193,12 +17161,7 @@
(call (mem:QI (match_operand:SI 1 "constant_call_address_operand" ""))
(match_operand:SI 2 "" "")))]
"!TARGET_64BIT"
-{
- if (SIBLING_CALL_P (insn))
- return "jmp\t%P1";
- else
- return "call\t%P1";
-}
+ { return ix86_output_call_insn (insn, operands[1], 1); }
[(set_attr "type" "callv")])
(define_insn "*call_value_0_rex64"
@@ -17206,12 +17169,7 @@
(call (mem:QI (match_operand:DI 1 "constant_call_address_operand" ""))
(match_operand:DI 2 "const_int_operand" "")))]
"TARGET_64BIT"
-{
- if (SIBLING_CALL_P (insn))
- return "jmp\t%P1";
- else
- return "call\t%P1";
-}
+ { return ix86_output_call_insn (insn, operands[1], 1); }
[(set_attr "type" "callv")])
(define_insn "*call_value_0_rex64_ms_sysv"
@@ -17232,12 +17190,7 @@
(clobber (reg:DI SI_REG))
(clobber (reg:DI DI_REG))]
"TARGET_64BIT && !SIBLING_CALL_P (insn)"
-{
- if (SIBLING_CALL_P (insn))
- return "jmp\t%P1";
- else
- return "call\t%P1";
-}
+ { return ix86_output_call_insn (insn, operands[1], 1); }
[(set_attr "type" "callv")])
(define_insn "*call_value_1"
@@ -17245,11 +17198,7 @@
(call (mem:QI (match_operand:SI 1 "call_insn_operand" "lsm"))
(match_operand:SI 2 "" "")))]
"!TARGET_64BIT && !SIBLING_CALL_P (insn)"
-{
- if (constant_call_address_operand (operands[1], Pmode))
- return "call\t%P1";
- return "call\t%A1";
-}
+ { return ix86_output_call_insn (insn, operands[1], 1); }
[(set_attr "type" "callv")])
(define_insn "*sibcall_value_1"
@@ -17257,9 +17206,7 @@
(call (mem:QI (match_operand:SI 1 "sibcall_insn_operand" "s,U"))
(match_operand:SI 2 "" "")))]
"!TARGET_64BIT && SIBLING_CALL_P (insn)"
- "@
- jmp\t%P1
- jmp\t%A1"
+ { return ix86_output_call_insn (insn, operands[1], 1); }
[(set_attr "type" "callv")])
(define_insn "*call_value_1_rex64"
@@ -17268,11 +17215,7 @@
(match_operand:DI 2 "" "")))]
"TARGET_64BIT && !SIBLING_CALL_P (insn)
&& ix86_cmodel != CM_LARGE && ix86_cmodel != CM_LARGE_PIC"
-{
- if (constant_call_address_operand (operands[1], Pmode))
- return "call\t%P1";
- return "call\t%A1";
-}
+ { return ix86_output_call_insn (insn, operands[1], 1); }
[(set_attr "type" "callv")])
(define_insn "*call_value_1_rex64_ms_sysv"
@@ -17293,11 +17236,7 @@
(clobber (reg:DI SI_REG))
(clobber (reg:DI DI_REG))]
"TARGET_64BIT && !SIBLING_CALL_P (insn)"
-{
- if (constant_call_address_operand (operands[1], Pmode))
- return "call\t%P1";
- return "call\t%A1";
-}
+ { return ix86_output_call_insn (insn, operands[1], 1); }
[(set_attr "type" "callv")])
(define_insn "*call_value_1_rex64_large"
@@ -17305,7 +17244,7 @@
(call (mem:QI (match_operand:DI 1 "call_insn_operand" "rm"))
(match_operand:DI 2 "" "")))]
"TARGET_64BIT && !SIBLING_CALL_P (insn)"
- "call\t%A1"
+ { return ix86_output_call_insn (insn, operands[1], 1); }
[(set_attr "type" "callv")])
(define_insn "*sibcall_value_1_rex64"
@@ -17313,9 +17252,7 @@
(call (mem:QI (match_operand:DI 1 "sibcall_insn_operand" "s,U"))
(match_operand:DI 2 "" "")))]
"TARGET_64BIT && SIBLING_CALL_P (insn)"
- "@
- jmp\t%P1
- jmp\t%A1"
+ { return ix86_output_call_insn (insn, operands[1], 1); }
[(set_attr "type" "callv")])
;; We used to use "int $5", in honor of #BR which maps to interrupt vector 5.
diff --git a/gcc/config/i386/winnt.c b/gcc/config/i386/winnt.c
index 60a8b79d64a..45a736ad60e 100644
--- a/gcc/config/i386/winnt.c
+++ b/gcc/config/i386/winnt.c
@@ -36,6 +36,7 @@ along with GCC; see the file COPYING3. If not see
#include "langhooks.h"
#include "ggc.h"
#include "target.h"
+#include "except.h"
#include "lto-streamer.h"
/* i386/PE specific attribute support.
@@ -730,4 +731,384 @@ i386_pe_file_end (void)
}
}
+
+/* x64 Structured Exception Handling unwind info. */
+
+struct seh_frame_state
+{
+ /* SEH records saves relative to the "current" stack pointer, whether
+ or not there's a frame pointer in place. This tracks the current
+ stack pointer offset from the CFA. */
+ HOST_WIDE_INT sp_offset;
+
+ /* The CFA is located at CFA_REG + CFA_OFFSET. */
+ HOST_WIDE_INT cfa_offset;
+ rtx cfa_reg;
+};
+
+/* Set up data structures beginning output for SEH. */
+
+void
+i386_pe_seh_init (FILE *f)
+{
+ struct seh_frame_state *seh;
+
+ if (!TARGET_SEH)
+ return;
+ if (cfun->is_thunk)
+ return;
+
+ /* We cannot support DRAP with SEH. We turned off support for it by
+ re-defining MAX_STACK_ALIGNMENT when SEH is enabled. */
+ gcc_assert (!stack_realign_drap);
+
+ seh = XCNEW (struct seh_frame_state);
+ cfun->machine->seh = seh;
+
+ seh->sp_offset = INCOMING_FRAME_SP_OFFSET;
+ seh->cfa_offset = INCOMING_FRAME_SP_OFFSET;
+ seh->cfa_reg = stack_pointer_rtx;
+
+ 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)
+{
+ struct seh_frame_state *seh;
+
+ if (!TARGET_SEH)
+ return;
+ if (cfun->is_thunk)
+ return;
+ seh = cfun->machine->seh;
+
+ /* Emit an assembler directive to set up the frame pointer. Always do
+ this last. The documentation talks about doing this "before" any
+ other code that uses offsets, but (experimentally) that's after we
+ emit the codes in reverse order (handled by the assembler). */
+ if (seh->cfa_reg != stack_pointer_rtx)
+ {
+ HOST_WIDE_INT offset = seh->sp_offset - seh->cfa_offset;
+
+ gcc_assert ((offset & 15) == 0);
+ gcc_assert (IN_RANGE (offset, 0, 240));
+
+ fputs ("\t.seh_setframe\t", f);
+ print_reg (seh->cfa_reg, 0, f);
+ fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC "\n", offset);
+ }
+
+ XDELETE (seh);
+ cfun->machine->seh = NULL;
+
+ fputs ("\t.seh_endprologue\n", f);
+}
+
+static void
+i386_pe_seh_fini (FILE *f)
+{
+ if (!TARGET_SEH)
+ return;
+ if (cfun->is_thunk)
+ 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));
+
+ seh->sp_offset += UNITS_PER_WORD;
+ if (seh->cfa_reg == stack_pointer_rtx)
+ seh->cfa_offset += UNITS_PER_WORD;
+
+ 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);
+ HOST_WIDE_INT offset;
+
+ /* Negative save offsets are of course not supported, since that
+ would be a store below the stack pointer and thus clobberable. */
+ gcc_assert (seh->sp_offset >= cfa_offset);
+ offset = seh->sp_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);
+}
+
+/* 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_reg == stack_pointer_rtx)
+ seh->cfa_offset += offset;
+ seh->sp_offset += offset;
+
+ fprintf (f, "\t.seh_stackalloc\t" HOST_WIDE_INT_PRINT_DEC "\n", offset);
+}
+
+/* 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);
+ }
+ else if (GET_CODE (src) == MINUS)
+ {
+ reg_offset = -INTVAL (XEXP (src, 1));
+ src = XEXP (src, 0);
+ }
+ gcc_assert (src == stack_pointer_rtx);
+ gcc_assert (seh->cfa_reg == stack_pointer_rtx);
+ 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->cfa_reg = dest;
+ seh->cfa_offset -= reg_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 (dest == seh->cfa_reg);
+
+ seh_emit_save (f, seh, src, seh->cfa_offset - reg_offset);
+}
+
+/* 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) || npass == 1)
+ 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_cfa_adjust_cfa (f, seh, pat);
+ break;
+
+ case PLUS:
+ addend = INTVAL (XEXP (src, 1));
+ src = XEXP (src, 0);
+ if (dest == hard_frame_pointer_rtx)
+ 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;
+
+ 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);
+ gcc_checking_assert (REG_P (src));
+ 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;
+
+ /* 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;
+
+ if (NOTE_P (insn) || !RTX_FRAME_RELATED_P (insn))
+ 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:
+ case REG_CFA_EXPRESSION:
+ /* Only emitted with DRAP, which we disable. */
+ gcc_unreachable ();
+ break;
+
+ case REG_CFA_REGISTER:
+ /* Only emitted in epilogues, which we skip. */
+ gcc_unreachable ();
+
+ 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;
+
+ 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"
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index bf248dcba60..9209c0d01aa 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -2552,7 +2552,9 @@ dwarf2out_frame_debug_expr (rtx expr, const char *label)
regno = REGNO (XEXP (XEXP (dest, 0), 0));
- if (cfa_store.reg == (unsigned) regno)
+ if (cfa.reg == (unsigned) regno)
+ offset -= cfa.offset;
+ else if (cfa_store.reg == (unsigned) regno)
offset -= cfa_store.offset;
else
{
@@ -2568,7 +2570,9 @@ dwarf2out_frame_debug_expr (rtx expr, const char *label)
{
int regno = REGNO (XEXP (dest, 0));
- if (cfa_store.reg == (unsigned) regno)
+ if (cfa.reg == (unsigned) regno)
+ offset = -cfa.offset;
+ else if (cfa_store.reg == (unsigned) regno)
offset = -cfa_store.offset;
else
{