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. From-SVN: r166119
This commit is contained in:
parent
b839050487
commit
f81c977403
8 changed files with 677 additions and 116 deletions
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue