/* Register to Stack convert for GNU compiler.
- Copyright (C) 1992-2013 Free Software Foundation, Inc.
+ Copyright (C) 1992-2019 Free Software Foundation, Inc.
This file is part of GCC.
All implicitly popped input regs must be closer to the top of
the reg-stack than any input that is not implicitly popped.
+ All explicitly referenced input operands may not "skip" a reg.
+ Otherwise we can have holes in the stack.
+
3. It is possible that if an input dies in an insn, reload might
use the input reg for an output reload. Consider this example:
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
+#include "target.h"
+#include "rtl.h"
#include "tree.h"
-#include "rtl-error.h"
-#include "tm_p.h"
-#include "function.h"
+#include "df.h"
#include "insn-config.h"
+#include "memmodel.h"
#include "regs.h"
-#include "hard-reg-set.h"
-#include "flags.h"
+#include "emit-rtl.h" /* FIXME: Can go away once crtl is moved to rtl.h. */
#include "recog.h"
-#include "basic-block.h"
+#include "varasm.h"
+#include "rtl-error.h"
+#include "cfgrtl.h"
+#include "cfganal.h"
+#include "cfgbuild.h"
+#include "cfgcleanup.h"
#include "reload.h"
-#include "ggc.h"
#include "tree-pass.h"
-#include "target.h"
-#include "df.h"
-#include "emit-rtl.h" /* FIXME: Can go away once crtl is moved to rtl.h. */
+#include "rtl-iter.h"
#ifdef STACK_REGS
static void pop_stack (stack_ptr, int);
static rtx *get_true_reg (rtx *);
-static int check_asm_stack_operands (rtx);
+static int check_asm_stack_operands (rtx_insn *);
static void get_asm_operands_in_out (rtx, int *, int *);
static rtx stack_result (tree);
static void replace_reg (rtx *, int);
-static void remove_regno_note (rtx, enum reg_note, unsigned int);
+static void remove_regno_note (rtx_insn *, enum reg_note, unsigned int);
static int get_hard_regnum (stack_ptr, rtx);
-static rtx emit_pop_insn (rtx, stack_ptr, rtx, enum emit_where);
-static void swap_to_top (rtx, stack_ptr, rtx, rtx);
-static bool move_for_stack_reg (rtx, stack_ptr, rtx);
-static bool move_nan_for_stack_reg (rtx, stack_ptr, rtx);
+static rtx_insn *emit_pop_insn (rtx_insn *, stack_ptr, rtx, enum emit_where);
+static void swap_to_top (rtx_insn *, stack_ptr, rtx, rtx);
+static bool move_for_stack_reg (rtx_insn *, stack_ptr, rtx);
+static bool move_nan_for_stack_reg (rtx_insn *, stack_ptr, rtx);
static int swap_rtx_condition_1 (rtx);
-static int swap_rtx_condition (rtx);
-static void compare_for_stack_reg (rtx, stack_ptr, rtx);
-static bool subst_stack_regs_pat (rtx, stack_ptr, rtx);
-static void subst_asm_stack_regs (rtx, stack_ptr);
-static bool subst_stack_regs (rtx, stack_ptr);
-static void change_stack (rtx, stack_ptr, stack_ptr, enum emit_where);
+static int swap_rtx_condition (rtx_insn *);
+static void compare_for_stack_reg (rtx_insn *, stack_ptr, rtx, bool);
+static bool subst_stack_regs_pat (rtx_insn *, stack_ptr, rtx);
+static void subst_asm_stack_regs (rtx_insn *, stack_ptr);
+static bool subst_stack_regs (rtx_insn *, stack_ptr);
+static void change_stack (rtx_insn *, stack_ptr, stack_ptr, enum emit_where);
static void print_stack (FILE *, stack_ptr);
-static rtx next_flags_user (rtx);
+static rtx_insn *next_flags_user (rtx_insn *);
\f
/* Return nonzero if any stack register is mentioned somewhere within PAT. */
\f
static rtx ix86_flags_rtx;
-static rtx
-next_flags_user (rtx insn)
+static rtx_insn *
+next_flags_user (rtx_insn *insn)
{
/* Search forward looking for the first use of this value.
Stop at block boundaries. */
return insn;
if (CALL_P (insn))
- return NULL_RTX;
+ return NULL;
}
- return NULL_RTX;
+ return NULL;
}
\f
/* Reorganize the stack into ascending numbers, before this insn. */
static void
-straighten_stack (rtx insn, stack_ptr regstack)
+straighten_stack (rtx_insn *insn, stack_ptr regstack)
{
struct stack_def temp_stack;
int top;
/* Eliminate FP subregister accesses in favor of the
actual FP register in use. */
{
- rtx subreg;
- if (STACK_REG_P (subreg = SUBREG_REG (*pat)))
+ rtx subreg = SUBREG_REG (*pat);
+
+ if (STACK_REG_P (subreg))
{
int regno_off = subreg_regno_offset (REGNO (subreg),
GET_MODE (subreg),
GET_MODE (subreg));
return pat;
}
+ pat = &XEXP (*pat, 0);
+ break;
}
+
+ case FLOAT_TRUNCATE:
+ if (!flag_unsafe_math_optimizations)
+ return pat;
+ /* FALLTHRU */
+
case FLOAT:
case FIX:
case FLOAT_EXTEND:
- pat = & XEXP (*pat, 0);
+ pat = &XEXP (*pat, 0);
break;
case UNSPEC:
if (XINT (*pat, 1) == UNSPEC_TRUNC_NOOP
- || XINT (*pat, 1) == UNSPEC_LDA)
- pat = & XVECEXP (*pat, 0, 0);
+ || XINT (*pat, 1) == UNSPEC_FILD_ATOMIC)
+ pat = &XVECEXP (*pat, 0, 0);
return pat;
- case FLOAT_TRUNCATE:
- if (!flag_unsafe_math_optimizations)
- return pat;
- pat = & XEXP (*pat, 0);
- break;
-
default:
return pat;
}
numbers below refer to that explanation. */
static int
-check_asm_stack_operands (rtx insn)
+check_asm_stack_operands (rtx_insn *insn)
{
int i;
int n_clobbers;
char reg_used_as_output[FIRST_PSEUDO_REGISTER];
char implicitly_dies[FIRST_PSEUDO_REGISTER];
- int alt;
+ char explicitly_used[FIRST_PSEUDO_REGISTER];
rtx *clobber_reg = 0;
int n_inputs, n_outputs;
/* Find out what the constraints require. If no constraint
alternative matches, this asm is malformed. */
- extract_insn (insn);
- constrain_operands (1);
- alt = which_alternative;
+ extract_constrain_insn (insn);
- preprocess_constraints ();
+ preprocess_constraints (insn);
get_asm_operands_in_out (body, &n_outputs, &n_inputs);
- if (alt < 0)
+ if (which_alternative < 0)
{
malformed_asm = 1;
/* Avoid further trouble with this insn. */
PATTERN (insn) = gen_rtx_USE (VOIDmode, const0_rtx);
return 0;
}
+ const operand_alternative *op_alt = which_op_alt ();
/* Strip SUBREGs here to make the following code simpler. */
for (i = 0; i < recog_data.n_operands; i++)
for (i = 0; i < n_outputs; i++)
if (STACK_REG_P (recog_data.operand[i]))
{
- if (reg_class_size[(int) recog_op_alt[i][alt].cl] != 1)
+ if (reg_class_size[(int) op_alt[i].cl] != 1)
{
error_for_asm (insn, "output constraint %d must specify a single register", i);
malformed_asm = 1;
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 %qs 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 registers must be grouped at top of stack");
malformed_asm = 1;
}
popped. */
memset (implicitly_dies, 0, sizeof (implicitly_dies));
+ memset (explicitly_used, 0, sizeof (explicitly_used));
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (STACK_REG_P (recog_data.operand[i]))
{
if (operands_match_p (clobber_reg[j], recog_data.operand[i]))
break;
- if (j < n_clobbers || recog_op_alt[i][alt].matches >= 0)
+ if (j < n_clobbers || op_alt[i].matches >= 0)
implicitly_dies[REGNO (recog_data.operand[i])] = 1;
+ else if (reg_class_size[(int) op_alt[i].cl] == 1)
+ explicitly_used[REGNO (recog_data.operand[i])] = 1;
}
/* Search for first non-popped reg. */
if (i != LAST_STACK_REG + 1)
{
error_for_asm (insn,
- "implicitly popped regs must be grouped at top of stack");
+ "implicitly popped registers must be grouped "
+ "at top of stack");
+ malformed_asm = 1;
+ }
+
+ /* Search for first not-explicitly used reg. */
+ for (i = FIRST_STACK_REG; i < LAST_STACK_REG + 1; i++)
+ if (! implicitly_dies[i] && ! explicitly_used[i])
+ break;
+
+ /* If there are any other explicitly used regs, that's an error. */
+ for (; i < LAST_STACK_REG + 1; i++)
+ if (explicitly_used[i])
+ break;
+
+ if (i != LAST_STACK_REG + 1)
+ {
+ error_for_asm (insn,
+ "explicitly used registers must be grouped "
+ "at top of stack");
malformed_asm = 1;
}
record any earlyclobber. */
for (i = n_outputs; i < n_outputs + n_inputs; i++)
- if (recog_op_alt[i][alt].matches == -1)
+ if (STACK_REG_P (recog_data.operand[i]) && op_alt[i].matches == -1)
{
int j;
gcc_assert (IN_RANGE (regno, FIRST_STACK_REG, LAST_STACK_REG));
gcc_assert (STACK_REG_P (*reg));
- gcc_assert (SCALAR_FLOAT_MODE_P (GET_MODE (*reg))
+ gcc_assert (GET_MODE_CLASS (GET_MODE (*reg)) == MODE_FLOAT
|| GET_MODE_CLASS (GET_MODE (*reg)) == MODE_COMPLEX_FLOAT);
*reg = FP_MODE_REG (regno, GET_MODE (*reg));
number REGNO from INSN. Remove only one such note. */
static void
-remove_regno_note (rtx insn, enum reg_note note, unsigned int regno)
+remove_regno_note (rtx_insn *insn, enum reg_note note, unsigned int regno)
{
rtx *note_link, this_rtx;
and source is the top of stack. A death note for the top of stack
cases the movdf pattern to pop. */
-static rtx
-emit_pop_insn (rtx insn, stack_ptr regstack, rtx reg, enum emit_where where)
+static rtx_insn *
+emit_pop_insn (rtx_insn *insn, stack_ptr regstack, rtx reg,
+ enum emit_where where)
{
- rtx pop_insn, pop_rtx;
+ machine_mode raw_mode = reg_raw_mode[FIRST_STACK_REG];
+ rtx_insn *pop_insn;
+ rtx 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);
+ rtx reg1 = FP_MODE_REG (REGNO (reg), raw_mode);
+ rtx reg2 = FP_MODE_REG (REGNO (reg) + 1, raw_mode);
- pop_insn = NULL_RTX;
+ pop_insn = NULL;
if (get_hard_regnum (regstack, reg1) >= 0)
pop_insn = emit_pop_insn (insn, regstack, reg1, where);
if (get_hard_regnum (regstack, reg2) >= 0)
gcc_assert (hard_regno >= FIRST_STACK_REG);
- pop_rtx = gen_rtx_SET (VOIDmode, FP_MODE_REG (hard_regno, DFmode),
- FP_MODE_REG (FIRST_STACK_REG, DFmode));
+ pop_rtx = gen_rtx_SET (FP_MODE_REG (hard_regno, raw_mode),
+ FP_MODE_REG (FIRST_STACK_REG, raw_mode));
if (where == EMIT_AFTER)
pop_insn = emit_insn_after (pop_rtx, insn);
else
pop_insn = emit_insn_before (pop_rtx, insn);
- add_reg_note (pop_insn, REG_DEAD, FP_MODE_REG (FIRST_STACK_REG, DFmode));
+ add_reg_note (pop_insn, REG_DEAD, FP_MODE_REG (FIRST_STACK_REG, raw_mode));
regstack->reg[regstack->top - (hard_regno - FIRST_STACK_REG)]
= regstack->reg[regstack->top];
If REG is already at the top of the stack, no insn is emitted. */
static void
-emit_swap_insn (rtx insn, stack_ptr regstack, rtx reg)
+emit_swap_insn (rtx_insn *insn, stack_ptr regstack, rtx reg)
{
int hard_regno;
- rtx swap_rtx;
- int tmp, other_reg; /* swap regno temps */
- rtx i1; /* the stack-reg insn prior to INSN */
+ int other_reg; /* swap regno temps */
+ rtx_insn *i1; /* the stack-reg insn prior to INSN */
rtx i1set = NULL_RTX; /* the SET rtx within I1 */
hard_regno = get_hard_regnum (regstack, reg);
gcc_assert (hard_regno >= FIRST_STACK_REG);
other_reg = regstack->top - (hard_regno - FIRST_STACK_REG);
-
- tmp = regstack->reg[other_reg];
- regstack->reg[other_reg] = regstack->reg[regstack->top];
- regstack->reg[regstack->top] = tmp;
+ std::swap (regstack->reg[regstack->top], regstack->reg[other_reg]);
/* Find the previous insn involving stack regs, but don't pass a
block boundary. */
i1 = NULL;
if (current_block && insn != BB_HEAD (current_block))
{
- rtx tmp = PREV_INSN (insn);
- rtx limit = PREV_INSN (BB_HEAD (current_block));
+ rtx_insn *tmp = PREV_INSN (insn);
+ rtx_insn *limit = PREV_INSN (BB_HEAD (current_block));
while (tmp != limit)
{
if (LABEL_P (tmp)
&& REG_P (i1src) && REGNO (i1src) == FIRST_STACK_REG
&& find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX)
return;
+
+ /* Instead of
+ fld a
+ fld b
+ fxch %st(1)
+ just use
+ fld b
+ fld a
+ if possible. Similarly for fld1, fldz, fldpi etc. instead of any
+ of the loads or for float extension from memory. */
+
+ i1src = SET_SRC (i1set);
+ if (GET_CODE (i1src) == FLOAT_EXTEND)
+ i1src = XEXP (i1src, 0);
+ if (REG_P (i1dest)
+ && REGNO (i1dest) == FIRST_STACK_REG
+ && (MEM_P (i1src) || GET_CODE (i1src) == CONST_DOUBLE)
+ && !side_effects_p (i1src)
+ && hard_regno == FIRST_STACK_REG + 1
+ && i1 != BB_HEAD (current_block))
+ {
+ /* i1 is the last insn that involves stack regs before insn, and
+ is known to be a load without other side-effects, i.e. fld b
+ in the above comment. */
+ rtx_insn *i2 = NULL;
+ rtx i2set;
+ rtx_insn *tmp = PREV_INSN (i1);
+ rtx_insn *limit = PREV_INSN (BB_HEAD (current_block));
+ /* Find the previous insn involving stack regs, but don't pass a
+ block boundary. */
+ while (tmp != limit)
+ {
+ if (LABEL_P (tmp)
+ || CALL_P (tmp)
+ || NOTE_INSN_BASIC_BLOCK_P (tmp)
+ || (NONJUMP_INSN_P (tmp)
+ && stack_regs_mentioned (tmp)))
+ {
+ i2 = tmp;
+ break;
+ }
+ tmp = PREV_INSN (tmp);
+ }
+ if (i2 != NULL_RTX
+ && (i2set = single_set (i2)) != NULL_RTX)
+ {
+ rtx i2dest = *get_true_reg (&SET_DEST (i2set));
+ rtx i2src = SET_SRC (i2set);
+ if (GET_CODE (i2src) == FLOAT_EXTEND)
+ i2src = XEXP (i2src, 0);
+ /* If the last two insns before insn that involve
+ stack regs are loads, where the latter (i1)
+ pushes onto the register stack and thus
+ moves the value from the first load (i2) from
+ %st to %st(1), consider swapping them. */
+ if (REG_P (i2dest)
+ && REGNO (i2dest) == FIRST_STACK_REG
+ && (MEM_P (i2src) || GET_CODE (i2src) == CONST_DOUBLE)
+ /* Ensure i2 doesn't have other side-effects. */
+ && !side_effects_p (i2src)
+ /* And that the two instructions can actually be
+ swapped, i.e. there shouldn't be any stores
+ in between i2 and i1 that might alias with
+ the i1 memory, and the memory address can't
+ use registers set in between i2 and i1. */
+ && !modified_between_p (SET_SRC (i1set), i2, i1))
+ {
+ /* Move i1 (fld b above) right before i2 (fld a
+ above. */
+ remove_insn (i1);
+ SET_PREV_INSN (i1) = NULL_RTX;
+ SET_NEXT_INSN (i1) = NULL_RTX;
+ set_block_for_insn (i1, NULL);
+ emit_insn_before (i1, i2);
+ return;
+ }
+ }
+ }
}
/* Avoid emitting the swap if this is the first register stack insn
return;
}
- swap_rtx = gen_swapxf (FP_MODE_REG (hard_regno, XFmode),
- FP_MODE_REG (FIRST_STACK_REG, XFmode));
-
+ machine_mode raw_mode = reg_raw_mode[FIRST_STACK_REG];
+ rtx op1 = FP_MODE_REG (hard_regno, raw_mode);
+ rtx op2 = FP_MODE_REG (FIRST_STACK_REG, raw_mode);
+ rtx swap_rtx
+ = gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec (2, gen_rtx_SET (op1, op2),
+ gen_rtx_SET (op2, op1)));
if (i1)
emit_insn_after (swap_rtx, i1);
else if (current_block)
is emitted. */
static void
-swap_to_top (rtx insn, stack_ptr regstack, rtx src1, rtx src2)
+swap_to_top (rtx_insn *insn, stack_ptr regstack, rtx src1, rtx src2)
{
struct stack_def temp_stack;
- int regno, j, k, temp;
+ int regno, j, k;
temp_stack = *regstack;
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;
+ std::swap (temp_stack.reg[j], temp_stack.reg[k]);
}
/* Place operand 2 next on the stack. */
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;
+ std::swap (temp_stack.reg[j], temp_stack.reg[k]);
}
change_stack (insn, regstack, &temp_stack, EMIT_BEFORE);
was deleted in the process. */
static bool
-move_for_stack_reg (rtx insn, stack_ptr regstack, rtx pat)
+move_for_stack_reg (rtx_insn *insn, stack_ptr regstack, rtx pat)
{
rtx *psrc = get_true_reg (&SET_SRC (pat));
rtx *pdest = get_true_reg (&SET_DEST (pat));
}
/* The destination ought to be dead. */
- gcc_assert (get_hard_regnum (regstack, dest) < FIRST_STACK_REG);
-
- replace_reg (psrc, get_hard_regnum (regstack, src));
+ if (get_hard_regnum (regstack, dest) >= FIRST_STACK_REG)
+ gcc_assert (any_malformed_asm);
+ else
+ {
+ replace_reg (psrc, get_hard_regnum (regstack, src));
- regstack->reg[++regstack->top] = REGNO (dest);
- SET_HARD_REG_BIT (regstack->reg_set, REGNO (dest));
- replace_reg (pdest, FIRST_STACK_REG);
+ regstack->reg[++regstack->top] = REGNO (dest);
+ SET_HARD_REG_BIT (regstack->reg_set, REGNO (dest));
+ replace_reg (pdest, FIRST_STACK_REG);
+ }
}
else if (STACK_REG_P (src))
{
&& XINT (SET_SRC (XVECEXP (pat, 0, 1)), 1) == UNSPEC_TAN)
emit_swap_insn (insn, regstack, dest);
else
- gcc_assert (get_hard_regnum (regstack, dest) < FIRST_STACK_REG);
+ gcc_assert (get_hard_regnum (regstack, dest) < FIRST_STACK_REG
+ || any_malformed_asm);
gcc_assert (regstack->top < REG_STACK_SIZE);
a NaN into DEST, then invokes move_for_stack_reg. */
static bool
-move_nan_for_stack_reg (rtx insn, stack_ptr regstack, rtx dest)
+move_nan_for_stack_reg (rtx_insn *insn, stack_ptr regstack, rtx dest)
{
rtx pat;
dest = FP_MODE_REG (REGNO (dest), SFmode);
- pat = gen_rtx_SET (VOIDmode, dest, not_a_num);
+ pat = gen_rtx_SET (dest, not_a_num);
PATTERN (insn) = pat;
INSN_CODE (insn) = -1;
}
static int
-swap_rtx_condition (rtx insn)
+swap_rtx_condition (rtx_insn *insn)
{
rtx pat = PATTERN (insn);
set up. */
static void
-compare_for_stack_reg (rtx insn, stack_ptr regstack, rtx pat_src)
+compare_for_stack_reg (rtx_insn *insn, stack_ptr regstack,
+ rtx pat_src, bool can_pop_second_op)
{
rtx *src1, *src2;
rtx src1_note, src2_note;
&& get_hard_regnum (regstack, *src2) == FIRST_STACK_REG))
&& swap_rtx_condition (insn))
{
- rtx temp;
- temp = XEXP (pat_src, 0);
- XEXP (pat_src, 0) = XEXP (pat_src, 1);
- XEXP (pat_src, 1) = temp;
+ std::swap (XEXP (pat_src, 0), XEXP (pat_src, 1));
src1 = get_true_reg (&XEXP (pat_src, 0));
src2 = get_true_reg (&XEXP (pat_src, 1));
if (src1_note)
{
- pop_stack (regstack, REGNO (XEXP (src1_note, 0)));
- replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
+ if (*src2 == CONST0_RTX (GET_MODE (*src2)))
+ {
+ /* This is `ftst' insn that can't pop register. */
+ remove_regno_note (insn, REG_DEAD, REGNO (XEXP (src1_note, 0)));
+ emit_pop_insn (insn, regstack, XEXP (src1_note, 0),
+ EMIT_AFTER);
+ }
+ else
+ {
+ pop_stack (regstack, REGNO (XEXP (src1_note, 0)));
+ replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
+ }
}
/* If the second operand dies, handle that. But if the operands are
at top (FIRST_STACK_REG) now. */
if (get_hard_regnum (regstack, XEXP (src2_note, 0)) == FIRST_STACK_REG
- && src1_note)
+ && src1_note && can_pop_second_op)
{
pop_stack (regstack, REGNO (XEXP (src2_note, 0)));
replace_reg (&XEXP (src2_note, 0), FIRST_STACK_REG + 1);
}
}
\f
-/* Substitute new registers in LOC, which is part of a debug insn.
- REGSTACK is the current register layout. */
-
-static int
-subst_stack_regs_in_debug_insn (rtx *loc, void *data)
-{
- stack_ptr regstack = (stack_ptr)data;
- int hard_regno;
-
- if (!STACK_REG_P (*loc))
- return 0;
-
- hard_regno = get_hard_regnum (regstack, *loc);
-
- /* If we can't find an active register, reset this debug insn. */
- if (hard_regno == -1)
- return 1;
-
- gcc_assert (hard_regno >= FIRST_STACK_REG);
-
- replace_reg (loc, hard_regno);
-
- return -1;
-}
-
/* Substitute hardware stack regs in debug insn INSN, using stack
layout REGSTACK. If we can't find a hardware stack reg for any of
the REGs in it, reset the debug insn. */
static void
-subst_all_stack_regs_in_debug_insn (rtx insn, struct stack_def *regstack)
+subst_all_stack_regs_in_debug_insn (rtx_insn *insn, struct stack_def *regstack)
{
- int ret = for_each_rtx (&INSN_VAR_LOCATION_LOC (insn),
- subst_stack_regs_in_debug_insn,
- regstack);
+ subrtx_ptr_iterator::array_type array;
+ FOR_EACH_SUBRTX_PTR (iter, array, &INSN_VAR_LOCATION_LOC (insn), NONCONST)
+ {
+ rtx *loc = *iter;
+ rtx x = *loc;
+ if (STACK_REG_P (x))
+ {
+ int hard_regno = get_hard_regnum (regstack, x);
- if (ret == 1)
- INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
- else
- gcc_checking_assert (ret == 0);
+ /* If we can't find an active register, reset this debug insn. */
+ if (hard_regno == -1)
+ {
+ INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
+ return;
+ }
+
+ gcc_assert (hard_regno >= FIRST_STACK_REG);
+ replace_reg (loc, hard_regno);
+ iter.skip_subrtxes ();
+ }
+ }
}
/* Substitute new registers in PAT, which is part of INSN. REGSTACK
was deleted in the process. */
static bool
-subst_stack_regs_pat (rtx insn, stack_ptr regstack, rtx pat)
+subst_stack_regs_pat (rtx_insn *insn, stack_ptr regstack, rtx pat)
{
rtx *dest, *src;
bool control_flow_insn_deleted = false;
if (get_hard_regnum (regstack, u) == -1)
{
rtx pat2 = gen_rtx_CLOBBER (VOIDmode, u);
- rtx insn2 = emit_insn_before (pat2, insn);
+ rtx_insn *insn2 = emit_insn_before (pat2, insn);
control_flow_insn_deleted
|= move_nan_for_stack_reg (insn2, regstack, u);
}
switch (GET_CODE (pat_src))
{
- case COMPARE:
- compare_for_stack_reg (insn, regstack, pat_src);
- break;
-
case CALL:
{
int count;
- for (count = hard_regno_nregs[REGNO (*dest)][GET_MODE (*dest)];
- --count >= 0;)
+ for (count = REG_NREGS (*dest); --count >= 0;)
{
regstack->reg[++regstack->top] = REGNO (*dest) + count;
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest) + count);
if (src1_hard_regnum == -1)
{
rtx pat2 = gen_rtx_CLOBBER (VOIDmode, *src1);
- rtx insn2 = emit_insn_before (pat2, insn);
+ rtx_insn *insn2 = emit_insn_before (pat2, insn);
control_flow_insn_deleted
|= move_nan_for_stack_reg (insn2, regstack, *src1);
}
if (src2_hard_regnum == -1)
{
rtx pat2 = gen_rtx_CLOBBER (VOIDmode, *src2);
- rtx insn2 = emit_insn_before (pat2, insn);
+ rtx_insn *insn2 = emit_insn_before (pat2, insn);
control_flow_insn_deleted
|= move_nan_for_stack_reg (insn2, regstack, *src2);
}
case UNSPEC:
switch (XINT (pat_src, 1))
{
- case UNSPEC_STA:
case UNSPEC_FIST:
+ case UNSPEC_FIST_ATOMIC:
case UNSPEC_FIST_FLOOR:
case UNSPEC_FIST_CEIL:
case UNSPEC_FRNDINT_FLOOR:
case UNSPEC_FRNDINT_CEIL:
case UNSPEC_FRNDINT_TRUNC:
- case UNSPEC_FRNDINT_MASK_PM:
/* Above insns operate on the top of the stack. */
replace_reg (src2, FIRST_STACK_REG + 1);
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);
- gcc_assert (GET_CODE (pat_src) == UNSPEC);
- gcc_assert (XINT (pat_src, 1) == UNSPEC_FNSTSW);
- /* Fall through. */
-
case UNSPEC_FNSTSW:
/* Combined fcomp+fnstsw generated for doing well with
CSE. When optimizing this would have been broken
up before now. */
pat_src = XVECEXP (pat_src, 0, 0);
- gcc_assert (GET_CODE (pat_src) == COMPARE);
+ if (GET_CODE (pat_src) == COMPARE)
+ goto do_compare;
- compare_for_stack_reg (insn, regstack, pat_src);
- break;
+ /* Fall through. */
+
+ case UNSPEC_NOTRAP:
+
+ pat_src = XVECEXP (pat_src, 0, 0);
+ gcc_assert (GET_CODE (pat_src) == COMPARE);
+ goto do_compare;
default:
gcc_unreachable ();
}
break;
+ case COMPARE:
+ do_compare:
+ /* `fcomi' insn can't pop two regs. */
+ compare_for_stack_reg (insn, regstack, pat_src,
+ REGNO (*dest) != FLAGS_REG);
+ break;
+
case IF_THEN_ELSE:
/* This insn requires the top of stack to be the destination. */
requirements, since record_asm_stack_regs removes any problem asm. */
static void
-subst_asm_stack_regs (rtx insn, stack_ptr regstack)
+subst_asm_stack_regs (rtx_insn *insn, stack_ptr regstack)
{
rtx body = PATTERN (insn);
- int alt;
rtx *note_reg; /* Array of note contents */
rtx **note_loc; /* Address of REG field of each note */
/* Find out what the constraints required. If no constraint
alternative matches, that is a compiler bug: we should have caught
such an insn in check_asm_stack_operands. */
- extract_insn (insn);
- constrain_operands (1);
- alt = which_alternative;
+ extract_constrain_insn (insn);
- preprocess_constraints ();
+ preprocess_constraints (insn);
+ const operand_alternative *op_alt = which_op_alt ();
get_asm_operands_in_out (body, &n_outputs, &n_inputs);
- gcc_assert (alt >= 0);
-
/* Strip SUBREGs here to make the following code simpler. */
for (i = 0; i < recog_data.n_operands; i++)
if (GET_CODE (recog_data.operand[i]) == SUBREG
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (STACK_REG_P (recog_data.operand[i])
- && reg_class_subset_p (recog_op_alt[i][alt].cl,
- FLOAT_REGS)
- && recog_op_alt[i][alt].cl != FLOAT_REGS)
+ && reg_class_subset_p (op_alt[i].cl, FLOAT_REGS)
+ && op_alt[i].cl != FLOAT_REGS)
{
/* If an operand needs to be in a particular reg in
FLOAT_REGS, the constraint was either 't' or 'u'. Since
it and swap it with whatever is already in I's place.
K is where recog_data.operand[i] is now. J is where it
should be. */
- int j, k, temp;
+ int j, k;
k = temp_stack.top - (regno - FIRST_STACK_REG);
j = (temp_stack.top
- (REGNO (recog_data.operand[i]) - FIRST_STACK_REG));
- temp = temp_stack.reg[k];
- temp_stack.reg[k] = temp_stack.reg[j];
- temp_stack.reg[j] = temp;
+ std::swap (temp_stack.reg[j], temp_stack.reg[k]);
}
}
int regnum = get_hard_regnum (regstack, clobber_reg[i]);
if (regnum >= 0)
- {
- /* Sigh - clobbers always have QImode. But replace_reg knows
- that these regs can't be MODE_INT and will assert. Just put
- the right reg there without calling replace_reg. */
-
- *clobber_loc[i] = FP_MODE_REG (regnum, DFmode);
- }
+ replace_reg (clobber_loc[i], regnum);
}
/* Now remove from REGSTACK any inputs that the asm implicitly popped. */
if (operands_match_p (clobber_reg[j], recog_data.operand[i]))
break;
- if (j < n_clobbers || recog_op_alt[i][alt].matches >= 0)
+ if (j < n_clobbers || op_alt[i].matches >= 0)
{
/* recog_data.operand[i] might not be at the top of stack.
But that's OK, because all we need to do is pop the
a control flow insn was deleted in the process. */
static bool
-subst_stack_regs (rtx insn, stack_ptr regstack)
+subst_stack_regs (rtx_insn *insn, stack_ptr regstack)
{
rtx *note_link, note;
bool control_flow_insn_deleted = false;
/* 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 (NOTE_P (insn) || INSN_DELETED_P (insn))
+ if (NOTE_P (insn) || insn->deleted ())
return control_flow_insn_deleted;
/* If this a noreturn call, we can't insert pop insns after it.
is no longer needed once this has executed. */
static void
-change_stack (rtx insn, stack_ptr old, stack_ptr new_stack, enum emit_where where)
+change_stack (rtx_insn *insn, stack_ptr old, stack_ptr new_stack,
+ enum emit_where where)
{
int reg;
- int update_end = 0;
+ machine_mode raw_mode = reg_raw_mode[FIRST_STACK_REG];
+ rtx_insn *update_end = NULL;
int i;
/* Stack adjustments for the first insn in a block update the
if (where == EMIT_AFTER)
{
if (current_block && BB_END (current_block) == insn)
- update_end = 1;
+ update_end = insn;
insn = NEXT_INSN (insn);
}
{
old->reg[++old->top] = i;
SET_HARD_REG_BIT (old->reg_set, i);
- emit_insn_before (gen_rtx_SET (VOIDmode,
- FP_MODE_REG (i, SFmode), not_a_num), insn);
+ emit_insn_before (gen_rtx_SET (FP_MODE_REG (i, SFmode), not_a_num),
+ insn);
}
/* Pop any registers that are not needed in the new block. */
/* If the destination block's stack already has a specified layout
and contains two or more registers, use a more intelligent algorithm
- to pop registers that minimizes the number number of fxchs below. */
+ to pop registers that minimizes the number of fxchs below. */
if (new_stack->top > 0)
{
bool slots[REG_STACK_SIZE];
next--;
dest = next--;
}
- emit_pop_insn (insn, old, FP_MODE_REG (old->reg[dest], DFmode),
+ emit_pop_insn (insn, old, FP_MODE_REG (old->reg[dest], raw_mode),
EMIT_BEFORE);
}
}
{
while (TEST_HARD_REG_BIT (new_stack->reg_set, old->reg[next]))
next--;
- emit_pop_insn (insn, old, FP_MODE_REG (old->reg[next], DFmode),
+ emit_pop_insn (insn, old, FP_MODE_REG (old->reg[next], raw_mode),
EMIT_BEFORE);
}
else
- emit_pop_insn (insn, old, FP_MODE_REG (old->reg[old->top], DFmode),
+ emit_pop_insn (insn, old, FP_MODE_REG (old->reg[old->top], raw_mode),
EMIT_BEFORE);
}
gcc_assert (reg != -1);
emit_swap_insn (insn, old,
- FP_MODE_REG (old->reg[reg], DFmode));
+ FP_MODE_REG (old->reg[reg], raw_mode));
}
/* See if any regs remain incorrect. If so, bring an
if (new_stack->reg[reg] != old->reg[reg])
{
emit_swap_insn (insn, old,
- FP_MODE_REG (old->reg[reg], DFmode));
+ FP_MODE_REG (old->reg[reg], raw_mode));
break;
}
} while (reg >= 0);
}
if (update_end)
- BB_END (current_block) = PREV_INSN (insn);
+ {
+ for (update_end = NEXT_INSN (update_end); update_end != insn;
+ update_end = NEXT_INSN (update_end))
+ {
+ set_block_for_insn (update_end, current_block);
+ if (INSN_P (update_end))
+ df_insn_rescan (update_end);
+ }
+ BB_END (current_block) = PREV_INSN (insn);
+ }
}
\f
/* Print stack configuration. */
Note that we are inserting converted code here. This code is
never seen by the convert_regs pass. */
- FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
+ FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR_FOR_FN (cfun)->succs)
{
basic_block block = e->dest;
block_info bi = BLOCK_INFO (block);
bi->stack_in.reg[++top] = reg;
- init = gen_rtx_SET (VOIDmode,
- FP_MODE_REG (FIRST_STACK_REG, SFmode),
+ init = gen_rtx_SET (FP_MODE_REG (FIRST_STACK_REG, SFmode),
not_a_num);
insert_insn_on_edge (init, e);
inserted = 1;
if (retvalue)
{
value_reg_low = REGNO (retvalue);
- value_reg_high = END_HARD_REGNO (retvalue) - 1;
+ value_reg_high = END_REGNO (retvalue) - 1;
}
- output_stack = &BLOCK_INFO (EXIT_BLOCK_PTR)->stack_in;
+ output_stack = &BLOCK_INFO (EXIT_BLOCK_PTR_FOR_FN (cfun))->stack_in;
if (value_reg_low == -1)
output_stack->top = -1;
else
}
else
{
- rtx seq, after;
+ rtx_insn *seq;
+ rtx_note *after;
current_block = NULL;
start_sequence ();
starting_stack_p = false;
- FOR_EACH_BB (bb)
- if (bb != ENTRY_BLOCK_PTR)
+ FOR_EACH_BB_FN (bb, cfun)
+ if (bb != ENTRY_BLOCK_PTR_FOR_FN (cfun))
{
edge e;
edge_iterator ei;
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)
+ if (e1->count () > e2->count ())
return e1;
- if (e1->count < e2->count)
+ if (e1->count () < e2->count ())
return e2;
/* Prefer critical edges to minimize inserting compensation code on
struct stack_def regstack;
block_info bi = BLOCK_INFO (block);
int reg;
- rtx insn, next;
+ rtx_insn *insn, *next;
bool control_flow_insn_deleted = false;
bool cfg_altered = false;
int debug_insns_with_starting_stack = 0;
/* Don't bother processing unless there is a stack reg
mentioned or if it's a CALL_INSN. */
- if (DEBUG_INSN_P (insn))
+ if (DEBUG_BIND_INSN_P (insn))
{
if (starting_stack_p)
debug_insns_with_starting_stack++;
for (insn = BB_HEAD (block); debug_insns_with_starting_stack;
insn = NEXT_INSN (insn))
{
- if (!DEBUG_INSN_P (insn))
+ if (!DEBUG_BIND_INSN_P (insn))
continue;
debug_insns_with_starting_stack--;
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);
+ set = gen_rtx_SET (FP_MODE_REG (reg, SFmode), not_a_num);
insn = emit_insn_after (set, insn);
control_flow_insn_deleted |= subst_stack_regs (insn, ®stack);
}
is only processed after all its predecessors. The number of predecessors
of every block has already been computed. */
- stack = XNEWVEC (basic_block, n_basic_blocks);
+ stack = XNEWVEC (basic_block, n_basic_blocks_for_fn (cfun));
sp = stack;
*sp++ = block;
/* Construct the desired stack for function exit. */
convert_regs_exit ();
- BLOCK_INFO (EXIT_BLOCK_PTR)->done = 1;
+ BLOCK_INFO (EXIT_BLOCK_PTR_FOR_FN (cfun))->done = 1;
/* ??? Future: process inner loops first, and give them arbitrary
initial stacks which emit_swap_insn can modify. This ought to
prevent double fxch that often appears at the head of a loop. */
/* Process all blocks reachable from all entry points. */
- FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
+ FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR_FOR_FN (cfun)->succs)
cfg_altered |= convert_regs_2 (e->dest);
/* ??? Process all unreachable blocks. Though there's no excuse
for keeping these even when not optimizing. */
- FOR_EACH_BB (b)
+ FOR_EACH_BB_FN (b, cfun)
{
block_info bi = BLOCK_INFO (b);
/* Set up block info for each basic block. */
alloc_aux_for_blocks (sizeof (struct block_info_def));
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, cfun)
{
block_info bi = BLOCK_INFO (bb);
edge_iterator ei;
FOR_EACH_EDGE (e, ei, bb->preds)
if (!(e->flags & EDGE_DFS_BACK)
- && e->src != ENTRY_BLOCK_PTR)
+ && e->src != ENTRY_BLOCK_PTR_FOR_FN (cfun))
bi->predecessors++;
/* Set current register status at last instruction `uninitialized'. */
/* Create the replacement registers up front. */
for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++)
{
- enum machine_mode mode;
- for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT);
- mode != VOIDmode;
- mode = GET_MODE_WIDER_MODE (mode))
+ machine_mode mode;
+ FOR_EACH_MODE_IN_CLASS (mode, MODE_FLOAT)
FP_MODE_REG (i, mode) = gen_rtx_REG (mode, i);
- for (mode = GET_CLASS_NARROWEST_MODE (MODE_COMPLEX_FLOAT);
- mode != VOIDmode;
- mode = GET_MODE_WIDER_MODE (mode))
+ FOR_EACH_MODE_IN_CLASS (mode, MODE_COMPLEX_FLOAT)
FP_MODE_REG (i, mode) = gen_rtx_REG (mode, i);
}
REAL_VALUE_TYPE r;
real_nan (&r, "", 1, SFmode);
- not_a_num = CONST_DOUBLE_FROM_REAL_VALUE (r, SFmode);
+ not_a_num = const_double_from_real_value (r, SFmode);
not_a_num = force_const_mem (SFmode, not_a_num);
}
}
#endif /* STACK_REGS */
\f
-static bool
-gate_handle_stack_regs (void)
-{
-#ifdef STACK_REGS
- return 1;
-#else
- return 0;
-#endif
-}
-
namespace {
const pass_data pass_data_stack_regs =
RTL_PASS, /* type */
"*stack_regs", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_gate */
- false, /* has_execute */
TV_REG_STACK, /* tv_id */
0, /* properties_required */
0, /* properties_provided */
{}
/* opt_pass methods: */
- bool gate () { return gate_handle_stack_regs (); }
+ virtual bool gate (function *)
+ {
+#ifdef STACK_REGS
+ return true;
+#else
+ return false;
+#endif
+ }
}; // class pass_stack_regs
RTL_PASS, /* type */
"stack", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- false, /* has_gate */
- true, /* has_execute */
TV_REG_STACK, /* tv_id */
0, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- ( TODO_df_finish | TODO_verify_rtl_sharing ), /* todo_flags_finish */
+ TODO_df_finish, /* todo_flags_finish */
};
class pass_stack_regs_run : public rtl_opt_pass
{}
/* opt_pass methods: */
- unsigned int execute () { return rest_of_handle_stack_regs (); }
+ virtual unsigned int execute (function *)
+ {
+ return rest_of_handle_stack_regs ();
+ }
}; // class pass_stack_regs_run