#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "rtl.h"
#include "tm_p.h"
#include "function.h"
#include "insn-config.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
#include "toplev.h"
#include "recog.h"
#include "output.h"
#include "basic-block.h"
#include "varray.h"
#include "reload.h"
#include "ggc.h"
#include "timevar.h"
#include "tree-pass.h"
#include "target.h"
#include "vecprim.h"
#ifdef STACK_REGS
static VEC(char,heap) *stack_regs_mentioned_data;
#define REG_STACK_SIZE (LAST_STACK_REG - FIRST_STACK_REG + 1)
int regstack_completed = 0;
typedef struct stack_def
{
int top;
HARD_REG_SET reg_set;
unsigned char reg[REG_STACK_SIZE];
} *stack;
typedef struct block_info_def
{
struct stack_def stack_in;
struct stack_def stack_out;
HARD_REG_SET out_reg_set;
int done;
int predecessors;
} *block_info;
#define BLOCK_INFO(B) ((block_info) (B)->aux)
enum emit_where
{
EMIT_AFTER,
EMIT_BEFORE
};
static basic_block current_block;
static bool starting_stack_p;
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)])
static rtx not_a_num;
static int stack_regs_mentioned_p (rtx pat);
static void pop_stack (stack, int);
static rtx *get_true_reg (rtx *);
static int check_asm_stack_operands (rtx);
static int get_asm_operand_n_inputs (rtx);
static rtx stack_result (tree);
static void replace_reg (rtx *, int);
static void remove_regno_note (rtx, enum reg_note, unsigned int);
static int get_hard_regnum (stack, rtx);
static rtx emit_pop_insn (rtx, stack, rtx, enum emit_where);
static void swap_to_top(rtx, stack, rtx, rtx);
static bool move_for_stack_reg (rtx, stack, rtx);
static bool move_nan_for_stack_reg (rtx, stack, rtx);
static int swap_rtx_condition_1 (rtx);
static int swap_rtx_condition (rtx);
static void compare_for_stack_reg (rtx, stack, rtx);
static bool subst_stack_regs_pat (rtx, stack, rtx);
static void subst_asm_stack_regs (rtx, stack);
static bool subst_stack_regs (rtx, stack);
static void change_stack (rtx, stack, stack, enum emit_where);
static void print_stack (FILE *, stack);
static rtx next_flags_user (rtx);
static int
stack_regs_mentioned_p (rtx pat)
{
const char *fmt;
int i;
if (STACK_REG_P (pat))
return 1;
fmt = GET_RTX_FORMAT (GET_CODE (pat));
for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
int j;
for (j = XVECLEN (pat, i) - 1; j >= 0; j--)
if (stack_regs_mentioned_p (XVECEXP (pat, i, j)))
return 1;
}
else if (fmt[i] == 'e' && stack_regs_mentioned_p (XEXP (pat, i)))
return 1;
}
return 0;
}
int
stack_regs_mentioned (rtx insn)
{
unsigned int uid, max;
int test;
if (! INSN_P (insn) || !stack_regs_mentioned_data)
return 0;
uid = INSN_UID (insn);
max = VEC_length (char, stack_regs_mentioned_data);
if (uid >= max)
{
char *p;
unsigned int old_max = max;
max = uid + uid / 20 + 1;
VEC_safe_grow (char, heap, stack_regs_mentioned_data, max);
p = VEC_address (char, stack_regs_mentioned_data);
memset (&p[old_max], 0,
sizeof (char) * (max - old_max));
}
test = VEC_index (char, stack_regs_mentioned_data, uid);
if (test == 0)
{
test = stack_regs_mentioned_p (PATTERN (insn)) ? 1 : 2;
VEC_replace (char, stack_regs_mentioned_data, uid, test);
}
return test == 1;
}
static rtx ix86_flags_rtx;
static rtx
next_flags_user (rtx insn)
{
while (insn != BB_END (current_block))
{
insn = NEXT_INSN (insn);
if (INSN_P (insn) && reg_mentioned_p (ix86_flags_rtx, PATTERN (insn)))
return insn;
if (CALL_P (insn))
return NULL_RTX;
}
return NULL_RTX;
}
static void
straighten_stack (rtx insn, stack regstack)
{
struct stack_def temp_stack;
int top;
if (regstack->top <= 0)
return;
COPY_HARD_REG_SET (temp_stack.reg_set, regstack->reg_set);
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_BEFORE);
}
static void
pop_stack (stack regstack, int regno)
{
int top = regstack->top;
CLEAR_HARD_REG_BIT (regstack->reg_set, regno);
regstack->top--;
if (regstack->reg [top] != regno)
{
int i;
for (i = regstack->top; i >= 0; i--)
if (regstack->reg [i] == regno)
{
int j;
for (j = i; j < top; j++)
regstack->reg [j] = regstack->reg [j + 1];
break;
}
}
}
static rtx *
get_true_reg (rtx *pat)
{
for (;;)
switch (GET_CODE (*pat))
{
case SUBREG:
{
rtx subreg;
if (FP_REG_P (subreg = SUBREG_REG (*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;
}
}
case FLOAT:
case FIX:
case FLOAT_EXTEND:
pat = & XEXP (*pat, 0);
break;
case FLOAT_TRUNCATE:
if (!flag_unsafe_math_optimizations)
return pat;
pat = & XEXP (*pat, 0);
break;
}
}
static bool any_malformed_asm;
static int
check_asm_stack_operands (rtx insn)
{
int i;
int n_clobbers;
int malformed_asm = 0;
rtx body = PATTERN (insn);
char reg_used_as_output[FIRST_PSEUDO_REGISTER];
char implicitly_dies[FIRST_PSEUDO_REGISTER];
int alt;
rtx *clobber_reg = 0;
int n_inputs, n_outputs;
extract_insn (insn);
constrain_operands (1);
alt = which_alternative;
preprocess_constraints ();
n_inputs = get_asm_operand_n_inputs (body);
n_outputs = recog_data.n_operands - n_inputs;
if (alt < 0)
{
malformed_asm = 1;
PATTERN (insn) = gen_rtx_USE (VOIDmode, const0_rtx);
return 0;
}
for (i = 0; i < recog_data.n_operands; i++)
if (GET_CODE (recog_data.operand[i]) == SUBREG
&& REG_P (SUBREG_REG (recog_data.operand[i])))
recog_data.operand[i] = SUBREG_REG (recog_data.operand[i]);
n_clobbers = 0;
if (GET_CODE (body) == PARALLEL)
{
clobber_reg = alloca (XVECLEN (body, 0) * sizeof (rtx));
for (i = 0; i < XVECLEN (body, 0); i++)
if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER)
{
rtx clobber = XVECEXP (body, 0, i);
rtx reg = XEXP (clobber, 0);
if (GET_CODE (reg) == SUBREG && REG_P (SUBREG_REG (reg)))
reg = SUBREG_REG (reg);
if (STACK_REG_P (reg))
{
clobber_reg[n_clobbers] = reg;
n_clobbers++;
}
}
}
memset (reg_used_as_output, 0, sizeof (reg_used_as_output));
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)
{
error_for_asm (insn, "output constraint %d must specify a single register", i);
malformed_asm = 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;
}
}
for (i = FIRST_STACK_REG; i < LAST_STACK_REG + 1; i++)
if (! reg_used_as_output[i])
break;
for (; i < LAST_STACK_REG + 1; i++)
if (reg_used_as_output[i])
break;
if (i != LAST_STACK_REG + 1)
{
error_for_asm (insn, "output regs must be grouped at top of stack");
malformed_asm = 1;
}
memset (implicitly_dies, 0, sizeof (implicitly_dies));
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (STACK_REG_P (recog_data.operand[i]))
{
int j;
for (j = 0; j < n_clobbers; j++)
if (operands_match_p (clobber_reg[j], recog_data.operand[i]))
break;
if (j < n_clobbers || recog_op_alt[i][alt].matches >= 0)
implicitly_dies[REGNO (recog_data.operand[i])] = 1;
}
for (i = FIRST_STACK_REG; i < LAST_STACK_REG + 1; i++)
if (! implicitly_dies[i])
break;
for (; i < LAST_STACK_REG + 1; i++)
if (implicitly_dies[i])
break;
if (i != LAST_STACK_REG + 1)
{
error_for_asm (insn,
"implicitly popped regs must be grouped at top of stack");
malformed_asm = 1;
}
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (recog_op_alt[i][alt].matches == -1)
{
int j;
for (j = 0; j < n_outputs; j++)
if (operands_match_p (recog_data.operand[j], recog_data.operand[i]))
{
error_for_asm (insn,
"output operand %d must use %<&%> constraint", j);
malformed_asm = 1;
}
}
if (malformed_asm)
{
PATTERN (insn) = gen_rtx_USE (VOIDmode, const0_rtx);
any_malformed_asm = true;
return 0;
}
return 1;
}
static int
get_asm_operand_n_inputs (rtx body)
{
switch (GET_CODE (body))
{
case SET:
gcc_assert (GET_CODE (SET_SRC (body)) == ASM_OPERANDS);
return ASM_OPERANDS_INPUT_LENGTH (SET_SRC (body));
case ASM_OPERANDS:
return ASM_OPERANDS_INPUT_LENGTH (body);
case PARALLEL:
return get_asm_operand_n_inputs (XVECEXP (body, 0, 0));
default:
gcc_unreachable ();
}
}
static rtx
stack_result (tree decl)
{
rtx result;
if (aggregate_value_p (DECL_RESULT (decl), decl))
return 0;
result = DECL_RTL_IF_SET (DECL_RESULT (decl));
if (result != 0)
result = targetm.calls.function_value (TREE_TYPE (DECL_RESULT (decl)),
decl, true);
return result != 0 && STACK_REG_P (result) ? result : 0;
}
static void
replace_reg (rtx *reg, int regno)
{
gcc_assert (regno >= FIRST_STACK_REG);
gcc_assert (regno <= LAST_STACK_REG);
gcc_assert (STACK_REG_P (*reg));
gcc_assert (SCALAR_FLOAT_MODE_P (GET_MODE (*reg))
|| GET_MODE_CLASS (GET_MODE (*reg)) == MODE_COMPLEX_FLOAT);
*reg = FP_MODE_REG (regno, GET_MODE (*reg));
}
static void
remove_regno_note (rtx insn, enum reg_note note, unsigned int regno)
{
rtx *note_link, this;
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)
{
*note_link = XEXP (this, 1);
return;
}
else
note_link = &XEXP (this, 1);
gcc_unreachable ();
}
static int
get_hard_regnum (stack regstack, rtx reg)
{
int i;
gcc_assert (STACK_REG_P (reg));
for (i = regstack->top; i >= 0; i--)
if (regstack->reg[i] == REGNO (reg))
break;
return i >= 0 ? (FIRST_STACK_REG + regstack->top - i) : -1;
}
static rtx
emit_pop_insn (rtx insn, stack regstack, rtx reg, enum emit_where where)
{
rtx pop_insn, pop_rtx;
int hard_regno;
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);
gcc_assert (pop_insn);
return pop_insn;
}
hard_regno = get_hard_regnum (regstack, reg);
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));
if (where == EMIT_AFTER)
pop_insn = emit_insn_after (pop_rtx, insn);
else
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),
REG_NOTES (pop_insn));
regstack->reg[regstack->top - (hard_regno - FIRST_STACK_REG)]
= regstack->reg[regstack->top];
regstack->top -= 1;
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (reg));
return pop_insn;
}
static void
emit_swap_insn (rtx insn, stack regstack, rtx reg)
{
int hard_regno;
rtx swap_rtx;
int tmp, other_reg;
rtx i1;
rtx i1set = NULL_RTX;
hard_regno = get_hard_regnum (regstack, reg);
if (hard_regno == FIRST_STACK_REG)
return;
if (hard_regno == -1)
{
gcc_assert (any_malformed_asm);
regstack->reg[++regstack->top] = REGNO (reg);
return;
}
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;
i1 = NULL;
if (current_block && insn != BB_HEAD (current_block))
{
rtx tmp = PREV_INSN (insn);
rtx limit = PREV_INSN (BB_HEAD (current_block));
while (tmp != limit)
{
if (LABEL_P (tmp)
|| CALL_P (tmp)
|| NOTE_INSN_BASIC_BLOCK_P (tmp)
|| (NONJUMP_INSN_P (tmp)
&& stack_regs_mentioned (tmp)))
{
i1 = tmp;
break;
}
tmp = PREV_INSN (tmp);
}
}
if (i1 != NULL_RTX
&& (i1set = single_set (i1)) != NULL_RTX)
{
rtx i1src = *get_true_reg (&SET_SRC (i1set));
rtx i1dest = *get_true_reg (&SET_DEST (i1set));
if (REG_P (i1dest) && REGNO (i1dest) == FIRST_STACK_REG
&& REG_P (i1src)
&& REGNO (i1src) == (unsigned) hard_regno - 1
&& find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX)
return;
if (REG_P (i1dest) && REGNO (i1dest) == (unsigned) hard_regno
&& REG_P (i1src) && REGNO (i1src) == FIRST_STACK_REG
&& find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX)
return;
}
if (current_block && starting_stack_p)
{
BLOCK_INFO (current_block)->stack_in = *regstack;
starting_stack_p = false;
return;
}
swap_rtx = gen_swapxf (FP_MODE_REG (hard_regno, XFmode),
FP_MODE_REG (FIRST_STACK_REG, XFmode));
if (i1)
emit_insn_after (swap_rtx, i1);
else if (current_block)
emit_insn_before (swap_rtx, BB_HEAD (current_block));
else
emit_insn_before (swap_rtx, insn);
}
static void
swap_to_top (rtx insn, stack regstack, rtx src1, rtx src2)
{
struct stack_def temp_stack;
int regno, j, k, temp;
temp_stack = *regstack;
regno = get_hard_regnum (&temp_stack, src1);
gcc_assert (regno >= 0);
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;
}
regno = get_hard_regnum (&temp_stack, src2);
gcc_assert (regno >= 0);
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);
}
static bool
move_for_stack_reg (rtx insn, stack regstack, rtx pat)
{
rtx *psrc = get_true_reg (&SET_SRC (pat));
rtx *pdest = get_true_reg (&SET_DEST (pat));
rtx src, dest;
rtx note;
bool control_flow_insn_deleted = false;
src = *psrc; dest = *pdest;
if (STACK_REG_P (src) && STACK_REG_P (dest))
{
note = find_regno_note (insn, REG_DEAD, REGNO (src));
if (note)
{
int i;
gcc_assert (REGNO (src) != REGNO (dest));
for (i = regstack->top; i >= 0; i--)
if (regstack->reg[i] == REGNO (src))
break;
gcc_assert (get_hard_regnum (regstack, dest) < FIRST_STACK_REG);
if (i < 0)
return move_nan_for_stack_reg (insn, regstack, dest);
if (find_regno_note (insn, REG_UNUSED, REGNO (dest)))
emit_pop_insn (insn, regstack, src, EMIT_AFTER);
else
{
regstack->reg[i] = REGNO (dest);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (dest));
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (src));
}
control_flow_insn_deleted |= control_flow_insn_p (insn);
delete_insn (insn);
return control_flow_insn_deleted;
}
if (REGNO (src) == REGNO (dest))
{
if (find_regno_note (insn, REG_UNUSED, REGNO (dest)))
emit_pop_insn (insn, regstack, dest, EMIT_AFTER);
control_flow_insn_deleted |= control_flow_insn_p (insn);
delete_insn (insn);
return control_flow_insn_deleted;
}
gcc_assert (get_hard_regnum (regstack, dest) < FIRST_STACK_REG);
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);
}
else if (STACK_REG_P (src))
{
emit_swap_insn (insn, regstack, src);
note = find_regno_note (insn, REG_DEAD, REGNO (src));
if (note)
{
replace_reg (&XEXP (note, 0), FIRST_STACK_REG);
regstack->top--;
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (src));
}
else if ((GET_MODE (src) == XFmode)
&& regstack->top < REG_STACK_SIZE - 1)
{
rtx push_rtx;
rtx top_stack_reg = FP_MODE_REG (FIRST_STACK_REG, GET_MODE (src));
push_rtx = gen_movxf (top_stack_reg, top_stack_reg);
emit_insn_before (push_rtx, insn);
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_DEAD, top_stack_reg,
REG_NOTES (insn));
}
replace_reg (psrc, FIRST_STACK_REG);
}
else
{
gcc_assert (STACK_REG_P (dest));
gcc_assert (get_hard_regnum (regstack, dest) < FIRST_STACK_REG);
gcc_assert (regstack->top < REG_STACK_SIZE);
regstack->reg[++regstack->top] = REGNO (dest);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (dest));
replace_reg (pdest, FIRST_STACK_REG);
}
return control_flow_insn_deleted;
}
static bool
move_nan_for_stack_reg (rtx insn, stack regstack, rtx dest)
{
rtx pat;
dest = FP_MODE_REG (REGNO (dest), SFmode);
pat = gen_rtx_SET (VOIDmode, dest, not_a_num);
PATTERN (insn) = pat;
INSN_CODE (insn) = -1;
return move_for_stack_reg (insn, regstack, pat);
}
static int
swap_rtx_condition_1 (rtx pat)
{
const char *fmt;
int i, r = 0;
if (COMPARISON_P (pat))
{
PUT_CODE (pat, swap_condition (GET_CODE (pat)));
r = 1;
}
else
{
fmt = GET_RTX_FORMAT (GET_CODE (pat));
for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
int j;
for (j = XVECLEN (pat, i) - 1; j >= 0; j--)
r |= swap_rtx_condition_1 (XVECEXP (pat, i, j));
}
else if (fmt[i] == 'e')
r |= swap_rtx_condition_1 (XEXP (pat, i));
}
}
return r;
}
static int
swap_rtx_condition (rtx insn)
{
rtx pat = PATTERN (insn);
if (GET_CODE (pat) == SET
&& REG_P (SET_DEST (pat))
&& REGNO (SET_DEST (pat)) == FLAGS_REG)
{
insn = next_flags_user (insn);
if (insn == NULL_RTX)
return 0;
pat = PATTERN (insn);
}
if (GET_CODE (pat) == SET
&& GET_CODE (SET_SRC (pat)) == UNSPEC
&& XINT (SET_SRC (pat), 1) == UNSPEC_FNSTSW)
{
rtx dest = SET_DEST (pat);
while (insn != BB_END (current_block))
{
insn = NEXT_INSN (insn);
if (INSN_P (insn) && reg_mentioned_p (dest, insn))
break;
if (CALL_P (insn))
return 0;
}
if (insn == BB_END (current_block))
return 0;
pat = PATTERN (insn);
if (GET_CODE (pat) != SET
|| GET_CODE (SET_SRC (pat)) != UNSPEC
|| XINT (SET_SRC (pat), 1) != UNSPEC_SAHF
|| ! dead_or_set_p (insn, dest))
return 0;
insn = next_flags_user (insn);
if (insn == NULL_RTX)
return 0;
pat = PATTERN (insn);
}
if (swap_rtx_condition_1 (pat))
{
int fail = 0;
INSN_CODE (insn) = -1;
if (recog_memoized (insn) == -1)
fail = 1;
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;
}
static void
compare_for_stack_reg (rtx insn, stack regstack, rtx pat_src)
{
rtx *src1, *src2;
rtx src1_note, src2_note;
src1 = get_true_reg (&XEXP (pat_src, 0));
src2 = get_true_reg (&XEXP (pat_src, 1));
if ((! STACK_REG_P (*src1)
|| (STACK_REG_P (*src2)
&& 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;
src1 = get_true_reg (&XEXP (pat_src, 0));
src2 = get_true_reg (&XEXP (pat_src, 1));
INSN_CODE (insn) = -1;
}
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
if (STACK_REG_P (*src2))
src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2));
else
src2_note = NULL_RTX;
emit_swap_insn (insn, regstack, *src1);
replace_reg (src1, FIRST_STACK_REG);
if (STACK_REG_P (*src2))
replace_reg (src2, get_hard_regnum (regstack, *src2));
if (src1_note)
{
pop_stack (regstack, REGNO (XEXP (src1_note, 0)));
replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
}
if (src2_note
&& ! (STACK_REG_P (*src1) && STACK_REG_P (*src2)
&& REGNO (*src1) == REGNO (*src2)))
{
if (get_hard_regnum (regstack, XEXP (src2_note, 0)) == FIRST_STACK_REG
&& src1_note)
{
pop_stack (regstack, REGNO (XEXP (src2_note, 0)));
replace_reg (&XEXP (src2_note, 0), FIRST_STACK_REG + 1);
}
else
{
remove_regno_note (insn, REG_DEAD, REGNO (XEXP (src2_note, 0)));
emit_pop_insn (insn, regstack, XEXP (src2_note, 0),
EMIT_AFTER);
}
}
}
static bool
subst_stack_regs_pat (rtx insn, stack regstack, rtx pat)
{
rtx *dest, *src;
bool control_flow_insn_deleted = false;
switch (GET_CODE (pat))
{
case USE:
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 control_flow_insn_deleted;
}
else
gcc_assert (get_hard_regnum (regstack, *src) != -1);
break;
case CLOBBER:
{
rtx note;
dest = get_true_reg (&XEXP (pat, 0));
if (STACK_REG_P (*dest))
{
note = find_reg_note (insn, REG_DEAD, *dest);
if (pat != PATTERN (insn))
{
if (note)
emit_pop_insn (insn, regstack, *dest, EMIT_BEFORE);
else
{
note = find_reg_note (insn, REG_UNUSED, *dest);
gcc_assert (note);
}
remove_note (insn, note);
replace_reg (dest, FIRST_STACK_REG + 1);
}
else
{
if (!note)
{
rtx t = *dest;
if (COMPLEX_MODE_P (GET_MODE (t)))
{
rtx u = FP_MODE_REG (REGNO (t) + 1, SFmode);
if (get_hard_regnum (regstack, u) == -1)
{
rtx pat2 = gen_rtx_CLOBBER (VOIDmode, u);
rtx insn2 = emit_insn_before (pat2, insn);
control_flow_insn_deleted
|= move_nan_for_stack_reg (insn2, regstack, u);
}
}
if (get_hard_regnum (regstack, t) == -1)
control_flow_insn_deleted
|= move_nan_for_stack_reg (insn, regstack, t);
}
}
}
break;
}
case SET:
{
rtx *src1 = (rtx *) 0, *src2;
rtx src1_note, src2_note;
rtx pat_src;
dest = get_true_reg (&SET_DEST (pat));
src = get_true_reg (&SET_SRC (pat));
pat_src = SET_SRC (pat);
if (STACK_REG_P (*src)
|| (STACK_REG_P (*dest)
&& (REG_P (*src) || MEM_P (*src)
|| GET_CODE (*src) == CONST_DOUBLE)))
{
control_flow_insn_deleted |= move_for_stack_reg (insn, regstack, pat);
break;
}
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;)
{
regstack->reg[++regstack->top] = REGNO (*dest) + count;
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest) + count);
}
}
replace_reg (dest, FIRST_STACK_REG);
break;
case REG:
gcc_assert (*dest == cc0_rtx);
src1 = src;
case FLOAT_TRUNCATE:
case SQRT:
case ABS:
case NEG:
if (src1 == 0)
src1 = get_true_reg (&XEXP (pat_src, 0));
emit_swap_insn (insn, regstack, *src1);
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
if (STACK_REG_P (*dest))
replace_reg (dest, FIRST_STACK_REG);
if (src1_note)
{
replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
regstack->top--;
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src1));
}
replace_reg (src1, FIRST_STACK_REG);
break;
case MINUS:
case DIV:
case MULT:
case PLUS:
src1 = get_true_reg (&XEXP (pat_src, 0));
src2 = get_true_reg (&XEXP (pat_src, 1));
if (STACK_REG_P (*src1))
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
else
src1_note = NULL_RTX;
if (STACK_REG_P (*src2))
src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2));
else
src2_note = NULL_RTX;
if (! STACK_REG_P (*src1) || ! STACK_REG_P (*src2))
emit_swap_insn (insn, regstack, *dest);
else
{
int src1_hard_regnum, src2_hard_regnum;
src1_hard_regnum = get_hard_regnum (regstack, *src1);
src2_hard_regnum = get_hard_regnum (regstack, *src2);
gcc_assert (src1_hard_regnum != -1);
gcc_assert (src2_hard_regnum != -1);
if (src1_hard_regnum != FIRST_STACK_REG
&& src2_hard_regnum != FIRST_STACK_REG)
emit_swap_insn (insn, regstack, *dest);
}
if (STACK_REG_P (*src1))
replace_reg (src1, get_hard_regnum (regstack, *src1));
if (STACK_REG_P (*src2))
replace_reg (src2, get_hard_regnum (regstack, *src2));
if (src1_note)
{
rtx src1_reg = XEXP (src1_note, 0);
if (REGNO (src1_reg) == regstack->reg[regstack->top])
{
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, get_hard_regnum (regstack, *dest));
}
else
{
int regno = get_hard_regnum (regstack, src1_reg);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, regno);
regstack->reg[regstack->top - (regno - FIRST_STACK_REG)]
= regstack->reg[regstack->top];
}
CLEAR_HARD_REG_BIT (regstack->reg_set,
REGNO (XEXP (src1_note, 0)));
replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
regstack->top--;
}
else if (src2_note)
{
rtx src2_reg = XEXP (src2_note, 0);
if (REGNO (src2_reg) == regstack->reg[regstack->top])
{
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, get_hard_regnum (regstack, *dest));
}
else
{
int regno = get_hard_regnum (regstack, src2_reg);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, regno);
regstack->reg[regstack->top - (regno - FIRST_STACK_REG)]
= regstack->reg[regstack->top];
}
CLEAR_HARD_REG_BIT (regstack->reg_set,
REGNO (XEXP (src2_note, 0)));
replace_reg (&XEXP (src2_note, 0), FIRST_STACK_REG);
regstack->top--;
}
else
{
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, get_hard_regnum (regstack, *dest));
}
if (COMMUTATIVE_ARITH_P (pat_src)
&& REG_P (*src1) && REG_P (*src2)
&& REGNO (*src1) != REGNO (*dest))
{
int tmp = REGNO (*src1);
replace_reg (src1, REGNO (*src2));
replace_reg (src2, tmp);
}
break;
case UNSPEC:
switch (XINT (pat_src, 1))
{
case UNSPEC_FIST:
case UNSPEC_FIST_FLOOR:
case UNSPEC_FIST_CEIL:
src1 = get_true_reg (&XVECEXP (pat_src, 0, 0));
emit_swap_insn (insn, regstack, *src1);
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
if (STACK_REG_P (*dest))
replace_reg (dest, FIRST_STACK_REG);
if (src1_note)
{
replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
regstack->top--;
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src1));
}
replace_reg (src1, FIRST_STACK_REG);
break;
case UNSPEC_SIN:
case UNSPEC_COS:
case UNSPEC_FRNDINT:
case UNSPEC_F2XM1:
case UNSPEC_FRNDINT_FLOOR:
case UNSPEC_FRNDINT_CEIL:
case UNSPEC_FRNDINT_TRUNC:
case UNSPEC_FRNDINT_MASK_PM:
src1 = get_true_reg (&XVECEXP (pat_src, 0, 0));
emit_swap_insn (insn, regstack, *src1);
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
gcc_assert (!src1_note);
if (STACK_REG_P (*dest))
replace_reg (dest, FIRST_STACK_REG);
replace_reg (src1, FIRST_STACK_REG);
break;
case UNSPEC_FPATAN:
case UNSPEC_FYL2X:
case UNSPEC_FYL2XP1:
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));
swap_to_top (insn, regstack, *src1, *src2);
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);
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;
regstack->reg[++regstack->top] = REGNO (*dest);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, FIRST_STACK_REG);
break;
case UNSPEC_FSCALE_FRACT:
case UNSPEC_FPREM_F:
case UNSPEC_FPREM1_F:
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));
gcc_assert (!src1_note);
gcc_assert (!src2_note);
swap_to_top (insn, regstack, *src1, *src2);
if (STACK_REG_P (*dest)) {
regstack->reg[regstack->top] = REGNO (*dest);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, FIRST_STACK_REG);
}
replace_reg (src1, FIRST_STACK_REG);
replace_reg (src2, FIRST_STACK_REG + 1);
break;
case UNSPEC_FSCALE_EXP:
case UNSPEC_FPREM_U:
case UNSPEC_FPREM1_U:
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));
gcc_assert (!src1_note);
gcc_assert (!src2_note);
swap_to_top (insn, regstack, *src1, *src2);
if (STACK_REG_P (*dest)) {
regstack->reg[regstack->top - 1] = REGNO (*dest);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, FIRST_STACK_REG + 1);
}
replace_reg (src1, FIRST_STACK_REG);
replace_reg (src2, FIRST_STACK_REG + 1);
break;
case UNSPEC_SINCOS_COS:
case UNSPEC_TAN_ONE:
case UNSPEC_XTRACT_FRACT:
src1 = get_true_reg (&XVECEXP (pat_src, 0, 0));
emit_swap_insn (insn, regstack, *src1);
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
gcc_assert (!src1_note);
if (STACK_REG_P (*dest)) {
regstack->reg[regstack->top + 1] = REGNO (*dest);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, FIRST_STACK_REG);
}
replace_reg (src1, FIRST_STACK_REG);
break;
case UNSPEC_SINCOS_SIN:
case UNSPEC_TAN_TAN:
case UNSPEC_XTRACT_EXP:
src1 = get_true_reg (&XVECEXP (pat_src, 0, 0));
emit_swap_insn (insn, regstack, *src1);
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
gcc_assert (!src1_note);
if (STACK_REG_P (*dest)) {
regstack->reg[regstack->top] = REGNO (*dest);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, FIRST_STACK_REG + 1);
regstack->top++;
}
replace_reg (src1, FIRST_STACK_REG);
break;
case UNSPEC_SAHF:
pat_src = XVECEXP (pat_src, 0, 0);
gcc_assert (GET_CODE (pat_src) == UNSPEC);
gcc_assert (XINT (pat_src, 1) == UNSPEC_FNSTSW);
case UNSPEC_FNSTSW:
pat_src = XVECEXP (pat_src, 0, 0);
gcc_assert (GET_CODE (pat_src) == COMPARE);
compare_for_stack_reg (insn, regstack, pat_src);
break;
default:
gcc_unreachable ();
}
break;
case IF_THEN_ELSE:
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 (get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG
&& REGNO (*dest) != regstack->reg[regstack->top])
{
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);
regstack->reg[regstack->top - idx1] = REGNO (*src2);
regstack->reg[regstack->top - idx2] = REGNO (*src1);
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];
int i;
src_note[0] = 0;
src_note[1] = src1_note;
src_note[2] = src2_note;
if (STACK_REG_P (*src1))
replace_reg (src1, get_hard_regnum (regstack, *src1));
if (STACK_REG_P (*src2))
replace_reg (src2, get_hard_regnum (regstack, *src2));
for (i = 1; i <= 2; i++)
if (src_note [i])
{
int regno = REGNO (XEXP (src_note[i], 0));
gcc_assert (regno != regstack->reg[regstack->top]);
remove_regno_note (insn, REG_DEAD, regno);
emit_pop_insn (insn, regstack, XEXP (src_note[i], 0),
EMIT_AFTER);
}
}
if (get_hard_regnum (regstack, *dest) < FIRST_STACK_REG)
regstack->reg[++regstack->top] = REGNO (*dest);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, FIRST_STACK_REG);
break;
default:
gcc_unreachable ();
}
break;
}
default:
break;
}
return control_flow_insn_deleted;
}
static void
subst_asm_stack_regs (rtx insn, stack regstack)
{
rtx body = PATTERN (insn);
int alt;
rtx *note_reg;
rtx **note_loc;
enum reg_note *note_kind;
rtx *clobber_reg = 0;
rtx **clobber_loc = 0;
struct stack_def temp_stack;
int n_notes;
int n_clobbers;
rtx note;
int i;
int n_inputs, n_outputs;
if (! check_asm_stack_operands (insn))
return;
extract_insn (insn);
constrain_operands (1);
alt = which_alternative;
preprocess_constraints ();
n_inputs = get_asm_operand_n_inputs (body);
n_outputs = recog_data.n_operands - n_inputs;
gcc_assert (alt >= 0);
for (i = 0; i < recog_data.n_operands; i++)
if (GET_CODE (recog_data.operand[i]) == SUBREG
&& REG_P (SUBREG_REG (recog_data.operand[i])))
{
recog_data.operand_loc[i] = & SUBREG_REG (recog_data.operand[i]);
recog_data.operand[i] = SUBREG_REG (recog_data.operand[i]);
}
for (i = 0, note = REG_NOTES (insn); note; note = XEXP (note, 1))
i++;
note_reg = alloca (i * sizeof (rtx));
note_loc = alloca (i * sizeof (rtx *));
note_kind = alloca (i * sizeof (enum reg_note));
n_notes = 0;
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
{
rtx reg = XEXP (note, 0);
rtx *loc = & XEXP (note, 0);
if (GET_CODE (reg) == SUBREG && REG_P (SUBREG_REG (reg)))
{
loc = & SUBREG_REG (reg);
reg = SUBREG_REG (reg);
}
if (STACK_REG_P (reg)
&& (REG_NOTE_KIND (note) == REG_DEAD
|| REG_NOTE_KIND (note) == REG_UNUSED))
{
note_reg[n_notes] = reg;
note_loc[n_notes] = loc;
note_kind[n_notes] = REG_NOTE_KIND (note);
n_notes++;
}
}
n_clobbers = 0;
if (GET_CODE (body) == PARALLEL)
{
clobber_reg = alloca (XVECLEN (body, 0) * sizeof (rtx));
clobber_loc = alloca (XVECLEN (body, 0) * sizeof (rtx *));
for (i = 0; i < XVECLEN (body, 0); i++)
if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER)
{
rtx clobber = XVECEXP (body, 0, i);
rtx reg = XEXP (clobber, 0);
rtx *loc = & XEXP (clobber, 0);
if (GET_CODE (reg) == SUBREG && REG_P (SUBREG_REG (reg)))
{
loc = & SUBREG_REG (reg);
reg = SUBREG_REG (reg);
}
if (STACK_REG_P (reg))
{
clobber_reg[n_clobbers] = reg;
clobber_loc[n_clobbers] = loc;
n_clobbers++;
}
}
}
temp_stack = *regstack;
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)
{
int regno = get_hard_regnum (&temp_stack, recog_data.operand[i]);
gcc_assert (regno >= 0);
if ((unsigned int) regno != REGNO (recog_data.operand[i]))
{
int j, k, temp;
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;
}
}
change_stack (insn, regstack, &temp_stack, EMIT_BEFORE);
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (STACK_REG_P (recog_data.operand[i]))
{
int regnum = get_hard_regnum (regstack, recog_data.operand[i]);
gcc_assert (regnum >= 0);
replace_reg (recog_data.operand_loc[i], regnum);
}
for (i = 0; i < n_notes; i++)
if (note_kind[i] == REG_DEAD)
{
int regnum = get_hard_regnum (regstack, note_reg[i]);
gcc_assert (regnum >= 0);
replace_reg (note_loc[i], regnum);
}
for (i = 0; i < n_clobbers; i++)
{
int regnum = get_hard_regnum (regstack, clobber_reg[i]);
if (regnum >= 0)
{
*clobber_loc[i] = FP_MODE_REG (regnum, DFmode);
}
}
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (STACK_REG_P (recog_data.operand[i]))
{
int j;
for (j = 0; j < n_clobbers; j++)
if (operands_match_p (clobber_reg[j], recog_data.operand[i]))
break;
if (j < n_clobbers || recog_op_alt[i][alt].matches >= 0)
{
CLEAR_HARD_REG_BIT (regstack->reg_set,
regstack->reg[regstack->top]);
regstack->top--;
}
}
for (i = LAST_STACK_REG; i >= FIRST_STACK_REG; i--)
{
int j;
for (j = 0; j < n_outputs; j++)
if (STACK_REG_P (recog_data.operand[j])
&& REGNO (recog_data.operand[j]) == (unsigned) i)
{
regstack->reg[++regstack->top] = i;
SET_HARD_REG_BIT (regstack->reg_set, i);
break;
}
}
for (i = 0; i < n_outputs; i++)
if (STACK_REG_P (recog_data.operand[i]))
{
int j;
for (j = 0; j < n_notes; j++)
if (REGNO (recog_data.operand[i]) == REGNO (note_reg[j])
&& note_kind[j] == REG_UNUSED)
{
insn = emit_pop_insn (insn, regstack, recog_data.operand[i],
EMIT_AFTER);
break;
}
}
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (STACK_REG_P (recog_data.operand[i]))
{
int j;
for (j = 0; j < n_notes; j++)
if (REGNO (recog_data.operand[i]) == REGNO (note_reg[j])
&& note_kind[j] == REG_DEAD
&& TEST_HARD_REG_BIT (regstack->reg_set,
REGNO (recog_data.operand[i])))
{
insn = emit_pop_insn (insn, regstack, recog_data.operand[i],
EMIT_AFTER);
break;
}
}
}
static bool
subst_stack_regs (rtx insn, stack regstack)
{
rtx *note_link, note;
bool control_flow_insn_deleted = false;
int i;
if (CALL_P (insn))
{
int top = regstack->top;
if (top >= 0)
{
straighten_stack (insn, regstack);
while (regstack->top >= 0)
{
CLEAR_HARD_REG_BIT (regstack->reg_set, FIRST_STACK_REG + regstack->top);
regstack->top--;
}
}
}
if (stack_regs_mentioned (insn))
{
int n_operands = asm_noperands (PATTERN (insn));
if (n_operands >= 0)
{
subst_asm_stack_regs (insn, regstack);
return control_flow_insn_deleted;
}
if (GET_CODE (PATTERN (insn)) == PARALLEL)
for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
{
if (stack_regs_mentioned_p (XVECEXP (PATTERN (insn), 0, i)))
{
if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == CLOBBER)
XVECEXP (PATTERN (insn), 0, i)
= shallow_copy_rtx (XVECEXP (PATTERN (insn), 0, i));
control_flow_insn_deleted
|= subst_stack_regs_pat (insn, regstack,
XVECEXP (PATTERN (insn), 0, i));
}
}
else
control_flow_insn_deleted
|= subst_stack_regs_pat (insn, regstack, PATTERN (insn));
}
if (NOTE_P (insn) || INSN_DELETED_P (insn))
return control_flow_insn_deleted;
if (CALL_P (insn)
&& find_reg_note (insn, REG_NORETURN, NULL))
{
regstack->top = -1;
CLEAR_HARD_REG_SET (regstack->reg_set);
return control_flow_insn_deleted;
}
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)))
{
*note_link = XEXP (note, 1);
insn = emit_pop_insn (insn, regstack, XEXP (note, 0), EMIT_AFTER);
}
else
note_link = &XEXP (note, 1);
return control_flow_insn_deleted;
}
static void
change_stack (rtx insn, stack old, stack new, enum emit_where where)
{
int reg;
int update_end = 0;
if (current_block
&& starting_stack_p
&& where == EMIT_BEFORE)
{
BLOCK_INFO (current_block)->stack_in = *new;
starting_stack_p = false;
*old = *new;
return;
}
if (where == EMIT_AFTER)
{
if (current_block && BB_END (current_block) == insn)
update_end = 1;
insn = NEXT_INSN (insn);
}
if (new->top > 0)
{
bool slots[REG_STACK_SIZE];
int pops[REG_STACK_SIZE];
int next, dest, topsrc;
for (reg = 0; reg <= new->top; reg++)
slots[reg] = TEST_HARD_REG_BIT (new->reg_set, old->reg[reg]);
topsrc = -1;
for (reg = old->top; reg > new->top; reg--)
if (TEST_HARD_REG_BIT (new->reg_set, old->reg[reg]))
{
dest = -1;
for (next = 0; next <= new->top; next++)
if (!slots[next] && new->reg[next] == old->reg[reg])
{
if (next == new->top)
topsrc = reg;
slots[next] = true;
dest = next;
break;
}
pops[reg] = dest;
}
else
pops[reg] = reg;
if (topsrc != -1)
for (reg = 0; reg < new->top; reg++)
if (!slots[reg])
{
pops[topsrc] = reg;
slots[new->top] = false;
slots[reg] = true;
break;
}
next = new->top;
for (reg = old->top; reg > new->top; reg--)
{
dest = pops[reg];
if (dest == -1)
{
while (slots[next])
next--;
dest = next--;
}
emit_pop_insn (insn, old, FP_MODE_REG (old->reg[dest], DFmode),
EMIT_BEFORE);
}
}
else
{
int live, next;
live = 0;
for (reg = 0; reg <= old->top; reg++)
if (TEST_HARD_REG_BIT (new->reg_set, old->reg[reg]))
live++;
next = live;
while (old->top >= live)
if (TEST_HARD_REG_BIT (new->reg_set, old->reg[old->top]))
{
while (TEST_HARD_REG_BIT (new->reg_set, old->reg[next]))
next--;
emit_pop_insn (insn, old, FP_MODE_REG (old->reg[next], DFmode),
EMIT_BEFORE);
}
else
emit_pop_insn (insn, old, FP_MODE_REG (old->reg[old->top], DFmode),
EMIT_BEFORE);
}
if (new->top == -2)
{
new->top = old->top;
memcpy (new->reg, old->reg, sizeof (new->reg));
}
else
{
GO_IF_HARD_REG_EQUAL (old->reg_set, new->reg_set, win);
gcc_unreachable ();
win:
gcc_assert (old->top == new->top);
if (new->top != -1)
do
{
while (old->reg[old->top] != new->reg[new->top])
{
for (reg = new->top; reg >= 0; reg--)
if (new->reg[reg] == old->reg[old->top])
break;
gcc_assert (reg != -1);
emit_swap_insn (insn, old,
FP_MODE_REG (old->reg[reg], DFmode));
}
for (reg = new->top; reg >= 0; reg--)
if (new->reg[reg] != old->reg[reg])
{
emit_swap_insn (insn, old,
FP_MODE_REG (old->reg[reg], DFmode));
break;
}
} while (reg >= 0);
for (reg = old->top; reg >= 0; reg--)
gcc_assert (old->reg[reg] == new->reg[reg]);
}
if (update_end)
BB_END (current_block) = PREV_INSN (insn);
}
static void
print_stack (FILE *file, stack s)
{
if (! file)
return;
if (s->top == -2)
fprintf (file, "uninitialized\n");
else if (s->top == -1)
fprintf (file, "empty\n");
else
{
int i;
fputs ("[ ", file);
for (i = 0; i <= s->top; ++i)
fprintf (file, "%d ", s->reg[i]);
fputs ("]\n", file);
}
}
static int
convert_regs_entry (void)
{
int inserted = 0;
edge e;
edge_iterator ei;
FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
{
basic_block block = e->dest;
block_info bi = BLOCK_INFO (block);
int reg, top = -1;
for (reg = LAST_STACK_REG; reg >= FIRST_STACK_REG; --reg)
if (TEST_HARD_REG_BIT (bi->stack_in.reg_set, reg))
{
rtx init;
bi->stack_in.reg[++top] = reg;
init = gen_rtx_SET (VOIDmode,
FP_MODE_REG (FIRST_STACK_REG, SFmode),
not_a_num);
insert_insn_on_edge (init, e);
inserted = 1;
}
bi->stack_in.top = top;
}
return inserted;
}
static void
convert_regs_exit (void)
{
int value_reg_low, value_reg_high;
stack output_stack;
rtx retvalue;
retvalue = stack_result (current_function_decl);
value_reg_low = value_reg_high = -1;
if (retvalue)
{
value_reg_low = REGNO (retvalue);
value_reg_high = value_reg_low
+ hard_regno_nregs[value_reg_low][GET_MODE (retvalue)] - 1;
}
output_stack = &BLOCK_INFO (EXIT_BLOCK_PTR)->stack_in;
if (value_reg_low == -1)
output_stack->top = -1;
else
{
int reg;
output_stack->top = value_reg_high - value_reg_low;
for (reg = value_reg_low; reg <= value_reg_high; ++reg)
{
output_stack->reg[value_reg_high - reg] = reg;
SET_HARD_REG_BIT (output_stack->reg_set, reg);
}
}
}
static void
propagate_stack (edge e)
{
stack src_stack = &BLOCK_INFO (e->src)->stack_out;
stack dest_stack = &BLOCK_INFO (e->dest)->stack_in;
int reg;
dest_stack->top = -1;
for (reg = 0; reg <= src_stack->top; ++reg)
if (TEST_HARD_REG_BIT (dest_stack->reg_set, src_stack->reg[reg]))
dest_stack->reg[++dest_stack->top] = src_stack->reg[reg];
}
static bool
compensate_edge (edge e)
{
basic_block source = e->src, target = e->dest;
stack target_stack = &BLOCK_INFO (target)->stack_in;
stack source_stack = &BLOCK_INFO (source)->stack_out;
struct stack_def regstack;
int reg;
if (dump_file)
fprintf (dump_file, "Edge %d->%d: ", source->index, target->index);
gcc_assert (target_stack->top != -2);
if (target_stack->top == source_stack->top)
{
for (reg = target_stack->top; reg >= 0; --reg)
if (target_stack->reg[reg] != source_stack->reg[reg])
break;
if (reg == -1)
{
if (dump_file)
fprintf (dump_file, "no changes needed\n");
return false;
}
}
if (dump_file)
{
fprintf (dump_file, "correcting stack to ");
print_stack (dump_file, target_stack);
}
if (e->flags & EDGE_ABNORMAL_CALL)
{
gcc_assert (source_stack->top == 0 || source_stack->top == 1);
gcc_assert (target_stack->top == -1);
return false;
}
if (e->flags & EDGE_EH)
{
gcc_assert (target_stack->top == -1);
return false;
}
gcc_assert (! (e->flags & EDGE_ABNORMAL));
regstack = *source_stack;
if (EDGE_COUNT (source->succs) == 1)
{
current_block = source;
change_stack (BB_END (source), ®stack, target_stack,
(JUMP_P (BB_END (source)) ? EMIT_BEFORE : EMIT_AFTER));
}
else
{
rtx seq, after;
current_block = NULL;
start_sequence ();
after = emit_note (NOTE_INSN_DELETED);
change_stack (after, ®stack, target_stack, EMIT_BEFORE);
seq = get_insns ();
end_sequence ();
insert_insn_on_edge (seq, e);
return true;
}
return false;
}
static bool
compensate_edges (void)
{
bool inserted = false;
basic_block bb;
starting_stack_p = false;
FOR_EACH_BB (bb)
if (bb != ENTRY_BLOCK_PTR)
{
edge e;
edge_iterator ei;
FOR_EACH_EDGE (e, ei, bb->succs)
inserted |= compensate_edge (e);
}
return inserted;
}
static edge
better_edge (edge e1, edge e2)
{
if (!e1)
return e2;
if (EDGE_FREQUENCY (e1) > EDGE_FREQUENCY (e2))
return e1;
if (EDGE_FREQUENCY (e1) < EDGE_FREQUENCY (e2))
return e2;
if (e1->count > e2->count)
return e1;
if (e1->count < e2->count)
return e2;
if (EDGE_CRITICAL_P (e1) != EDGE_CRITICAL_P (e2))
return EDGE_CRITICAL_P (e1) ? e1 : e2;
return (e1->src->index < e2->src->index) ? e1 : e2;
}
static void
convert_regs_1 (basic_block block)
{
struct stack_def regstack;
block_info bi = BLOCK_INFO (block);
int reg;
rtx insn, next;
bool control_flow_insn_deleted = false;
any_malformed_asm = false;
if (bi->stack_in.top == -2)
{
edge e, beste = NULL;
edge_iterator ei;
FOR_EACH_EDGE (e, ei, block->preds)
if (BLOCK_INFO (e->src)->done)
beste = better_edge (beste, e);
if (beste)
propagate_stack (beste);
else
{
bi->stack_in.top = -1;
for (reg = LAST_STACK_REG; reg >= FIRST_STACK_REG; --reg)
if (TEST_HARD_REG_BIT (bi->stack_in.reg_set, reg))
bi->stack_in.reg[++bi->stack_in.top] = reg;
}
}
if (dump_file)
{
fprintf (dump_file, "\nBasic block %d\nInput stack: ", block->index);
print_stack (dump_file, &bi->stack_in);
}
current_block = block;
next = BB_HEAD (block);
regstack = bi->stack_in;
starting_stack_p = true;
do
{
insn = next;
next = NEXT_INSN (insn);
gcc_assert (next);
if (insn == BB_END (block))
next = NULL;
if (stack_regs_mentioned (insn)
|| CALL_P (insn))
{
if (dump_file)
{
fprintf (dump_file, " insn %d input stack: ",
INSN_UID (insn));
print_stack (dump_file, ®stack);
}
control_flow_insn_deleted |= subst_stack_regs (insn, ®stack);
starting_stack_p = false;
}
}
while (next);
if (dump_file)
{
fprintf (dump_file, "Expected live registers [");
for (reg = FIRST_STACK_REG; reg <= LAST_STACK_REG; ++reg)
if (TEST_HARD_REG_BIT (bi->out_reg_set, reg))
fprintf (dump_file, " %d", reg);
fprintf (dump_file, " ]\nOutput stack: ");
print_stack (dump_file, ®stack);
}
insn = BB_END (block);
if (JUMP_P (insn))
insn = PREV_INSN (insn);
for (reg = FIRST_STACK_REG; reg <= LAST_STACK_REG; ++reg)
{
if (TEST_HARD_REG_BIT (bi->out_reg_set, reg)
&& ! TEST_HARD_REG_BIT (regstack.reg_set, reg))
{
rtx set;
if (dump_file)
fprintf (dump_file, "Emitting insn initializing reg %d\n", reg);
set = gen_rtx_SET (VOIDmode, FP_MODE_REG (reg, SFmode), not_a_num);
insn = emit_insn_after (set, insn);
control_flow_insn_deleted |= subst_stack_regs (insn, ®stack);
}
}
if (control_flow_insn_deleted)
purge_dead_edges (block);
GO_IF_HARD_REG_EQUAL (regstack.reg_set, bi->out_reg_set, win);
gcc_assert (any_malformed_asm);
win:
bi->stack_out = regstack;
bi->done = true;
}
static void
convert_regs_2 (basic_block block)
{
basic_block *stack, *sp;
stack = XNEWVEC (basic_block, n_basic_blocks);
sp = stack;
*sp++ = block;
do
{
edge e;
edge_iterator ei;
block = *--sp;
FOR_EACH_EDGE (e, ei, block->succs)
if (! (e->flags & EDGE_DFS_BACK))
{
BLOCK_INFO (e->dest)->predecessors--;
if (!BLOCK_INFO (e->dest)->predecessors)
*sp++ = e->dest;
}
convert_regs_1 (block);
}
while (sp != stack);
free (stack);
}
static void
convert_regs (void)
{
int inserted;
basic_block b;
edge e;
edge_iterator ei;
inserted = convert_regs_entry ();
convert_regs_exit ();
BLOCK_INFO (EXIT_BLOCK_PTR)->done = 1;
FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
convert_regs_2 (e->dest);
FOR_EACH_BB (b)
{
block_info bi = BLOCK_INFO (b);
if (! bi->done)
convert_regs_2 (b);
}
inserted |= compensate_edges ();
clear_aux_for_blocks ();
fixup_abnormal_edges ();
if (inserted)
commit_edge_insertions ();
if (dump_file)
fputc ('\n', dump_file);
}
static bool
reg_to_stack (void)
{
basic_block bb;
int i;
int max_uid;
if (stack_regs_mentioned_data != NULL)
VEC_free (char, heap, stack_regs_mentioned_data);
for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++)
if (regs_ever_live[i])
break;
if (i > LAST_STACK_REG)
return false;
if (!optimize
|| ((flag_sched2_use_superblocks || flag_sched2_use_traces)
&& flag_schedule_insns_after_reload))
{
count_or_remove_death_notes (NULL, 1);
life_analysis (PROP_DEATH_NOTES);
}
mark_dfs_back_edges ();
alloc_aux_for_blocks (sizeof (struct block_info_def));
FOR_EACH_BB (bb)
{
block_info bi = BLOCK_INFO (bb);
edge_iterator ei;
edge e;
int reg;
FOR_EACH_EDGE (e, ei, bb->preds)
if (!(e->flags & EDGE_DFS_BACK)
&& e->src != ENTRY_BLOCK_PTR)
bi->predecessors++;
bi->stack_in.top = -2;
for (reg = FIRST_STACK_REG; reg <= LAST_STACK_REG; reg++)
{
if (REGNO_REG_SET_P (bb->il.rtl->global_live_at_end, reg))
SET_HARD_REG_BIT (bi->out_reg_set, reg);
if (REGNO_REG_SET_P (bb->il.rtl->global_live_at_start, reg))
SET_HARD_REG_BIT (bi->stack_in.reg_set, reg);
}
}
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))
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))
FP_MODE_REG (i, mode) = gen_rtx_REG (mode, i);
}
ix86_flags_rtx = gen_rtx_REG (CCmode, FLAGS_REG);
if (flag_pic)
not_a_num = CONST0_RTX (SFmode);
else
{
not_a_num = gen_lowpart (SFmode, GEN_INT (0x7fc00000));
not_a_num = force_const_mem (SFmode, not_a_num);
}
max_uid = get_max_uid ();
stack_regs_mentioned_data = VEC_alloc (char, heap, max_uid + 1);
memset (VEC_address (char, stack_regs_mentioned_data),
0, sizeof (char) * max_uid + 1);
convert_regs ();
free_aux_for_blocks ();
return true;
}
#endif
static bool
gate_handle_stack_regs (void)
{
#ifdef STACK_REGS
return 1;
#else
return 0;
#endif
}
static unsigned int
rest_of_handle_stack_regs (void)
{
#ifdef STACK_REGS
if (reg_to_stack () && optimize)
{
regstack_completed = 1;
if (cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_POST_REGSTACK
| (flag_crossjumping ? CLEANUP_CROSSJUMP : 0))
&& (flag_reorder_blocks || flag_reorder_blocks_and_partition))
{
reorder_basic_blocks (0);
cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_POST_REGSTACK);
}
}
else
regstack_completed = 1;
#endif
return 0;
}
struct tree_opt_pass pass_stack_regs =
{
"stack",
gate_handle_stack_regs,
rest_of_handle_stack_regs,
NULL,
NULL,
0,
TV_REG_STACK,
0,
0,
0,
0,
TODO_dump_func |
TODO_ggc_collect,
'k'
};