* 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
+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
#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)
{ \
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__))"); \
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. */
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);
#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);
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;
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;
}
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;
}
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;
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. */
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.
/* 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 */
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
/* 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
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);
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)));
}
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)
/* 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. */
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
}
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)
{
/* 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. */
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));
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";
+ }
+}
\f
/* Clear stack slot assignments remembered from previous functions.
This is called from INIT_EXPANDERS once before RTL is emitted for each
/* 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
{
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;
/* 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
[(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"
(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"
(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
(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"
(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"
(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"
(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"
(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"
(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"
(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"
(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"
(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"
(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"
(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"
(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")])
\f
;; We used to use "int $5", in honor of #BR which maps to interrupt vector 5.
#include "langhooks.h"
#include "ggc.h"
#include "target.h"
+#include "except.h"
#include "lto-streamer.h"
/* i386/PE specific attribute support.
}
}
+\f
+/* 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);
+}
+\f
+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);
+}
+\f
+
#include "gt-winnt.h"
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
{
{
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
{