You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to the Free
- Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA. */
+ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
/* This pass converts stack-like registers from the "flat register
file" model that gcc uses, to a stack convention that the 387 uses.
#include "varray.h"
#include "reload.h"
#include "ggc.h"
+#include "timevar.h"
+#include "tree-pass.h"
+#include "target.h"
+#include "vecprim.h"
+
+#ifdef STACK_REGS
/* We use this array to cache info about insns, because otherwise we
spend too much time in stack_regs_mentioned_p.
Indexed by insn UIDs. A value of zero is uninitialized, one indicates
the insn uses stack registers, two indicates the insn does not use
stack registers. */
-static GTY(()) varray_type stack_regs_mentioned_data;
-
-#ifdef STACK_REGS
+static VEC(char,heap) *stack_regs_mentioned_data;
#define REG_STACK_SIZE (LAST_STACK_REG - FIRST_STACK_REG + 1)
+int regstack_completed = 0;
+
/* This is the basic stack record. TOP is an index into REG[] such
that REG[TOP] is the top of stack. If TOP is -1 the stack is empty.
struct stack_def stack_out; /* Output stack configuration. */
HARD_REG_SET out_reg_set; /* Stack regs live on output. */
int done; /* True if block already converted. */
- int predecessors; /* Number of predecessors that needs
+ int predecessors; /* Number of predecessors that need
to be visited. */
} *block_info;
static basic_block current_block;
/* In the current_block, whether we're processing the first register
- stack or call instruction, i.e. the the regstack is currently the
+ stack or call instruction, i.e. the regstack is currently the
same as BLOCK_INFO(current_block)->stack_in. */
static bool starting_stack_p;
return 0;
uid = INSN_UID (insn);
- max = VARRAY_SIZE (stack_regs_mentioned_data);
+ max = VEC_length (char, stack_regs_mentioned_data);
if (uid >= max)
{
+ char *p;
+ unsigned int old_max = max;
+
/* Allocate some extra size to avoid too many reallocs, but
do not grow too quickly. */
- max = uid + uid / 20;
- VARRAY_GROW (stack_regs_mentioned_data, max);
+ max = uid + uid / 20 + 1;
+ VEC_safe_grow (char, heap, stack_regs_mentioned_data, max);
+ p = VEC_address (char, stack_regs_mentioned_data);
+ memset (&p[old_max], 0,
+ sizeof (char) * (max - old_max));
}
- test = VARRAY_CHAR (stack_regs_mentioned_data, uid);
+ test = VEC_index (char, stack_regs_mentioned_data, uid);
if (test == 0)
{
/* This insn has yet to be examined. Do so now. */
test = stack_regs_mentioned_p (PATTERN (insn)) ? 1 : 2;
- VARRAY_CHAR (stack_regs_mentioned_data, uid) = test;
+ VEC_replace (char, stack_regs_mentioned_data, uid, test);
}
return test == 1;
pat = & XEXP (*pat, 0);
break;
+ case UNSPEC:
+ if (XINT (*pat, 1) == UNSPEC_TRUNC_NOOP)
+ pat = & XVECEXP (*pat, 0, 0);
+ return pat;
+
case FLOAT_TRUNCATE:
if (!flag_unsafe_math_optimizations)
return pat;
result = DECL_RTL_IF_SET (DECL_RESULT (decl));
if (result != 0)
- {
-#ifdef FUNCTION_OUTGOING_VALUE
- result
- = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (decl)), decl);
-#else
- result = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (decl)), decl);
-#endif
- }
+ result = targetm.calls.function_value (TREE_TYPE (DECL_RESULT (decl)),
+ decl, true);
return result != 0 && STACK_REG_P (result) ? result : 0;
}
gcc_assert (regno <= LAST_STACK_REG);
gcc_assert (STACK_REG_P (*reg));
- gcc_assert (GET_MODE_CLASS (GET_MODE (*reg)) == MODE_FLOAT
+ gcc_assert (SCALAR_FLOAT_MODE_P (GET_MODE (*reg))
|| GET_MODE_CLASS (GET_MODE (*reg)) == MODE_COMPLEX_FLOAT);
*reg = FP_MODE_REG (regno, GET_MODE (*reg));
if (!note)
{
rtx t = *dest;
- if (get_hard_regnum (regstack, t) == -1)
- control_flow_insn_deleted
- |= move_nan_for_stack_reg (insn, regstack, t);
if (COMPLEX_MODE_P (GET_MODE (t)))
{
- t = FP_MODE_REG (REGNO (t) + 1, DFmode);
- if (get_hard_regnum (regstack, t) == -1)
- control_flow_insn_deleted
- |= move_nan_for_stack_reg (insn, regstack, t);
+ rtx u = FP_MODE_REG (REGNO (t) + 1, SFmode);
+ if (get_hard_regnum (regstack, u) == -1)
+ {
+ rtx pat2 = gen_rtx_CLOBBER (VOIDmode, u);
+ rtx insn2 = emit_insn_before (pat2, insn);
+ control_flow_insn_deleted
+ |= move_nan_for_stack_reg (insn2, regstack, u);
+ }
}
+ if (get_hard_regnum (regstack, t) == -1)
+ control_flow_insn_deleted
+ |= move_nan_for_stack_reg (insn, regstack, t);
}
}
}
/* Push the result back onto stack. Empty stack slot
will be filled in second part of insn. */
- if (STACK_REG_P (*dest)) {
- regstack->reg[regstack->top] = REGNO (*dest);
- SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
- replace_reg (dest, FIRST_STACK_REG);
- }
+ if (STACK_REG_P (*dest))
+ {
+ regstack->reg[regstack->top] = REGNO (*dest);
+ SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
+ replace_reg (dest, FIRST_STACK_REG);
+ }
replace_reg (src1, FIRST_STACK_REG);
replace_reg (src2, FIRST_STACK_REG + 1);
/* Push the result back onto stack. Fill empty slot from
first part of insn and fix top of stack pointer. */
- if (STACK_REG_P (*dest)) {
- regstack->reg[regstack->top - 1] = REGNO (*dest);
- SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
- replace_reg (dest, FIRST_STACK_REG + 1);
- }
+ if (STACK_REG_P (*dest))
+ {
+ regstack->reg[regstack->top - 1] = REGNO (*dest);
+ SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
+ replace_reg (dest, FIRST_STACK_REG + 1);
+ }
replace_reg (src1, FIRST_STACK_REG);
replace_reg (src2, FIRST_STACK_REG + 1);
/* Push the result back onto stack. Empty stack slot
will be filled in second part of insn. */
- if (STACK_REG_P (*dest)) {
- regstack->reg[regstack->top + 1] = REGNO (*dest);
- SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
- replace_reg (dest, FIRST_STACK_REG);
- }
+ if (STACK_REG_P (*dest))
+ {
+ regstack->reg[regstack->top + 1] = REGNO (*dest);
+ SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
+ replace_reg (dest, FIRST_STACK_REG);
+ }
replace_reg (src1, FIRST_STACK_REG);
break;
/* Push the result back onto stack. Fill empty slot from
first part of insn and fix top of stack pointer. */
- if (STACK_REG_P (*dest)) {
- regstack->reg[regstack->top] = REGNO (*dest);
- SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
- replace_reg (dest, FIRST_STACK_REG + 1);
+ if (STACK_REG_P (*dest))
+ {
+ regstack->reg[regstack->top] = REGNO (*dest);
+ SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
+ replace_reg (dest, FIRST_STACK_REG + 1);
- regstack->top++;
- }
+ regstack->top++;
+ }
replace_reg (src1, FIRST_STACK_REG);
break;
if (NOTE_P (insn) || INSN_DELETED_P (insn))
return control_flow_insn_deleted;
+ /* If this a noreturn call, we can't insert pop insns after it.
+ Instead, reset the stack state to empty. */
+ if (CALL_P (insn)
+ && find_reg_note (insn, REG_NORETURN, NULL))
+ {
+ regstack->top = -1;
+ CLEAR_HARD_REG_SET (regstack->reg_set);
+ return control_flow_insn_deleted;
+ }
+
/* If there is a REG_UNUSED note on a stack register on this insn,
the indicated reg must be popped. The REG_UNUSED note is removed,
since the form of the newly emitted pop insn references the reg,
should have been defined by now. */
static bool
-compensate_edge (edge e, FILE *file)
+compensate_edge (edge e)
{
- basic_block block = e->src, target = e->dest;
- block_info bi = BLOCK_INFO (block);
- struct stack_def regstack, tmpstack;
+ basic_block source = e->src, target = e->dest;
stack target_stack = &BLOCK_INFO (target)->stack_in;
+ stack source_stack = &BLOCK_INFO (source)->stack_out;
+ struct stack_def regstack;
int reg;
- current_block = block;
- regstack = bi->stack_out;
- if (file)
- fprintf (file, "Edge %d->%d: ", block->index, target->index);
+ if (dump_file)
+ fprintf (dump_file, "Edge %d->%d: ", source->index, target->index);
gcc_assert (target_stack->top != -2);
/* Check whether stacks are identical. */
- if (target_stack->top == regstack.top)
+ if (target_stack->top == source_stack->top)
{
for (reg = target_stack->top; reg >= 0; --reg)
- if (target_stack->reg[reg] != regstack.reg[reg])
+ if (target_stack->reg[reg] != source_stack->reg[reg])
break;
if (reg == -1)
{
- if (file)
- fprintf (file, "no changes needed\n");
+ if (dump_file)
+ fprintf (dump_file, "no changes needed\n");
return false;
}
}
- if (file)
+ if (dump_file)
{
- fprintf (file, "correcting stack to ");
- print_stack (file, target_stack);
+ fprintf (dump_file, "correcting stack to ");
+ print_stack (dump_file, target_stack);
}
- /* Care for non-call EH edges specially. The normal return path have
- values in registers. These will be popped en masse by the unwind
- library. */
- if ((e->flags & (EDGE_EH | EDGE_ABNORMAL_CALL)) == EDGE_EH)
- target_stack->top = -1;
-
- /* Other calls may appear to have values live in st(0), but the
+ /* Abnormal calls may appear to have values live in st(0), but the
abnormal return path will not have actually loaded the values. */
- else if (e->flags & EDGE_ABNORMAL_CALL)
+ if (e->flags & EDGE_ABNORMAL_CALL)
{
/* Assert that the lifetimes are as we expect -- one value
live at st(0) on the end of the source block, and no
- values live at the beginning of the destination block. */
- HARD_REG_SET tmp;
-
- CLEAR_HARD_REG_SET (tmp);
- GO_IF_HARD_REG_EQUAL (target_stack->reg_set, tmp, eh1);
- gcc_unreachable ();
- eh1:
-
- /* We are sure that there is st(0) live, otherwise we won't compensate.
+ values live at the beginning of the destination block.
For complex return values, we may have st(1) live as well. */
- SET_HARD_REG_BIT (tmp, FIRST_STACK_REG);
- if (TEST_HARD_REG_BIT (regstack.reg_set, FIRST_STACK_REG + 1))
- SET_HARD_REG_BIT (tmp, FIRST_STACK_REG + 1);
- GO_IF_HARD_REG_EQUAL (regstack.reg_set, tmp, eh2);
- gcc_unreachable ();
- eh2:
+ gcc_assert (source_stack->top == 0 || source_stack->top == 1);
+ gcc_assert (target_stack->top == -1);
+ return false;
+ }
- target_stack->top = -1;
+ /* Handle non-call EH edges specially. The normal return path have
+ values in registers. These will be popped en masse by the unwind
+ library. */
+ if (e->flags & EDGE_EH)
+ {
+ gcc_assert (target_stack->top == -1);
+ return false;
}
+ /* We don't support abnormal edges. Global takes care to
+ avoid any live register across them, so we should never
+ have to insert instructions on such edges. */
+ gcc_assert (! (e->flags & EDGE_ABNORMAL));
+
+ /* Make a copy of source_stack as change_stack is destructive. */
+ regstack = *source_stack;
+
/* It is better to output directly to the end of the block
instead of to the edge, because emit_swap can do minimal
insn scheduling. We can do this when there is only one
edge out, and it is not abnormal. */
- else if (EDGE_COUNT (block->succs) == 1 && !(e->flags & EDGE_ABNORMAL))
+ if (EDGE_COUNT (source->succs) == 1)
{
- /* change_stack kills values in regstack. */
- tmpstack = regstack;
-
- change_stack (BB_END (block), &tmpstack, target_stack,
- (JUMP_P (BB_END (block))
- ? EMIT_BEFORE : EMIT_AFTER));
+ current_block = source;
+ change_stack (BB_END (source), ®stack, target_stack,
+ (JUMP_P (BB_END (source)) ? EMIT_BEFORE : EMIT_AFTER));
}
else
{
rtx seq, after;
- /* We don't support abnormal edges. Global takes care to
- avoid any live register across them, so we should never
- have to insert instructions on such edges. */
- gcc_assert (!(e->flags & EDGE_ABNORMAL));
-
current_block = NULL;
start_sequence ();
/* ??? change_stack needs some point to emit insns after. */
after = emit_note (NOTE_INSN_DELETED);
- tmpstack = regstack;
- change_stack (after, &tmpstack, target_stack, EMIT_BEFORE);
+ change_stack (after, ®stack, target_stack, EMIT_BEFORE);
seq = get_insns ();
end_sequence ();
source block to the stack_in of the destination block. */
static bool
-compensate_edges (FILE *file)
+compensate_edges (void)
{
bool inserted = false;
basic_block bb;
edge_iterator ei;
FOR_EACH_EDGE (e, ei, bb->succs)
- inserted |= compensate_edge (e, file);
+ inserted |= compensate_edge (e);
}
return inserted;
}
if (EDGE_CRITICAL_P (e1) != EDGE_CRITICAL_P (e2))
return EDGE_CRITICAL_P (e1) ? e1 : e2;
- /* Avoid non-deterministic behaviour. */
+ /* Avoid non-deterministic behavior. */
return (e1->src->index < e2->src->index) ? e1 : e2;
}
/* Convert stack register references in one block. */
static void
-convert_regs_1 (FILE *file, basic_block block)
+convert_regs_1 (basic_block block)
{
struct stack_def regstack;
block_info bi = BLOCK_INFO (block);
}
}
- if (file)
+ if (dump_file)
{
- fprintf (file, "\nBasic block %d\nInput stack: ", block->index);
- print_stack (file, &bi->stack_in);
+ fprintf (dump_file, "\nBasic block %d\nInput stack: ", block->index);
+ print_stack (dump_file, &bi->stack_in);
}
/* Process all insns in this block. Keep track of NEXT so that we
if (stack_regs_mentioned (insn)
|| CALL_P (insn))
{
- if (file)
+ if (dump_file)
{
- fprintf (file, " insn %d input stack: ",
+ fprintf (dump_file, " insn %d input stack: ",
INSN_UID (insn));
- print_stack (file, ®stack);
+ print_stack (dump_file, ®stack);
}
control_flow_insn_deleted |= subst_stack_regs (insn, ®stack);
starting_stack_p = false;
}
while (next);
- if (file)
+ if (dump_file)
{
- fprintf (file, "Expected live registers [");
+ fprintf (dump_file, "Expected live registers [");
for (reg = FIRST_STACK_REG; reg <= LAST_STACK_REG; ++reg)
if (TEST_HARD_REG_BIT (bi->out_reg_set, reg))
- fprintf (file, " %d", reg);
- fprintf (file, " ]\nOutput stack: ");
- print_stack (file, ®stack);
+ fprintf (dump_file, " %d", reg);
+ fprintf (dump_file, " ]\nOutput stack: ");
+ print_stack (dump_file, ®stack);
}
insn = BB_END (block);
{
rtx set;
- if (file)
- fprintf (file, "Emitting insn initializing reg %d\n", reg);
+ if (dump_file)
+ fprintf (dump_file, "Emitting insn initializing reg %d\n", reg);
set = gen_rtx_SET (VOIDmode, FP_MODE_REG (reg, SFmode), not_a_num);
insn = emit_insn_after (set, insn);
gcc_assert (any_malformed_asm);
win:
bi->stack_out = regstack;
+ bi->done = true;
}
/* Convert registers in all blocks reachable from BLOCK. */
static void
-convert_regs_2 (FILE *file, basic_block block)
+convert_regs_2 (basic_block block)
{
basic_block *stack, *sp;
is only processed after all its predecessors. The number of predecessors
of every block has already been computed. */
- stack = xmalloc (sizeof (*stack) * n_basic_blocks);
+ stack = XNEWVEC (basic_block, n_basic_blocks);
sp = stack;
*sp++ = block;
*sp++ = e->dest;
}
- convert_regs_1 (file, block);
- BLOCK_INFO (block)->done = 1;
+ convert_regs_1 (block);
}
while (sp != stack);
to the stack-like registers the 387 uses. */
static void
-convert_regs (FILE *file)
+convert_regs (void)
{
int inserted;
basic_block b;
/* Process all blocks reachable from all entry points. */
FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
- convert_regs_2 (file, e->dest);
+ convert_regs_2 (e->dest);
/* ??? Process all unreachable blocks. Though there's no excuse
for keeping these even when not optimizing. */
block_info bi = BLOCK_INFO (b);
if (! bi->done)
- convert_regs_2 (file, b);
+ convert_regs_2 (b);
}
- inserted |= compensate_edges (file);
+ inserted |= compensate_edges ();
clear_aux_for_blocks ();
if (inserted)
commit_edge_insertions ();
- if (file)
- fputc ('\n', file);
+ if (dump_file)
+ fputc ('\n', dump_file);
}
\f
/* Convert register usage from "flat" register file usage to a "stack
code duplication created when the converter inserts pop insns on
the edges. */
-bool
-reg_to_stack (FILE *file)
+static bool
+reg_to_stack (void)
{
basic_block bb;
int i;
int max_uid;
/* Clean up previous run. */
- stack_regs_mentioned_data = 0;
+ if (stack_regs_mentioned_data != NULL)
+ VEC_free (char, heap, stack_regs_mentioned_data);
/* See if there is something to do. Flow analysis is quite
expensive so we might save some compilation time. */
Also need to rebuild life when superblock scheduling is done
as it don't update liveness yet. */
if (!optimize
- || (flag_sched2_use_superblocks
+ || ((flag_sched2_use_superblocks || flag_sched2_use_traces)
&& flag_schedule_insns_after_reload))
{
count_or_remove_death_notes (NULL, 1);
- life_analysis (file, PROP_DEATH_NOTES);
+ life_analysis (PROP_DEATH_NOTES);
}
mark_dfs_back_edges ();
/* Copy live_at_end and live_at_start into temporaries. */
for (reg = FIRST_STACK_REG; reg <= LAST_STACK_REG; reg++)
{
- if (REGNO_REG_SET_P (bb->global_live_at_end, reg))
+ if (REGNO_REG_SET_P (bb->il.rtl->global_live_at_end, reg))
SET_HARD_REG_BIT (bi->out_reg_set, reg);
- if (REGNO_REG_SET_P (bb->global_live_at_start, reg))
+ if (REGNO_REG_SET_P (bb->il.rtl->global_live_at_start, reg))
SET_HARD_REG_BIT (bi->stack_in.reg_set, reg);
}
}
/* Allocate a cache for stack_regs_mentioned. */
max_uid = get_max_uid ();
- VARRAY_CHAR_INIT (stack_regs_mentioned_data, max_uid + 1,
- "stack_regs_mentioned cache");
+ stack_regs_mentioned_data = VEC_alloc (char, heap, max_uid + 1);
+ memset (VEC_address (char, stack_regs_mentioned_data),
+ 0, sizeof (char) * max_uid + 1);
- convert_regs (file);
+ convert_regs ();
free_aux_for_blocks ();
return true;
}
#endif /* STACK_REGS */
+\f
+static bool
+gate_handle_stack_regs (void)
+{
+#ifdef STACK_REGS
+ return 1;
+#else
+ return 0;
+#endif
+}
-#include "gt-reg-stack.h"
+/* Convert register usage from flat register file usage to a stack
+ register file. */
+static unsigned int
+rest_of_handle_stack_regs (void)
+{
+#ifdef STACK_REGS
+ if (reg_to_stack () && optimize)
+ {
+ regstack_completed = 1;
+ if (cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_POST_REGSTACK
+ | (flag_crossjumping ? CLEANUP_CROSSJUMP : 0))
+ && (flag_reorder_blocks || flag_reorder_blocks_and_partition))
+ {
+ reorder_basic_blocks (0);
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_POST_REGSTACK);
+ }
+ }
+ else
+ regstack_completed = 1;
+#endif
+ return 0;
+}
+
+struct tree_opt_pass pass_stack_regs =
+{
+ "stack", /* name */
+ gate_handle_stack_regs, /* gate */
+ rest_of_handle_stack_regs, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_REG_STACK, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_dump_func |
+ TODO_ggc_collect, /* todo_flags_finish */
+ 'k' /* letter */
+};