/* Register to Stack convert for GNU compiler.
- Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000 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 GNU CC.
+ This file is part of GCC.
- GNU CC is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
+ GCC is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
- GNU CC is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
+ GCC is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ License for more details.
You should have received a copy of the GNU General Public License
- along with GNU CC; see the file COPYING. If not, write to
- the Free Software Foundation, 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
+ 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. */
/* This pass converts stack-like registers from the "flat register
file" model that gcc uses, to a stack convention that the 387 uses.
\f
#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "tree.h"
#include "rtl.h"
#include "tm_p.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
-#include "insn-flags.h"
#include "toplev.h"
#include "recog.h"
#include "output.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
{
int top; /* index to top stack element */
HARD_REG_SET reg_set; /* set of live registers */
- char reg[REG_STACK_SIZE]; /* register - stack mapping */
+ 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_in; /* Input stack configuration. */
+ 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
+ to be visited. */
} *block_info;
#define BLOCK_INFO(B) ((block_info) (B)->aux)
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 int get_asm_operand_n_inputs PARAMS ((rtx));
static rtx stack_result PARAMS ((tree));
static void replace_reg PARAMS ((rtx *, int));
-static void remove_regno_note PARAMS ((rtx, enum reg_note, int));
+static void remove_regno_note PARAMS ((rtx, enum reg_note,
+ unsigned int));
static int get_hard_regnum PARAMS ((stack, rtx));
-static void delete_insn_for_stacker PARAMS ((rtx));
static rtx emit_pop_insn PARAMS ((rtx, stack, rtx,
enum emit_where));
static void emit_swap_insn PARAMS ((rtx, stack, rtx));
static void print_stack PARAMS ((FILE *, stack));
static rtx next_flags_user PARAMS ((rtx));
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)
rtx pat;
{
- register const char *fmt;
- register int i;
+ const char *fmt;
+ int i;
if (STACK_REG_P (pat))
return 1;
{
if (fmt[i] == 'E')
{
- register int j;
+ int j;
for (j = XVECLEN (pat, i) - 1; j >= 0; j--)
if (stack_regs_mentioned_p (XVECEXP (pat, i, j)))
unsigned int uid, max;
int test;
- if (! INSN_P (insn))
+ if (! INSN_P (insn) || !stack_regs_mentioned_data)
return 0;
uid = INSN_UID (insn);
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. */
- /* ??? This really cries for BLOCK_END! */
- while (1)
+ while (insn != current_block->end)
{
insn = NEXT_INSN (insn);
- if (!insn)
- return NULL_RTX;
if (INSN_P (insn) && reg_mentioned_p (ix86_flags_rtx, PATTERN (insn)))
- return insn;
+ return insn;
- if (GET_CODE (insn) == JUMP_INSN
- || GET_CODE (insn) == CODE_LABEL
- || GET_CODE (insn) == CALL_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;
dump file, if used.
Construct a CFG and run life analysis. Then convert each insn one
- by one. Run a last jump_optimize pass, if optimizing, to eliminate
+ by one. Run a last cleanup_cfg pass, if optimizing, to eliminate
code duplication created when the converter inserts pop insns on
the edges. */
rtx first;
FILE *file;
{
+ basic_block bb;
int i;
int max_uid;
- block_info bi;
+
+ /* Clean up previous run. */
+ stack_regs_mentioned_data = 0;
+
+ if (!optimize)
+ split_all_insns (0);
/* See if there is something to do. Flow analysis is quite
expensive so we might save some compilation time. */
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. */
- find_basic_blocks (first, max_reg_num (), file);
- count_or_remove_death_notes (NULL, 1);
- life_analysis (first, file, PROP_DEATH_NOTES);
+ if (!optimize)
+ {
+ 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. */
- bi = (block_info) xcalloc ((n_basic_blocks + 1), sizeof (*bi));
- for (i = n_basic_blocks - 1; i >= 0; --i)
- BASIC_BLOCK (i)->aux = bi + i;
- EXIT_BLOCK_PTR->aux = bi + n_basic_blocks;
+ alloc_aux_for_blocks (sizeof (struct block_info_def));
+ FOR_EACH_BB_REVERSE (bb)
+ {
+ edge e;
+ for (e = bb->pred; e; e=e->pred_next)
+ if (!(e->flags & EDGE_DFS_BACK)
+ && e->src != ENTRY_BLOCK_PTR)
+ BLOCK_INFO (bb)->predecessors++;
+ }
/* Create the replacement registers up front. */
for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++)
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'. */
VARRAY_CHAR_INIT (stack_regs_mentioned_data, max_uid + 1,
"stack_regs_mentioned cache");
- if (convert_regs (file) && optimize)
- {
- jump_optimize (first, JUMP_CROSS_JUMP_DEATH_MATTERS,
- !JUMP_NOOP_MOVES, !JUMP_AFTER_REGSCAN);
- }
+ convert_regs (file);
- /* Clean up. */
- VARRAY_FREE (stack_regs_mentioned_data);
- free (bi);
+ free_aux_for_blocks ();
}
\f
/* Check PAT, which is in INSN, for LABEL_REFs. Add INSN to the
record_label_references (insn, pat)
rtx insn, pat;
{
- register enum rtx_code code = GET_CODE (pat);
- register int i;
- register const char *fmt;
+ enum rtx_code code = GET_CODE (pat);
+ int i;
+ const char *fmt;
if (code == LABEL_REF)
{
- register rtx label = XEXP (pat, 0);
- register rtx ref;
+ rtx label = XEXP (pat, 0);
+ rtx ref;
if (GET_CODE (label) != CODE_LABEL)
abort ();
record_label_references (insn, XEXP (pat, i));
if (fmt[i] == 'E')
{
- register int j;
+ int j;
for (j = 0; j < XVECLEN (pat, i); j++)
record_label_references (insn, XVECEXP (pat, i, j));
}
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 (FP_REG_P (subreg = SUBREG_REG (*pat)))
{
- *pat = FP_MODE_REG (REGNO (subreg) + SUBREG_WORD (*pat),
+ int regno_off = subreg_regno_offset (REGNO (subreg),
+ GET_MODE (subreg),
+ SUBREG_BYTE (*pat),
+ GET_MODE (*pat));
+ *pat = FP_MODE_REG (REGNO (subreg) + regno_off,
GET_MODE (subreg));
default:
return pat;
{
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
- reg_used_as_output[REGNO (recog_data.operand[i])] = 1;
+ 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",
+ i, reg_names [REGNO (clobber_reg[j])]);
+ malformed_asm = 1;
+ break;
+ }
+ if (j == n_clobbers)
+ reg_used_as_output[REGNO (recog_data.operand[i])] = 1;
+ }
}
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;
}
}
if (aggregate_value_p (DECL_RESULT (decl)))
return 0;
- result = DECL_RTL (DECL_RESULT (decl));
- /* ?!? What is this code supposed to do? Can this code actually
- trigger if we kick out aggregates above? */
- if (result != 0
- && ! (GET_CODE (result) == REG
- && REGNO (result) < FIRST_PSEUDO_REGISTER))
+ 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);
+ = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (decl)), decl);
#else
result = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (decl)), decl);
#endif
remove_regno_note (insn, note, regno)
rtx insn;
enum reg_note note;
- int regno;
+ unsigned int regno;
{
- register rtx *note_link, this;
+ 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)
return i >= 0 ? (FIRST_STACK_REG + regstack->top - i) : -1;
}
-
-/* Delete INSN from the RTL. Mark the insn, but don't remove it from
- the chain of insns. Doing so could confuse block_begin and block_end
- if this were the only insn in the block. */
-
-static void
-delete_insn_for_stacker (insn)
- rtx insn;
-{
- PUT_CODE (insn, NOTE);
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (insn) = 0;
-}
\f
/* Emit an insn to pop virtual register REG before or after INSN.
REGSTACK is the stack state after INSN and is updated to reflect this
rtx pop_insn, pop_rtx;
int hard_regno;
+ /* For complex types take care to pop both halves. These may survive in
+ CLOBBER and USE expressions. */
+ if (COMPLEX_MODE_P (GET_MODE (reg)))
+ {
+ rtx reg1 = FP_MODE_REG (REGNO (reg), DFmode);
+ rtx reg2 = FP_MODE_REG (REGNO (reg) + 1, DFmode);
+
+ pop_insn = NULL_RTX;
+ if (get_hard_regnum (regstack, reg1) >= 0)
+ 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);
+ if (!pop_insn)
+ abort ();
+ return pop_insn;
+ }
+
hard_regno = get_hard_regnum (regstack, reg);
if (hard_regno < FIRST_STACK_REG)
FP_MODE_REG (FIRST_STACK_REG, DFmode));
if (where == EMIT_AFTER)
- pop_insn = emit_block_insn_after (pop_rtx, insn, current_block);
+ pop_insn = emit_insn_after (pop_rtx, insn);
else
- pop_insn = emit_block_insn_before (pop_rtx, insn, current_block);
+ pop_insn = emit_insn_before (pop_rtx, insn);
REG_NOTES (pop_insn)
= gen_rtx_EXPR_LIST (REG_DEAD, FP_MODE_REG (FIRST_STACK_REG, DFmode),
while (tmp != limit)
{
if (GET_CODE (tmp) == CODE_LABEL
+ || GET_CODE (tmp) == CALL_INSN
|| NOTE_INSN_BASIC_BLOCK_P (tmp)
|| (GET_CODE (tmp) == INSN
&& stack_regs_mentioned (tmp)))
swap with, omit the swap. */
if (GET_CODE (i1dest) == REG && REGNO (i1dest) == FIRST_STACK_REG
- && GET_CODE (i1src) == REG && REGNO (i1src) == hard_regno - 1
+ && GET_CODE (i1src) == REG
+ && REGNO (i1src) == (unsigned) hard_regno - 1
&& find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX)
return;
/* If the previous insn wrote to the reg we are to swap with,
omit the swap. */
- if (GET_CODE (i1dest) == REG && REGNO (i1dest) == hard_regno
+ if (GET_CODE (i1dest) == REG && REGNO (i1dest) == (unsigned) hard_regno
&& GET_CODE (i1src) == REG && REGNO (i1src) == FIRST_STACK_REG
&& find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX)
return;
FP_MODE_REG (FIRST_STACK_REG, XFmode));
if (i1)
- emit_block_insn_after (swap_rtx, i1, current_block);
+ emit_insn_after (swap_rtx, i1);
else if (current_block)
- emit_block_insn_before (swap_rtx, current_block->head, current_block);
+ emit_insn_before (swap_rtx, current_block->head);
else
emit_insn_before (swap_rtx, insn);
}
{
emit_pop_insn (insn, regstack, src, EMIT_AFTER);
- delete_insn_for_stacker (insn);
+ delete_insn (insn);
return;
}
SET_HARD_REG_BIT (regstack->reg_set, REGNO (dest));
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (src));
- delete_insn_for_stacker (insn);
+ delete_insn (insn);
return;
}
if (find_regno_note (insn, REG_UNUSED, REGNO (dest)))
emit_pop_insn (insn, regstack, dest, EMIT_AFTER);
- delete_insn_for_stacker (insn);
+ delete_insn (insn);
return;
}
- /* The destination ought to be dead */
+ /* The destination ought to be dead. */
if (get_hard_regnum (regstack, dest) >= FIRST_STACK_REG)
abort ();
regstack->top--;
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (src));
}
- else if (GET_MODE (src) == XFmode && regstack->top < REG_STACK_SIZE - 1)
+ else if ((GET_MODE (src) == XFmode || GET_MODE (src) == TFmode)
+ && regstack->top < REG_STACK_SIZE - 1)
{
/* A 387 cannot write an XFmode value to a MEM without
clobbering the source reg. The output code can handle
stack is not full, and then write the value to memory via
a pop. */
rtx push_rtx, push_insn;
- rtx top_stack_reg = FP_MODE_REG (FIRST_STACK_REG, XFmode);
+ rtx top_stack_reg = FP_MODE_REG (FIRST_STACK_REG, GET_MODE (src));
- push_rtx = gen_movxf (top_stack_reg, top_stack_reg);
+ if (GET_MODE (src) == TFmode)
+ push_rtx = gen_movtf (top_stack_reg, top_stack_reg);
+ else
+ push_rtx = gen_movxf (top_stack_reg, top_stack_reg);
push_insn = emit_insn_before (push_rtx, insn);
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_DEAD, top_stack_reg,
REG_NOTES (insn));
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 ();
\f
/* Swap the condition on a branch, if there is one. Return true if we
found a condition to swap. False if the condition was not used as
- such. */
+ such. */
static int
swap_rtx_condition_1 (pat)
rtx pat;
{
- register const char *fmt;
- register int i, r = 0;
+ const char *fmt;
+ int i, r = 0;
if (GET_RTX_CLASS (GET_CODE (pat)) == '<')
{
{
if (fmt[i] == 'E')
{
- register int j;
+ int j;
for (j = XVECLEN (pat, i) - 1; j >= 0; j--)
r |= swap_rtx_condition_1 (XVECEXP (pat, i, j));
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. */
- /* ??? This really cries for BLOCK_END! */
- while (1)
+ while (insn != current_block->end)
{
insn = NEXT_INSN (insn);
- if (insn == NULL_RTX)
- return 0;
if (INSN_P (insn) && reg_mentioned_p (dest, insn))
break;
- if (GET_CODE (insn) == JUMP_INSN)
- return 0;
- if (GET_CODE (insn) == CODE_LABEL)
+ if (GET_CODE (insn) == CALL_INSN)
return 0;
}
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;
pat = PATTERN (insn);
}
- return swap_rtx_condition_1 (pat);
+ if (swap_rtx_condition_1 (pat))
+ {
+ int fail = 0;
+ INSN_CODE (insn) = -1;
+ if (recog_memoized (insn) == -1)
+ fail = 1;
+ /* In case the flags don't die here, recurse to try fix
+ following user too. */
+ else if (! dead_or_set_p (insn, ix86_flags_rtx))
+ {
+ insn = next_flags_user (insn);
+ if (!insn || !swap_rtx_condition (insn))
+ fail = 1;
+ }
+ if (fail)
+ {
+ swap_rtx_condition_1 (pat);
+ return 0;
+ }
+ return 1;
+ }
+ return 0;
}
/* Handle a comparison. Special care needs to be taken to avoid
/* 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
PATTERN (insn) = pat;
move_for_stack_reg (insn, regstack, pat);
}
+ if (! note && COMPLEX_MODE_P (GET_MODE (*dest))
+ && get_hard_regnum (regstack, FP_MODE_REG (REGNO (*dest), DFmode)) == -1)
+ {
+ pat = gen_rtx_SET (VOIDmode,
+ FP_MODE_REG (REGNO (*dest) + 1, SFmode),
+ nan);
+ PATTERN (insn) = pat;
+ move_for_stack_reg (insn, regstack, pat);
+ }
}
}
break;
case SET:
{
- rtx *src1 = (rtx *) NULL_PTR, *src2;
+ rtx *src1 = (rtx *) 0, *src2;
rtx src1_note, src2_note;
rtx pat_src;
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. */
break;
case IF_THEN_ELSE:
- /* This insn requires the top of stack to be the destination. */
+ /* This insn requires the top of stack to be the destination. */
+
+ src1 = get_true_reg (&XEXP (pat_src, 1));
+ src2 = get_true_reg (&XEXP (pat_src, 2));
+
+ src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
+ src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2));
/* If the comparison operator is an FP comparison operator,
it is handled correctly by compare_for_stack_reg () who
will move the destination to the top of stack. But if the
comparison operator is not an FP comparison operator, we
- have to handle it here. */
+ have to handle it here. */
if (get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG
&& REGNO (*dest) != regstack->reg[regstack->top])
- emit_swap_insn (insn, regstack, *dest);
-
- src1 = get_true_reg (&XEXP (pat_src, 1));
- src2 = get_true_reg (&XEXP (pat_src, 2));
-
- src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
- src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2));
+ {
+ /* 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. */
+ if ((REGNO (*src1) == regstack->reg[regstack->top]
+ && src1_note)
+ || (REGNO (*src2) == regstack->reg[regstack->top]
+ && src2_note))
+ {
+ int idx1 = (get_hard_regnum (regstack, *src1)
+ - FIRST_STACK_REG);
+ int idx2 = (get_hard_regnum (regstack, *src2)
+ - FIRST_STACK_REG);
+
+ /* Make reg-stack believe that the operands are already
+ swapped on the stack */
+ regstack->reg[regstack->top - idx1] = REGNO (*src2);
+ regstack->reg[regstack->top - idx2] = REGNO (*src1);
+
+ /* Reverse condition to compensate the operand swap.
+ i386 do have comparison always reversible. */
+ PUT_CODE (XEXP (pat_src, 0),
+ reversed_comparison_code (XEXP (pat_src, 0), insn));
+ }
+ else
+ emit_swap_insn (insn, regstack, *dest);
+ }
{
rtx src_note [3];
EMIT_AFTER);
}
else
- {
- CLEAR_HARD_REG_BIT (regstack->reg_set, regno);
- replace_reg (&XEXP (src_note[i], 0), FIRST_STACK_REG);
- regstack->top--;
- }
+ /* Top of stack never dies, as it is the
+ destination. */
+ abort ();
}
}
/* Make dest the top of stack. Add dest to regstack if
- not present. */
+ 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 ();
if (regno < 0)
abort ();
- if (regno != REGNO (recog_data.operand[i]))
+ if ((unsigned int) regno != REGNO (recog_data.operand[i]))
{
/* recog_data.operand[i] is not in the right place. Find
it and swap it with whatever is already in I's place.
for (j = 0; j < n_outputs; j++)
if (STACK_REG_P (recog_data.operand[j])
- && REGNO (recog_data.operand[j]) == i)
+ && REGNO (recog_data.operand[j]) == (unsigned) i)
{
regstack->reg[++regstack->top] = i;
SET_HARD_REG_BIT (regstack->reg_set, i);
rtx insn;
stack regstack;
{
- register rtx *note_link, note;
- register int i;
+ rtx *note_link, note;
+ int i;
if (GET_CODE (insn) == CALL_INSN)
{
/* 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);
}
}
}
+/* 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. */
+static bool
+compensate_edge (e, file)
+ edge e;
+ FILE *file;
+{
+ basic_block block = e->src, target = e->dest;
+ block_info bi = BLOCK_INFO (block);
+ struct stack_def regstack, tmpstack;
+ stack target_stack = &BLOCK_INFO (target)->stack_in;
+ int reg;
+
+ current_block = block;
+ regstack = bi->stack_out;
+ if (file)
+ fprintf (file, "Edge %d->%d: ", block->index, target->index);
+
+ if (target_stack->top == -2)
+ {
+ /* 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]))
+ break;
+
+ if (reg == -1)
+ {
+ if (file)
+ fprintf (file, "new block; copying stack position\n");
+
+ /* change_stack kills values in regstack. */
+ tmpstack = regstack;
+
+ change_stack (block->end, &tmpstack, target_stack, EMIT_AFTER);
+ 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);
+ }
+ }
+
+ /* 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 return path will not have actually loaded the values. */
+ else 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);
+ 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:
+
+ target_stack->top = -1;
+ }
+
+ /* 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 (block->succ->succ_next == NULL && !(e->flags & EDGE_ABNORMAL))
+ {
+ /* change_stack kills values in regstack. */
+ tmpstack = regstack;
+
+ change_stack (block->end, &tmpstack, target_stack,
+ (GET_CODE (block->end) == JUMP_INSN
+ ? 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. */
+ if (e->flags & EDGE_ABNORMAL)
+ abort ();
+
+ current_block = NULL;
+ start_sequence ();
+
+ /* ??? 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 = get_insns ();
+ end_sequence ();
+
+ insert_insn_on_edge (seq, e);
+ return true;
+ }
+ return false;
+}
+
/* Convert stack register references in one block. */
static int
FILE *file;
basic_block block;
{
- struct stack_def regstack, tmpstack;
+ struct stack_def regstack;
block_info bi = BLOCK_INFO (block);
int inserted, reg;
rtx insn, next;
- edge e;
+ edge e, beste = NULL;
+
+ inserted = 0;
+
+ /* 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 (e = block->pred; e ; e = e->pred_next)
+ {
+ 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;
+ }
+
+ /* Entry block does have stack already initialized. */
+ if (bi->stack_in.top == -2)
+ inserted |= compensate_edge (beste, file);
+ else
+ beste = NULL;
current_block = block;
-
+
if (file)
{
fprintf (file, "\nBasic block %d\nInput stack: ", block->index);
set = gen_rtx_SET (VOIDmode, FP_MODE_REG (reg, SFmode),
nan);
- insn = emit_block_insn_after (set, insn, block);
+ insn = emit_insn_after (set, insn);
subst_stack_regs (insn, ®stack);
}
}
GO_IF_HARD_REG_EQUAL (regstack.reg_set, bi->out_reg_set, win);
abort ();
win:
+ bi->stack_out = regstack;
- /* 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. */
- inserted = 0;
+ /* Compensate the back edges, as those wasn't visited yet. */
for (e = block->succ; e ; e = e->succ_next)
{
- basic_block target = e->dest;
- stack target_stack = &BLOCK_INFO (target)->stack_in;
-
- if (file)
- fprintf (file, "Edge to block %d: ", target->index);
-
- if (target_stack->top == -2)
- {
- /* 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]))
- break;
-
- if (reg == -1)
- {
- if (file)
- fprintf (file, "new block; copying stack position\n");
-
- /* change_stack kills values in regstack. */
- tmpstack = regstack;
-
- change_stack (block->end, &tmpstack,
- target_stack, EMIT_AFTER);
- continue;
- }
-
- 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");
- continue;
- }
- }
-
- if (file)
- {
- fprintf (file, "correcting stack to ");
- print_stack (file, target_stack);
- }
- }
-
- /* Care for EH edges specially. The normal return path may return
- a value in st(0), but the EH path will not, and there's no need
- to add popping code to the edge. */
- if (e->flags & (EDGE_EH | EDGE_ABNORMAL_CALL))
+ if (e->flags & EDGE_DFS_BACK
+ || (e->dest == EXIT_BLOCK_PTR))
{
- /* 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);
- abort();
- eh1:
-
- SET_HARD_REG_BIT (tmp, FIRST_STACK_REG);
- GO_IF_HARD_REG_EQUAL (regstack.reg_set, tmp, eh2);
- abort();
- eh2:
-
- target_stack->top = -1;
- }
-
- /* 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 (block->succ->succ_next == NULL
- && ! (e->flags & EDGE_ABNORMAL))
- {
- /* change_stack kills values in regstack. */
- tmpstack = regstack;
-
- change_stack (block->end, &tmpstack, target_stack,
- (GET_CODE (block->end) == JUMP_INSN
- ? EMIT_BEFORE : EMIT_AFTER));
+ if (!BLOCK_INFO (e->dest)->done
+ && e->dest != block)
+ abort ();
+ inserted |= compensate_edge (e, file);
}
- else
+ }
+ for (e = block->pred; e ; e = e->pred_next)
+ {
+ if (e != beste && !(e->flags & EDGE_DFS_BACK)
+ && e->src != ENTRY_BLOCK_PTR)
{
- 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. */
- if (e->flags & EDGE_ABNORMAL)
+ if (!BLOCK_INFO (e->src)->done)
abort ();
-
- 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. */
- after = emit_note (NULL, NOTE_INSN_DELETED);
-
- tmpstack = regstack;
- change_stack (after, &tmpstack, target_stack, EMIT_BEFORE);
-
- seq = gen_sequence ();
- end_sequence ();
-
- insert_insn_on_edge (seq, e);
- inserted = 1;
- current_block = block;
+ inserted |= compensate_edge (e, file);
}
}
sp = stack;
*sp++ = block;
- BLOCK_INFO (block)->done = 1;
inserted = 0;
do
block = *--sp;
inserted |= convert_regs_1 (file, block);
+ BLOCK_INFO (block)->done = 1;
for (e = block->succ; e ; e = e->succ_next)
- if (! BLOCK_INFO (e->dest)->done)
+ if (! (e->flags & EDGE_DFS_BACK))
{
- *sp++ = e->dest;
- BLOCK_INFO (e->dest)->done = 1;
+ BLOCK_INFO (e->dest)->predecessors--;
+ if (!BLOCK_INFO (e->dest)->predecessors)
+ *sp++ = e->dest;
}
}
while (sp != stack);
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"