/* Register to Stack convert for GNU compiler.
- Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001 Free Software Foundation, Inc.
+ Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
This file is part of GCC.
\f
#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "tree.h"
#include "rtl.h"
#include "tm_p.h"
#include "basic-block.h"
#include "varray.h"
#include "reload.h"
+#include "ggc.h"
+
+/* 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
unsigned char reg[REG_STACK_SIZE];/* register - stack mapping */
} *stack;
-/* This is used to carry information about basic blocks. It is
+/* This is used to carry information about basic blocks. It is
attached to the AUX field of the standard CFG block. */
typedef struct block_info_def
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 predecesors; /* Number of predecesors that needs
+ int predecessors; /* Number of predecessors that needs
to be visited. */
} *block_info;
EMIT_BEFORE
};
-/* 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 varray_type stack_regs_mentioned_data;
-
/* The block we're currently working on. */
static basic_block current_block;
-/* This is the register file for all register after conversion */
+/* 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];
#define FP_MODE_REG(regno,mode) \
- (FP_mode_reg[(regno)-FIRST_STACK_REG][(int)(mode)])
+ (FP_mode_reg[(regno)-FIRST_STACK_REG][(int) (mode)])
/* Used to initialize uninitialized registers. */
static rtx nan;
static void record_label_references PARAMS ((rtx, rtx));
static bool compensate_edge PARAMS ((edge, FILE *));
\f
-/* Return non-zero if any stack register is mentioned somewhere within PAT. */
+/* Return nonzero if any stack register is mentioned somewhere within PAT. */
static int
stack_regs_mentioned_p (pat)
next_flags_user (insn)
rtx insn;
{
- /* Search forward looking for the first use of this value.
+ /* Search forward looking for the first use of this value.
Stop at block boundaries. */
while (insn != current_block->end)
insn = NEXT_INSN (insn);
if (INSN_P (insn) && reg_mentioned_p (ix86_flags_rtx, PATTERN (insn)))
- return insn;
+ return insn;
if (GET_CODE (insn) == CALL_INSN)
return NULL_RTX;
return NULL_RTX;
}
\f
-/* Reorganise the stack into ascending numbers,
+/* Reorganize the stack into ascending numbers,
after this insn. */
static void
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);
}
-/* Pop a register from the stack */
+/* Pop a register from the stack. */
static void
pop_stack (regstack, regno)
CLEAR_HARD_REG_BIT (regstack->reg_set, regno);
regstack->top--;
- /* If regno was not at the top of stack then adjust stack */
+ /* If regno was not at the top of stack then adjust stack. */
if (regstack->reg [top] != regno)
{
int i;
rtx first;
FILE *file;
{
+ basic_block bb;
int i;
int max_uid;
/* Clean up previous run. */
- if (stack_regs_mentioned_data)
- {
- VARRAY_FREE (stack_regs_mentioned_data);
- stack_regs_mentioned_data = 0;
- }
+ stack_regs_mentioned_data = 0;
if (!optimize)
split_all_insns (0);
if (i > LAST_STACK_REG)
return;
- /* Ok, floating point instructions exist. If not optimizing,
+ /* Ok, floating point instructions exist. If not optimizing,
build the CFG and run life analysis. */
if (!optimize)
- find_basic_blocks (first, max_reg_num (), file);
- count_or_remove_death_notes (NULL, 1);
- life_analysis (first, file, PROP_DEATH_NOTES);
+ {
+ count_or_remove_death_notes (NULL, 1);
+ life_analysis (first, file, PROP_DEATH_NOTES);
+ }
mark_dfs_back_edges ();
/* Set up block info for each basic block. */
alloc_aux_for_blocks (sizeof (struct block_info_def));
- for (i = n_basic_blocks - 1; i >= 0; --i)
+ FOR_EACH_BB_REVERSE (bb)
{
edge e;
- basic_block bb = BASIC_BLOCK (i);
for (e = bb->pred; e; e=e->pred_next)
if (!(e->flags & EDGE_DFS_BACK)
&& e->src != ENTRY_BLOCK_PTR)
- BLOCK_INFO (bb)->predecesors++;
+ BLOCK_INFO (bb)->predecessors++;
}
/* Create the replacement registers up front. */
ix86_flags_rtx = gen_rtx_REG (CCmode, FLAGS_REG);
- /* A QNaN for initializing uninitialized variables.
+ /* A QNaN for initializing uninitialized variables.
??? We can't load from constant memory in PIC mode, because
- we're insertting these instructions before the prologue and
+ we're inserting these instructions before the prologue and
the PIC register hasn't been set up. In that case, fall back
on zero, which we can get from `ldz'. */
switch (GET_CODE (*pat))
{
case SUBREG:
- /* Eliminate FP subregister accesses in favour of the
+ /* Eliminate FP subregister accesses in favor of the
actual FP register in use. */
{
rtx subreg;
{
if (reg_class_size[(int) recog_op_alt[i][alt].class] != 1)
{
- error_for_asm (insn, "Output constraint %d must specify a single register", i);
+ error_for_asm (insn, "output constraint %d must specify a single register", i);
malformed_asm = 1;
}
- else
+ else
{
int j;
for (j = 0; j < n_clobbers; j++)
if (REGNO (recog_data.operand[i]) == REGNO (clobber_reg[j]))
{
- error_for_asm (insn, "Output constraint %d cannot be specified together with \"%s\" clobber",
+ error_for_asm (insn, "output constraint %d cannot be specified together with \"%s\" clobber",
i, reg_names [REGNO (clobber_reg[j])]);
malformed_asm = 1;
break;
if (i != LAST_STACK_REG + 1)
{
- error_for_asm (insn, "Output regs must be grouped at top of stack");
+ error_for_asm (insn, "output regs must be grouped at top of stack");
malformed_asm = 1;
}
if (i != LAST_STACK_REG + 1)
{
error_for_asm (insn,
- "Implicitly popped regs must be grouped at top of stack");
+ "implicitly popped regs must be grouped at top of stack");
malformed_asm = 1;
}
- /* Enfore rule #3: If any input operand uses the "f" constraint, all
+ /* Enforce rule #3: If any input operand uses the "f" constraint, all
output constraints must use the "&" earlyclobber.
??? Detect this more deterministically by having constrain_asm_operands
if (operands_match_p (recog_data.operand[j], recog_data.operand[i]))
{
error_for_asm (insn,
- "Output operand %d must use `&' constraint", j);
+ "output operand %d must use `&' constraint", j);
malformed_asm = 1;
}
}
{
#ifdef FUNCTION_OUTGOING_VALUE
result
- = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (decl)), decl);
+ = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (decl)), decl);
#else
result = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (decl)), decl);
#endif
{
rtx *note_link, this;
- note_link = ®_NOTES(insn);
+ note_link = ®_NOTES (insn);
for (this = *note_link; this; this = XEXP (this, 1))
if (REG_NOTE_KIND (this) == note
&& REG_P (XEXP (this, 0)) && REGNO (XEXP (this, 0)) == regno)
pop_insn = NULL_RTX;
if (get_hard_regnum (regstack, reg1) >= 0)
- pop_insn = emit_pop_insn (insn, regstack, reg1, where);
+ pop_insn = emit_pop_insn (insn, regstack, reg1, where);
if (get_hard_regnum (regstack, reg2) >= 0)
- pop_insn = emit_pop_insn (insn, regstack, reg2, where);
+ pop_insn = emit_pop_insn (insn, regstack, reg2, where);
if (!pop_insn)
abort ();
return pop_insn;
return;
}
- /* The destination ought to be dead */
+ /* The destination ought to be dead. */
if (get_hard_regnum (regstack, dest) >= FIRST_STACK_REG)
abort ();
stack. The stack mapping is changed to reflect that DEST is
now at top of stack. */
- /* The destination ought to be dead */
+ /* The destination ought to be dead. */
if (get_hard_regnum (regstack, dest) >= FIRST_STACK_REG)
abort ();
if (GET_CODE (pat) == SET
&& GET_CODE (SET_SRC (pat)) == UNSPEC
- && XINT (SET_SRC (pat), 1) == 9)
+ && XINT (SET_SRC (pat), 1) == UNSPEC_FNSTSW)
{
rtx dest = SET_DEST (pat);
- /* Search forward looking for the first use of this value.
+ /* Search forward looking for the first use of this value.
Stop at block boundaries. */
while (insn != current_block->end)
{
pat = PATTERN (insn);
if (GET_CODE (pat) != SET
|| GET_CODE (SET_SRC (pat)) != UNSPEC
- || XINT (SET_SRC (pat), 1) != 10
+ || XINT (SET_SRC (pat), 1) != UNSPEC_SAHF
|| ! dead_or_set_p (insn, dest))
return 0;
/* Deaths in USE insns can happen in non optimizing compilation.
Handle them by popping the dying register. */
src = get_true_reg (&XEXP (pat, 0));
- if (STACK_REG_P (*src)
- && find_regno_note (insn, REG_DEAD, REGNO (*src)))
- {
- emit_pop_insn (insn, regstack, *src, EMIT_AFTER);
- return;
- }
+ if (STACK_REG_P (*src)
+ && find_regno_note (insn, REG_DEAD, REGNO (*src)))
+ {
+ emit_pop_insn (insn, regstack, *src, EMIT_AFTER);
+ return;
+ }
/* ??? Uninitialized USE should not happen. */
else if (get_hard_regnum (regstack, *src) == -1)
- abort();
+ abort ();
break;
case CLOBBER:
/* The fix_truncdi_1 pattern wants to be able to allocate
it's own scratch register. It does this by clobbering
an fp reg so that it is assured of an empty reg-stack
- register. If the register is live, kill it now.
+ register. If the register is live, kill it now.
Remove the DEAD/UNUSED note so we don't try to kill it
later too. */
{
/* A top-level clobber with no REG_DEAD, and no hard-regnum
indicates an uninitialized value. Because reload removed
- all other clobbers, this must be due to a function
+ all other clobbers, this must be due to a function
returning without a value. Load up a NaN. */
if (! note
replace_reg (dest, get_hard_regnum (regstack, *dest));
}
- /* Keep operand 1 maching with destination. */
+ /* Keep operand 1 matching with destination. */
if (GET_RTX_CLASS (GET_CODE (pat_src)) == 'c'
&& REG_P (*src1) && REG_P (*src2)
&& REGNO (*src1) != REGNO (*dest))
case UNSPEC:
switch (XINT (pat_src, 1))
{
- case 1: /* sin */
- case 2: /* cos */
+ case UNSPEC_SIN:
+ case UNSPEC_COS:
/* These insns only operate on the top of the stack. */
src1 = get_true_reg (&XVECEXP (pat_src, 0, 0));
replace_reg (src1, FIRST_STACK_REG);
break;
- case 10:
- /* (unspec [(unspec [(compare ..)] 9)] 10)
- Unspec 9 is fnstsw; unspec 10 is sahf. The combination
- matches the PPRO fcomi instruction. */
+ case UNSPEC_FPATAN:
+ /* These insns operate on the top two stack slots. */
+
+ src1 = get_true_reg (&XVECEXP (pat_src, 0, 0));
+ src2 = get_true_reg (&XVECEXP (pat_src, 0, 1));
+
+ src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
+ src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2));
+
+ {
+ struct stack_def temp_stack;
+ int regno, j, k, temp;
+
+ temp_stack = *regstack;
+
+ /* Place operand 1 at the top of stack. */
+ regno = get_hard_regnum (&temp_stack, *src1);
+ if (regno < 0)
+ abort ();
+ if (regno != FIRST_STACK_REG)
+ {
+ k = temp_stack.top - (regno - FIRST_STACK_REG);
+ j = temp_stack.top;
+
+ temp = temp_stack.reg[k];
+ temp_stack.reg[k] = temp_stack.reg[j];
+ temp_stack.reg[j] = temp;
+ }
+
+ /* Place operand 2 next on the stack. */
+ regno = get_hard_regnum (&temp_stack, *src2);
+ if (regno < 0)
+ abort ();
+ if (regno != FIRST_STACK_REG + 1)
+ {
+ k = temp_stack.top - (regno - FIRST_STACK_REG);
+ j = temp_stack.top - 1;
+
+ temp = temp_stack.reg[k];
+ temp_stack.reg[k] = temp_stack.reg[j];
+ temp_stack.reg[j] = temp;
+ }
+
+ change_stack (insn, regstack, &temp_stack, EMIT_BEFORE);
+ }
+
+ replace_reg (src1, FIRST_STACK_REG);
+ replace_reg (src2, FIRST_STACK_REG + 1);
+
+ if (src1_note)
+ replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
+ if (src2_note)
+ replace_reg (&XEXP (src2_note, 0), FIRST_STACK_REG + 1);
+
+ /* Pop both input operands from the stack. */
+ CLEAR_HARD_REG_BIT (regstack->reg_set,
+ regstack->reg[regstack->top]);
+ CLEAR_HARD_REG_BIT (regstack->reg_set,
+ regstack->reg[regstack->top - 1]);
+ regstack->top -= 2;
+
+ /* Push the result back onto the stack. */
+ regstack->reg[++regstack->top] = REGNO (*dest);
+ SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
+ replace_reg (dest, FIRST_STACK_REG);
+ break;
+
+ case UNSPEC_SAHF:
+ /* (unspec [(unspec [(compare)] UNSPEC_FNSTSW)] UNSPEC_SAHF)
+ The combination matches the PPRO fcomi instruction. */
pat_src = XVECEXP (pat_src, 0, 0);
if (GET_CODE (pat_src) != UNSPEC
- || XINT (pat_src, 1) != 9)
+ || XINT (pat_src, 1) != UNSPEC_FNSTSW)
abort ();
/* FALLTHRU */
- case 9:
- /* (unspec [(compare ..)] 9) */
+ case UNSPEC_FNSTSW:
/* Combined fcomp+fnstsw generated for doing well with
CSE. When optimizing this would have been broken
up before now. */
&& REGNO (*dest) != regstack->reg[regstack->top])
{
/* In case one of operands is the top of stack and the operands
- dies, it is safe to make it the destination operand by reversing
- the direction of cmove and avoid fxch. */
+ dies, it is safe to make it the destination operand by
+ reversing the direction of cmove and avoid fxch. */
if ((REGNO (*src1) == regstack->reg[regstack->top]
&& src1_note)
|| (REGNO (*src2) == regstack->reg[regstack->top]
reversed_comparison_code (XEXP (pat_src, 0), insn));
}
else
- emit_swap_insn (insn, regstack, *dest);
+ emit_swap_insn (insn, regstack, *dest);
}
{
/* Make dest the top of stack. Add dest to regstack if
not present. */
if (get_hard_regnum (regstack, *dest) < FIRST_STACK_REG)
- regstack->reg[++regstack->top] = REGNO (*dest);
+ regstack->reg[++regstack->top] = REGNO (*dest);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, FIRST_STACK_REG);
break;
n_inputs = get_asm_operand_n_inputs (body);
n_outputs = recog_data.n_operands - n_inputs;
-
+
if (alt < 0)
abort ();
/* subst_stack_regs_pat may have deleted a no-op insn. If so, any
REG_UNUSED will already have been dealt with, so just return. */
- if (GET_CODE (insn) == NOTE)
+ if (GET_CODE (insn) == NOTE || INSN_DELETED_P (insn))
return;
/* If there is a REG_UNUSED note on a stack register on this insn,
since the form of the newly emitted pop insn references the reg,
making it no longer `unset'. */
- note_link = ®_NOTES(insn);
+ note_link = ®_NOTES (insn);
for (note = *note_link; note; note = XEXP (note, 1))
if (REG_NOTE_KIND (note) == REG_UNUSED && STACK_REG_P (XEXP (note, 0)))
{
abort ();
/* If the stack is not empty (new->top != -1), loop here emitting
- swaps until the stack is correct.
+ swaps until the stack is correct.
The worst case number of swaps emitted is N + 2, where N is the
depth of the stack. In some cases, the reg at the top of
}
\f
/* This function was doing life analysis. We now let the regular live
- code do it's job, so we only need to check some extra invariants
+ code do it's job, so we only need to check some extra invariants
that reg-stack expects. Primary among these being that all registers
are initialized before use.
static int
convert_regs_entry ()
{
- int inserted = 0, i;
+ int inserted = 0;
edge e;
+ basic_block block;
- for (i = n_basic_blocks - 1; i >= 0; --i)
+ FOR_EACH_BB_REVERSE (block)
{
- basic_block block = BASIC_BLOCK (i);
block_info bi = BLOCK_INFO (block);
int reg;
-
+
/* Set current register status at last instruction `uninitialized'. */
bi->stack_in.top = -2;
-
+
/* Copy live_at_end and live_at_start into temporaries. */
for (reg = FIRST_STACK_REG; reg <= LAST_STACK_REG; reg++)
{
}
}
- /* Load something into each stack register live at function entry.
+ /* Load something into each stack register live at function entry.
Such live registers can be caused by uninitialized variables or
- functions not returning values on all paths. In order to keep
+ functions not returning values on all paths. In order to keep
the push/pop code happy, and to not scrog the register stack, we
- must put something in these registers. Use a QNaN.
+ must put something in these registers. Use a QNaN.
- Note that we are insertting converted code here. This code is
+ Note that we are inserting converted code here. This code is
never seen by the convert_regs pass. */
for (e = ENTRY_BLOCK_PTR->succ; e ; e = e->succ_next)
output_stack->top = value_reg_high - value_reg_low;
for (reg = value_reg_low; reg <= value_reg_high; ++reg)
{
- output_stack->reg[reg - value_reg_low] = reg;
+ output_stack->reg[value_reg_high - reg] = reg;
SET_HARD_REG_BIT (output_stack->reg_set, reg);
}
}
tmpstack = regstack;
change_stack (block->end, &tmpstack, target_stack, EMIT_AFTER);
- return false;
+ return false;
}
if (file)
abort ();
eh1:
+ /* We are sure that there is st(0) live, otherwise we won't compensate.
+ 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);
abort ();
eh2:
current_block = NULL;
start_sequence ();
- /* ??? change_stack needs some point to emit insns after.
- Also needed to keep gen_sequence from returning a
- pattern as opposed to a sequence, which would lose
- REG_DEAD notes. */
+ /* ??? change_stack needs some point to emit insns after. */
after = emit_note (NULL, NOTE_INSN_DELETED);
tmpstack = regstack;
change_stack (after, &tmpstack, target_stack, EMIT_BEFORE);
- seq = gen_sequence ();
+ seq = get_insns ();
end_sequence ();
insert_insn_on_edge (seq, e);
/* 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, preffer critical
+ 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 (e = block->pred; e ; e = e->pred_next)
inserted |= compensate_edge (beste, file);
else
beste = NULL;
-
+
current_block = block;
if (file)
if (!BLOCK_INFO (e->dest)->done
&& e->dest != block)
abort ();
- inserted |= compensate_edge (e, file);
+ inserted |= compensate_edge (e, file);
}
}
for (e = block->pred; e ; e = e->pred_next)
{
if (!BLOCK_INFO (e->src)->done)
abort ();
- inserted |= compensate_edge (e, file);
+ inserted |= compensate_edge (e, file);
}
}
for (e = block->succ; e ; e = e->succ_next)
if (! (e->flags & EDGE_DFS_BACK))
{
- BLOCK_INFO (e->dest)->predecesors--;
- if (!BLOCK_INFO (e->dest)->predecesors)
+ BLOCK_INFO (e->dest)->predecessors--;
+ if (!BLOCK_INFO (e->dest)->predecessors)
*sp++ = e->dest;
}
}
convert_regs (file)
FILE *file;
{
- int inserted, i;
+ int inserted;
+ basic_block b;
edge e;
/* Initialize uninitialized registers on function entry. */
/* Process all blocks reachable from all entry points. */
for (e = ENTRY_BLOCK_PTR->succ; e ; e = e->succ_next)
inserted |= convert_regs_2 (file, e->dest);
-
- /* ??? Process all unreachable blocks. Though there's no excuse
+
+ /* ??? Process all unreachable blocks. Though there's no excuse
for keeping these even when not optimizing. */
- for (i = 0; i < n_basic_blocks; ++i)
+ FOR_EACH_BB (b)
{
- basic_block b = BASIC_BLOCK (i);
block_info bi = BLOCK_INFO (b);
if (! bi->done)
}
}
+ fixup_abnormal_edges ();
if (inserted)
commit_edge_insertions ();
return inserted;
}
#endif /* STACK_REGS */
+
+#include "gt-reg-stack.h"