+2011-07-15 Eric Botcazou <ebotcazou@adacore.com>
+
+ PR target/48220
+ * doc/md.texi (Standard Names): Document window_save.
+ * cfgexpand.c (expand_debug_parm_decl): New function extracted from
+ expand_debug_expr and expand_debug_source_expr. If the target has
+ a window_save instruction, adjust the ENTRY_VALUE_EXP.
+ (expand_debug_expr) <SSA_NAME>: Call expand_debug_parm_decl if the
+ SSA_NAME_VAR is a parameter.
+ (expand_debug_source_expr) <PARM_DECL>: Call expand_debug_parm_decl.
+ * var-tracking.c (parm_reg_t): New type and associated vector type.
+ (windowed_parm_regs): New variable.
+ (adjust_insn): If the target has a window_save instruction and this
+ is the instruction, make its effect on parameter registers explicit.
+ (next_non_note_insn_var_location): New function.
+ (emit_notes_in_bb): Use it instead of NEXT_INSN throughout.
+ (vt_add_function_parameter): If the target has a window_save insn,
+ adjust the incoming RTL and record that in windowed_parm_regs.
+ (vt_finalize): Free windowed_parm_regs.
+
2011-07-15 Bernd Schmidt <bernds@codesourcery.com>
* doc/invoke.texi (C6X Options): New section.
return x;
}
-/* Return an RTX equivalent to the value of the tree expression
- EXP. */
+/* Return an RTX equivalent to the value of the parameter DECL. */
+
+static rtx
+expand_debug_parm_decl (tree decl)
+{
+ rtx incoming = DECL_INCOMING_RTL (decl);
+
+ if (incoming
+ && GET_MODE (incoming) != BLKmode
+ && ((REG_P (incoming) && HARD_REGISTER_P (incoming))
+ || (MEM_P (incoming)
+ && REG_P (XEXP (incoming, 0))
+ && HARD_REGISTER_P (XEXP (incoming, 0)))))
+ {
+ rtx rtl = gen_rtx_ENTRY_VALUE (GET_MODE (incoming));
+
+#ifdef HAVE_window_save
+ /* DECL_INCOMING_RTL uses the INCOMING_REGNO of parameter registers.
+ If the target machine has an explicit window save instruction, the
+ actual entry value is the corresponding OUTGOING_REGNO instead. */
+ if (REG_P (incoming)
+ && OUTGOING_REGNO (REGNO (incoming)) != REGNO (incoming))
+ incoming
+ = gen_rtx_REG_offset (incoming, GET_MODE (incoming),
+ OUTGOING_REGNO (REGNO (incoming)), 0);
+ else if (MEM_P (incoming))
+ {
+ rtx reg = XEXP (incoming, 0);
+ if (OUTGOING_REGNO (REGNO (reg)) != REGNO (reg))
+ {
+ reg = gen_raw_REG (GET_MODE (reg), OUTGOING_REGNO (REGNO (reg)));
+ incoming = replace_equiv_address_nv (incoming, reg);
+ }
+ }
+#endif
+
+ ENTRY_VALUE_EXP (rtl) = incoming;
+ return rtl;
+ }
+
+ if (incoming
+ && GET_MODE (incoming) != BLKmode
+ && !TREE_ADDRESSABLE (decl)
+ && MEM_P (incoming)
+ && (XEXP (incoming, 0) == virtual_incoming_args_rtx
+ || (GET_CODE (XEXP (incoming, 0)) == PLUS
+ && XEXP (XEXP (incoming, 0), 0) == virtual_incoming_args_rtx
+ && CONST_INT_P (XEXP (XEXP (incoming, 0), 1)))))
+ return incoming;
+
+ return NULL_RTX;
+}
+
+/* Return an RTX equivalent to the value of the tree expression EXP. */
static rtx
expand_debug_expr (tree exp)
if (SSA_NAME_IS_DEFAULT_DEF (exp)
&& TREE_CODE (SSA_NAME_VAR (exp)) == PARM_DECL)
{
- rtx incoming = DECL_INCOMING_RTL (SSA_NAME_VAR (exp));
- if (incoming
- && GET_MODE (incoming) != BLKmode
- && ((REG_P (incoming) && HARD_REGISTER_P (incoming))
- || (MEM_P (incoming)
- && REG_P (XEXP (incoming, 0))
- && HARD_REGISTER_P (XEXP (incoming, 0)))))
- {
- op0 = gen_rtx_ENTRY_VALUE (GET_MODE (incoming));
- ENTRY_VALUE_EXP (op0) = incoming;
- goto adjust_mode;
- }
- if (incoming
- && MEM_P (incoming)
- && !TREE_ADDRESSABLE (SSA_NAME_VAR (exp))
- && GET_MODE (incoming) != BLKmode
- && (XEXP (incoming, 0) == virtual_incoming_args_rtx
- || (GET_CODE (XEXP (incoming, 0)) == PLUS
- && XEXP (XEXP (incoming, 0), 0)
- == virtual_incoming_args_rtx
- && CONST_INT_P (XEXP (XEXP (incoming, 0),
- 1)))))
- {
- op0 = incoming;
- goto adjust_mode;
- }
+ op0 = expand_debug_parm_decl (SSA_NAME_VAR (exp));
+ if (op0)
+ goto adjust_mode;
op0 = expand_debug_expr (SSA_NAME_VAR (exp));
- if (!op0)
- return NULL;
- goto adjust_mode;
+ if (op0)
+ goto adjust_mode;
}
return NULL;
}
{
case PARM_DECL:
{
- rtx incoming = DECL_INCOMING_RTL (exp);
mode = DECL_MODE (exp);
- if (incoming
- && GET_MODE (incoming) != BLKmode
- && ((REG_P (incoming) && HARD_REGISTER_P (incoming))
- || (MEM_P (incoming)
- && REG_P (XEXP (incoming, 0))
- && HARD_REGISTER_P (XEXP (incoming, 0)))))
- {
- op0 = gen_rtx_ENTRY_VALUE (GET_MODE (incoming));
- ENTRY_VALUE_EXP (op0) = incoming;
- break;
- }
- if (incoming
- && MEM_P (incoming)
- && !TREE_ADDRESSABLE (exp)
- && GET_MODE (incoming) != BLKmode
- && (XEXP (incoming, 0) == virtual_incoming_args_rtx
- || (GET_CODE (XEXP (incoming, 0)) == PLUS
- && XEXP (XEXP (incoming, 0), 0)
- == virtual_incoming_args_rtx
- && CONST_INT_P (XEXP (XEXP (incoming, 0), 1)))))
- {
- op0 = incoming;
- break;
- }
+ op0 = expand_debug_parm_decl (exp);
+ if (op0)
+ break;
/* See if this isn't an argument that has been completely
optimized out. */
if (!DECL_RTL_SET_P (exp)
- && incoming == NULL_RTX
+ && !DECL_INCOMING_RTL (exp)
&& DECL_ABSTRACT_ORIGIN (current_function_decl))
{
tree aexp = exp;
The @code{prologue} pattern is particularly useful for targets which perform
instruction scheduling.
+@cindex @code{window_save} instruction pattern
+@anchor{window_save instruction pattern}
+@item @samp{window_save}
+This pattern, if defined, emits RTL for a register window save. It should
+be defined if the target machine has register windows but the window events
+are decoupled from calls to subroutines. The canonical example is the SPARC
+architecture.
+
@cindex @code{epilogue} instruction pattern
@anchor{epilogue instruction pattern}
@item @samp{epilogue}
operations.
The micro operations of one instruction are ordered so that
pre-modifying stack adjustment < use < use with no var < call insn <
- < set < clobber < post-modifying stack adjustment
+ < clobber < set < post-modifying stack adjustment
Then, a forward dataflow analysis is performed to find out how locations
of variables change through code and to propagate the variable locations
/* Scratch register bitmap used by cselib_expand_value_rtx. */
static bitmap scratch_regs = NULL;
+typedef struct GTY(()) parm_reg {
+ rtx outgoing;
+ rtx incoming;
+} parm_reg_t;
+
+DEF_VEC_O(parm_reg_t);
+DEF_VEC_ALLOC_O(parm_reg_t, gc);
+
+/* Vector of windowed parameter registers, if any. */
+static VEC(parm_reg_t, gc) *windowed_parm_regs = NULL;
+
/* Variable used to tell whether cselib_process_insn called our hook. */
static bool cselib_hook_called;
{
struct adjust_mem_data amd;
rtx set;
+
+#ifdef HAVE_window_save
+ /* If the target machine has an explicit window save instruction, the
+ transformation OUTGOING_REGNO -> INCOMING_REGNO is done there. */
+ if (RTX_FRAME_RELATED_P (insn)
+ && find_reg_note (insn, REG_CFA_WINDOW_SAVE, NULL_RTX))
+ {
+ unsigned int i, nregs = VEC_length(parm_reg_t, windowed_parm_regs);
+ rtx rtl = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (nregs * 2));
+ parm_reg_t *p;
+
+ FOR_EACH_VEC_ELT (parm_reg_t, windowed_parm_regs, i, p)
+ {
+ XVECEXP (rtl, 0, i * 2)
+ = gen_rtx_SET (VOIDmode, p->incoming, p->outgoing);
+ /* Do not clobber the attached DECL, but only the REG. */
+ XVECEXP (rtl, 0, i * 2 + 1)
+ = gen_rtx_CLOBBER (GET_MODE (p->outgoing),
+ gen_raw_REG (GET_MODE (p->outgoing),
+ REGNO (p->outgoing)));
+ }
+
+ validate_change (NULL_RTX, &PATTERN (insn), rtl, true);
+ return;
+ }
+#endif
+
amd.mem_mode = VOIDmode;
amd.stack_adjust = -VTI (bb)->out.stack_adjust;
amd.side_effects = NULL_RTX;
emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN, new_set->vars);
}
+/* Return the next insn after INSN that is not a NOTE_INSN_VAR_LOCATION. */
+
+static rtx
+next_non_note_insn_var_location (rtx insn)
+{
+ while (insn)
+ {
+ insn = NEXT_INSN (insn);
+ if (insn == 0
+ || !NOTE_P (insn)
+ || NOTE_KIND (insn) != NOTE_INSN_VAR_LOCATION)
+ break;
+ }
+
+ return insn;
+}
+
/* Emit the notes for changes of location parts in the basic block BB. */
static void
FOR_EACH_VEC_ELT (micro_operation, VTI (bb)->mos, i, mo)
{
rtx insn = mo->insn;
+ rtx next_insn = next_non_note_insn_var_location (insn);
switch (mo->type)
{
val_store (set, XEXP (reverse, 0), XEXP (reverse, 1),
insn, false);
- emit_notes_for_changes (NEXT_INSN (insn), EMIT_NOTE_BEFORE_INSN,
+ emit_notes_for_changes (next_insn, EMIT_NOTE_BEFORE_INSN,
set->vars);
}
break;
var_mem_delete_and_set (set, loc, true, VAR_INIT_STATUS_INITIALIZED,
set_src);
- emit_notes_for_changes (NEXT_INSN (insn), EMIT_NOTE_BEFORE_INSN,
+ emit_notes_for_changes (next_insn, EMIT_NOTE_BEFORE_INSN,
set->vars);
}
break;
else
var_mem_delete_and_set (set, loc, false, src_status, set_src);
- emit_notes_for_changes (NEXT_INSN (insn), EMIT_NOTE_BEFORE_INSN,
+ emit_notes_for_changes (next_insn, EMIT_NOTE_BEFORE_INSN,
set->vars);
}
break;
else
var_mem_delete (set, loc, true);
- emit_notes_for_changes (NEXT_INSN (insn), EMIT_NOTE_BEFORE_INSN,
+ emit_notes_for_changes (next_insn, EMIT_NOTE_BEFORE_INSN,
set->vars);
}
break;
plus_constant (arg_pointer_rtx, off));
}
+#ifdef HAVE_window_save
+ /* DECL_INCOMING_RTL uses the INCOMING_REGNO of parameter registers.
+ If the target machine has an explicit window save instruction, the
+ actual entry value is the corresponding OUTGOING_REGNO instead. */
+ if (REG_P (incoming)
+ && HARD_REGISTER_P (incoming)
+ && OUTGOING_REGNO (REGNO (incoming)) != REGNO (incoming))
+ {
+ parm_reg_t *p
+ = VEC_safe_push (parm_reg_t, gc, windowed_parm_regs, NULL);
+ p->incoming = incoming;
+ incoming
+ = gen_rtx_REG_offset (incoming, GET_MODE (incoming),
+ OUTGOING_REGNO (REGNO (incoming)), 0);
+ p->outgoing = incoming;
+ }
+ else if (MEM_P (incoming)
+ && REG_P (XEXP (incoming, 0))
+ && HARD_REGISTER_P (XEXP (incoming, 0)))
+ {
+ rtx reg = XEXP (incoming, 0);
+ if (OUTGOING_REGNO (REGNO (reg)) != REGNO (reg))
+ {
+ parm_reg_t *p
+ = VEC_safe_push (parm_reg_t, gc, windowed_parm_regs, NULL);
+ p->incoming = reg;
+ reg = gen_raw_REG (GET_MODE (reg), OUTGOING_REGNO (REGNO (reg)));
+ p->outgoing = reg;
+ incoming = replace_equiv_address_nv (incoming, reg);
+ }
+ }
+#endif
+
if (!vt_get_decl_and_offset (incoming, &decl, &offset))
{
if (REG_P (incoming) || MEM_P (incoming))
cselib_finish ();
BITMAP_FREE (scratch_regs);
scratch_regs = NULL;
+ VEC_free (parm_reg_t, gc, windowed_parm_regs);
}
if (vui_vec)