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.
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;
/* The block we're currently working on. */
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
+ same as BLOCK_INFO(current_block)->stack_in. */
+static bool starting_stack_p;
+
/* This is the register file for all register after conversion. */
static rtx
FP_mode_reg[LAST_STACK_REG+1-FIRST_STACK_REG][(int) MAX_MACHINE_MODE];
/* Forward declarations */
static int stack_regs_mentioned_p (rtx pat);
-static void straighten_stack (rtx, stack);
static void pop_stack (stack, int);
static rtx *get_true_reg (rtx *);
static void remove_regno_note (rtx, enum reg_note, unsigned int);
static int get_hard_regnum (stack, rtx);
static rtx emit_pop_insn (rtx, stack, rtx, enum emit_where);
-static void emit_swap_insn (rtx, stack, rtx);
static void swap_to_top(rtx, stack, rtx, rtx);
static bool move_for_stack_reg (rtx, stack, rtx);
static bool move_nan_for_stack_reg (rtx, stack, rtx);
static void change_stack (rtx, stack, stack, enum emit_where);
static void print_stack (FILE *, stack);
static rtx next_flags_user (rtx);
-static bool compensate_edge (edge, FILE *);
\f
/* Return nonzero if any stack register is mentioned somewhere within PAT. */
return NULL_RTX;
}
\f
-/* Reorganize the stack into ascending numbers,
- after this insn. */
+/* Reorganize the stack into ascending numbers, before this insn. */
static void
straighten_stack (rtx insn, stack regstack)
for (top = temp_stack.top = regstack->top; top >= 0; top--)
temp_stack.reg[top] = FIRST_STACK_REG + temp_stack.top - top;
- change_stack (insn, regstack, &temp_stack, EMIT_AFTER);
+ change_stack (insn, regstack, &temp_stack, EMIT_BEFORE);
}
/* Pop a register from the stack. */
return;
}
+ /* Avoid emitting the swap if this is the first register stack insn
+ of the current_block. Instead update the current_block's stack_in
+ and let compensate edges take care of this for us. */
+ if (current_block && starting_stack_p)
+ {
+ BLOCK_INFO (current_block)->stack_in = *regstack;
+ starting_stack_p = false;
+ return;
+ }
+
swap_rtx = gen_swapxf (FP_MODE_REG (hard_regno, XFmode),
FP_MODE_REG (FIRST_STACK_REG, XFmode));
if (top >= 0)
{
- straighten_stack (PREV_INSN (insn), regstack);
+ straighten_stack (insn, regstack);
/* Now mark the arguments as dead after the call. */
int reg;
int update_end = 0;
+ /* Stack adjustments for the first insn in a block update the
+ current_block's stack_in instead of inserting insns directly.
+ compensate_edges will add the necessary code later. */
+ if (current_block
+ && starting_stack_p
+ && where == EMIT_BEFORE)
+ {
+ BLOCK_INFO (current_block)->stack_in = *new;
+ starting_stack_p = false;
+ *old = *new;
+ return;
+ }
+
/* We will be inserting new insns "backwards". If we are to insert
after INSN, find the next insn, and insert before it. */
}
}
-/* Adjust the stack of this block on exit to match the stack of the
- target block, or copy stack info into the stack of the successor
- of the successor hasn't been processed yet. */
+/* Copy the stack info from the end of edge E's source block to the
+ start of E's destination block. */
+
+static void
+propagate_stack (edge e)
+{
+ stack src_stack = &BLOCK_INFO (e->src)->stack_out;
+ stack dest_stack = &BLOCK_INFO (e->dest)->stack_in;
+ int reg;
+
+ /* Preserve the order of the original stack, but check whether
+ any pops are needed. */
+ dest_stack->top = -1;
+ for (reg = 0; reg <= src_stack->top; ++reg)
+ if (TEST_HARD_REG_BIT (dest_stack->reg_set, src_stack->reg[reg]))
+ dest_stack->reg[++dest_stack->top] = src_stack->reg[reg];
+}
+
+
+/* Adjust the stack of edge E's source block on exit to match the stack
+ of it's target block upon input. The stack layouts of both blocks
+ should have been defined by now. */
+
static bool
compensate_edge (edge e, FILE *file)
{
- 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);
+ fprintf (file, "Edge %d->%d: ", source->index, target->index);
+
+ gcc_assert (target_stack->top != -2);
- if (target_stack->top == -2)
+ /* Check whether stacks are identical. */
+ if (target_stack->top == source_stack->top)
{
- /* The target block hasn't had a stack order selected.
- We need merely ensure that no pops are needed. */
- for (reg = regstack.top; reg >= 0; --reg)
- if (!TEST_HARD_REG_BIT (target_stack->reg_set, regstack.reg[reg]))
+ for (reg = target_stack->top; reg >= 0; --reg)
+ if (target_stack->reg[reg] != source_stack->reg[reg])
break;
if (reg == -1)
{
if (file)
- fprintf (file, "new block; copying stack position\n");
-
- /* change_stack kills values in regstack. */
- tmpstack = regstack;
-
- change_stack (BB_END (block), &tmpstack, target_stack, EMIT_AFTER);
+ fprintf (file, "no changes needed\n");
return false;
}
-
- if (file)
- fprintf (file, "new block; pops needed\n");
}
- else
- {
- if (target_stack->top == regstack.top)
- {
- for (reg = target_stack->top; reg >= 0; --reg)
- if (target_stack->reg[reg] != regstack.reg[reg])
- break;
- if (reg == -1)
- {
- if (file)
- fprintf (file, "no changes needed\n");
- return false;
- }
- }
-
- if (file)
- {
- fprintf (file, "correcting stack to ");
- print_stack (file, target_stack);
- }
+ if (file)
+ {
+ fprintf (file, "correcting stack to ");
+ print_stack (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 ();
return false;
}
+/* Traverse all non-entry edges in the CFG, and emit the necessary
+ edge compensation code to change the stack from stack_out of the
+ source block to the stack_in of the destination block. */
+
+static bool
+compensate_edges (FILE *file)
+{
+ bool inserted = false;
+ basic_block bb;
+
+ starting_stack_p = false;
+
+ FOR_EACH_BB (bb)
+ if (bb != ENTRY_BLOCK_PTR)
+ {
+ edge e;
+ edge_iterator ei;
+
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ inserted |= compensate_edge (e, file);
+ }
+ return inserted;
+}
+
+/* Select the better of two edges E1 and E2 to use to determine the
+ stack layout for their shared destination basic block. This is
+ typically the more frequently executed. The edge E1 may be NULL
+ (in which case E2 is returned), but E2 is always non-NULL. */
+
+static edge
+better_edge (edge e1, edge e2)
+{
+ if (!e1)
+ return e2;
+
+ if (EDGE_FREQUENCY (e1) > EDGE_FREQUENCY (e2))
+ return e1;
+ if (EDGE_FREQUENCY (e1) < EDGE_FREQUENCY (e2))
+ return e2;
+
+ if (e1->count > e2->count)
+ return e1;
+ if (e1->count < e2->count)
+ return e2;
+
+ /* Prefer critical edges to minimize inserting compensation code on
+ critical edges. */
+
+ if (EDGE_CRITICAL_P (e1) != EDGE_CRITICAL_P (e2))
+ return EDGE_CRITICAL_P (e1) ? e1 : e2;
+
+ /* Avoid non-deterministic behaviour. */
+ return (e1->src->index < e2->src->index) ? e1 : e2;
+}
+
/* Convert stack register references in one block. */
-static int
+static void
convert_regs_1 (FILE *file, basic_block block)
{
struct stack_def regstack;
block_info bi = BLOCK_INFO (block);
- int inserted, reg;
+ int reg;
rtx insn, next;
- edge e, beste = NULL;
bool control_flow_insn_deleted = false;
- edge_iterator ei;
- inserted = 0;
any_malformed_asm = false;
- /* Find the edge we will copy stack from. It should be the most frequent
- one as it will get cheapest after compensation code is generated,
- if multiple such exists, take one with largest count, prefer critical
- one (as splitting critical edges is more expensive), or one with lowest
- index, to avoid random changes with different orders of the edges. */
- FOR_EACH_EDGE (e, ei, block->preds)
- {
- if (e->flags & EDGE_DFS_BACK)
- ;
- else if (! beste)
- beste = e;
- else if (EDGE_FREQUENCY (beste) < EDGE_FREQUENCY (e))
- beste = e;
- else if (EDGE_FREQUENCY (beste) > EDGE_FREQUENCY (e))
- ;
- else if (beste->count < e->count)
- beste = e;
- else if (beste->count > e->count)
- ;
- else if ((EDGE_CRITICAL_P (e) != 0)
- != (EDGE_CRITICAL_P (beste) != 0))
- {
- if (EDGE_CRITICAL_P (e))
- beste = e;
- }
- else if (e->src->index < beste->src->index)
- beste = e;
- }
-
- /* Initialize stack at block entry. */
+ /* Choose an initial stack layout, if one hasn't already been chosen. */
if (bi->stack_in.top == -2)
{
+ edge e, beste = NULL;
+ edge_iterator ei;
+
+ /* Select the best incoming edge (typically the most frequent) to
+ use as a template for this basic block. */
+ FOR_EACH_EDGE (e, ei, block->preds)
+ if (BLOCK_INFO (e->src)->done)
+ beste = better_edge (beste, e);
+
if (beste)
- inserted |= compensate_edge (beste, file);
+ propagate_stack (beste);
else
{
/* No predecessors. Create an arbitrary input stack. */
- int reg;
-
bi->stack_in.top = -1;
for (reg = LAST_STACK_REG; reg >= FIRST_STACK_REG; --reg)
if (TEST_HARD_REG_BIT (bi->stack_in.reg_set, reg))
bi->stack_in.reg[++bi->stack_in.top] = reg;
}
}
- else
- /* Entry blocks do have stack already initialized. */
- beste = NULL;
-
- current_block = block;
if (file)
{
/* Process all insns in this block. Keep track of NEXT so that we
don't process insns emitted while substituting in INSN. */
+ current_block = block;
next = BB_HEAD (block);
regstack = bi->stack_in;
+ starting_stack_p = true;
+
do
{
insn = next;
print_stack (file, ®stack);
}
control_flow_insn_deleted |= subst_stack_regs (insn, ®stack);
+ starting_stack_p = false;
}
}
while (next);
gcc_assert (any_malformed_asm);
win:
bi->stack_out = regstack;
-
- /* Compensate the back edges, as those wasn't visited yet. */
- FOR_EACH_EDGE (e, ei, block->succs)
- {
- if (e->flags & EDGE_DFS_BACK
- || (e->dest == EXIT_BLOCK_PTR))
- {
- gcc_assert (BLOCK_INFO (e->dest)->done
- || e->dest == block);
- inserted |= compensate_edge (e, file);
- }
- }
- FOR_EACH_EDGE (e, ei, block->preds)
- {
- if (e != beste && !(e->flags & EDGE_DFS_BACK)
- && e->src != ENTRY_BLOCK_PTR)
- {
- gcc_assert (BLOCK_INFO (e->src)->done);
- inserted |= compensate_edge (e, file);
- }
- }
-
- return inserted;
+ bi->done = true;
}
/* Convert registers in all blocks reachable from BLOCK. */
-static int
+static void
convert_regs_2 (FILE *file, basic_block block)
{
basic_block *stack, *sp;
- int inserted;
/* We process the blocks in a top-down manner, in a way such that one block
is only processed after all its predecessors. The number of predecessors
*sp++ = block;
- inserted = 0;
do
{
edge e;
*sp++ = e->dest;
}
- inserted |= convert_regs_1 (file, block);
- BLOCK_INFO (block)->done = 1;
+ convert_regs_1 (file, block);
}
while (sp != stack);
free (stack);
-
- return inserted;
}
/* Traverse all basic blocks in a function, converting the register
/* Process all blocks reachable from all entry points. */
FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
- inserted |= convert_regs_2 (file, e->dest);
+ convert_regs_2 (file, 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)
- inserted |= convert_regs_2 (file, b);
+ convert_regs_2 (file, b);
}
+
+ inserted |= compensate_edges (file);
+
clear_aux_for_blocks ();
fixup_abnormal_edges ();
/* Set up block info for each basic block. */
alloc_aux_for_blocks (sizeof (struct block_info_def));
- FOR_EACH_BB_REVERSE (bb)
+ FOR_EACH_BB (bb)
{
block_info bi = BLOCK_INFO (bb);
edge_iterator ei;
/* 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);
}
}