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:
Richard Henderson 2010-10-31 17:40:33 -07:00 committed by Richard Henderson
parent b839050487
commit f81c977403
8 changed files with 677 additions and 116 deletions

View file

@ -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

View file

@ -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. */

View 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);

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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"

View file

@ -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
{