#include "config.h"
#include "system.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"
static GTY(()) varray_type stack_regs_mentioned_data;
#ifdef STACK_REGS
#define REG_STACK_SIZE (LAST_STACK_REG - FIRST_STACK_REG + 1)
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 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 nan;
static int stack_regs_mentioned_p PARAMS ((rtx pat));
static void straighten_stack PARAMS ((rtx, stack));
static void pop_stack PARAMS ((stack, int));
static rtx *get_true_reg PARAMS ((rtx *));
static int check_asm_stack_operands PARAMS ((rtx));
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,
unsigned int));
static int get_hard_regnum PARAMS ((stack, rtx));
static rtx emit_pop_insn PARAMS ((rtx, stack, rtx,
enum emit_where));
static void emit_swap_insn PARAMS ((rtx, stack, rtx));
static void move_for_stack_reg PARAMS ((rtx, stack, rtx));
static int swap_rtx_condition_1 PARAMS ((rtx));
static int swap_rtx_condition PARAMS ((rtx));
static void compare_for_stack_reg PARAMS ((rtx, stack, rtx));
static void subst_stack_regs_pat PARAMS ((rtx, stack, rtx));
static void subst_asm_stack_regs PARAMS ((rtx, stack));
static void subst_stack_regs PARAMS ((rtx, stack));
static void change_stack PARAMS ((rtx, stack, stack,
enum emit_where));
static int convert_regs_entry PARAMS ((void));
static void convert_regs_exit PARAMS ((void));
static int convert_regs_1 PARAMS ((FILE *, basic_block));
static int convert_regs_2 PARAMS ((FILE *, basic_block));
static int convert_regs PARAMS ((FILE *));
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 *));
static int
stack_regs_mentioned_p (pat)
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 (insn)
rtx insn;
{
unsigned int uid, max;
int test;
if (! INSN_P (insn) || !stack_regs_mentioned_data)
return 0;
uid = INSN_UID (insn);
max = VARRAY_SIZE (stack_regs_mentioned_data);
if (uid >= max)
{
max = uid + uid / 20;
VARRAY_GROW (stack_regs_mentioned_data, max);
}
test = VARRAY_CHAR (stack_regs_mentioned_data, uid);
if (test == 0)
{
test = stack_regs_mentioned_p (PATTERN (insn)) ? 1 : 2;
VARRAY_CHAR (stack_regs_mentioned_data, uid) = test;
}
return test == 1;
}
static rtx ix86_flags_rtx;
static rtx
next_flags_user (insn)
rtx insn;
{
while (insn != current_block->end)
{
insn = NEXT_INSN (insn);
if (INSN_P (insn) && reg_mentioned_p (ix86_flags_rtx, PATTERN (insn)))
return insn;
if (GET_CODE (insn) == CALL_INSN)
return NULL_RTX;
}
return NULL_RTX;
}
static void
straighten_stack (insn, regstack)
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_AFTER);
}
static void
pop_stack (regstack, regno)
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;
}
}
}
void
reg_to_stack (first, file)
rtx first;
FILE *file;
{
basic_block bb;
int i;
int max_uid;
stack_regs_mentioned_data = 0;
for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++)
if (regs_ever_live[i])
break;
if (i > LAST_STACK_REG)
return;
if (!optimize)
{
count_or_remove_death_notes (NULL, 1);
life_analysis (first, file, PROP_DEATH_NOTES);
}
mark_dfs_back_edges ();
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++;
}
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)
nan = CONST0_RTX (SFmode);
else
{
nan = gen_lowpart (SFmode, GEN_INT (0x7fc00000));
nan = force_const_mem (SFmode, nan);
}
max_uid = get_max_uid ();
VARRAY_CHAR_INIT (stack_regs_mentioned_data, max_uid + 1,
"stack_regs_mentioned cache");
convert_regs (file);
free_aux_for_blocks ();
}
static void
record_label_references (insn, pat)
rtx insn, pat;
{
enum rtx_code code = GET_CODE (pat);
int i;
const char *fmt;
if (code == LABEL_REF)
{
rtx label = XEXP (pat, 0);
rtx ref;
if (GET_CODE (label) != CODE_LABEL)
abort ();
if (INSN_UID (label) == 0)
return;
for (ref = LABEL_REFS (label);
ref && ref != label;
ref = LABEL_NEXTREF (ref))
if (CONTAINING_INSN (ref) == insn)
return;
CONTAINING_INSN (pat) = insn;
LABEL_NEXTREF (pat) = LABEL_REFS (label);
LABEL_REFS (label) = pat;
return;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
record_label_references (insn, XEXP (pat, i));
if (fmt[i] == 'E')
{
int j;
for (j = 0; j < XVECLEN (pat, i); j++)
record_label_references (insn, XVECEXP (pat, i, j));
}
}
}
static rtx *
get_true_reg (pat)
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);
}
}
static int
check_asm_stack_operands (insn)
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
&& GET_CODE (SUBREG_REG (recog_data.operand[i])) == REG)
recog_data.operand[i] = SUBREG_REG (recog_data.operand[i]);
n_clobbers = 0;
if (GET_CODE (body) == PARALLEL)
{
clobber_reg = (rtx *) 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 && GET_CODE (SUBREG_REG (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].class] != 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);
return 0;
}
return 1;
}
static int
get_asm_operand_n_inputs (body)
rtx body;
{
if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS)
return ASM_OPERANDS_INPUT_LENGTH (SET_SRC (body));
else if (GET_CODE (body) == ASM_OPERANDS)
return ASM_OPERANDS_INPUT_LENGTH (body);
else if (GET_CODE (body) == PARALLEL
&& GET_CODE (XVECEXP (body, 0, 0)) == SET)
return ASM_OPERANDS_INPUT_LENGTH (SET_SRC (XVECEXP (body, 0, 0)));
else if (GET_CODE (body) == PARALLEL
&& GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS)
return ASM_OPERANDS_INPUT_LENGTH (XVECEXP (body, 0, 0));
abort ();
}
static rtx
stack_result (decl)
tree decl;
{
rtx result;
if (aggregate_value_p (DECL_RESULT (decl)))
return 0;
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);
#else
result = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (decl)), decl);
#endif
}
return result != 0 && STACK_REG_P (result) ? result : 0;
}
static void
replace_reg (reg, regno)
rtx *reg;
int regno;
{
if (regno < FIRST_STACK_REG || regno > LAST_STACK_REG
|| ! STACK_REG_P (*reg))
abort ();
switch (GET_MODE_CLASS (GET_MODE (*reg)))
{
default: abort ();
case MODE_FLOAT:
case MODE_COMPLEX_FLOAT:;
}
*reg = FP_MODE_REG (regno, GET_MODE (*reg));
}
static void
remove_regno_note (insn, note, regno)
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);
abort ();
}
static int
get_hard_regnum (regstack, reg)
stack regstack;
rtx reg;
{
int i;
if (! STACK_REG_P (reg))
abort ();
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 (insn, regstack, reg, where)
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);
if (!pop_insn)
abort ();
return pop_insn;
}
hard_regno = get_hard_regnum (regstack, reg);
if (hard_regno < FIRST_STACK_REG)
abort ();
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 (insn, regstack, reg)
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)
abort ();
if (hard_regno == FIRST_STACK_REG)
return;
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 != current_block->head)
{
rtx tmp = PREV_INSN (insn);
rtx limit = PREV_INSN (current_block->head);
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)))
{
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 (GET_CODE (i1dest) == REG && REGNO (i1dest) == FIRST_STACK_REG
&& GET_CODE (i1src) == REG
&& REGNO (i1src) == (unsigned) hard_regno - 1
&& find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX)
return;
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;
}
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, current_block->head);
else
emit_insn_before (swap_rtx, insn);
}
static void
move_for_stack_reg (insn, regstack, pat)
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;
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;
if (REGNO (src) == REGNO (dest))
abort ();
for (i = regstack->top; i >= 0; i--)
if (regstack->reg[i] == REGNO (src))
break;
if (i < 0 || get_hard_regnum (regstack, dest) >= FIRST_STACK_REG)
abort ();
if (find_regno_note (insn, REG_UNUSED, REGNO (dest)))
{
emit_pop_insn (insn, regstack, src, EMIT_AFTER);
delete_insn (insn);
return;
}
regstack->reg[i] = REGNO (dest);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (dest));
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (src));
delete_insn (insn);
return;
}
if (REGNO (src) == REGNO (dest))
{
if (find_regno_note (insn, REG_UNUSED, REGNO (dest)))
emit_pop_insn (insn, regstack, dest, EMIT_AFTER);
delete_insn (insn);
return;
}
if (get_hard_regnum (regstack, dest) >= FIRST_STACK_REG)
abort ();
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 || GET_MODE (src) == TFmode)
&& regstack->top < REG_STACK_SIZE - 1)
{
rtx push_rtx, push_insn;
rtx top_stack_reg = FP_MODE_REG (FIRST_STACK_REG, GET_MODE (src));
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));
}
replace_reg (psrc, FIRST_STACK_REG);
}
else if (STACK_REG_P (dest))
{
if (get_hard_regnum (regstack, dest) >= FIRST_STACK_REG)
abort ();
if (regstack->top >= REG_STACK_SIZE)
abort ();
regstack->reg[++regstack->top] = REGNO (dest);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (dest));
replace_reg (pdest, FIRST_STACK_REG);
}
else
abort ();
}
static int
swap_rtx_condition_1 (pat)
rtx pat;
{
const char *fmt;
int i, r = 0;
if (GET_RTX_CLASS (GET_CODE (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 (insn)
rtx insn;
{
rtx pat = PATTERN (insn);
if (GET_CODE (pat) == SET
&& GET_CODE (SET_DEST (pat)) == REG
&& 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 != current_block->end)
{
insn = NEXT_INSN (insn);
if (INSN_P (insn) && reg_mentioned_p (dest, insn))
break;
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) != 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 (insn, regstack, pat_src)
rtx insn;
stack regstack;
rtx pat_src;
{
rtx *src1, *src2;
rtx src1_note, src2_note;
rtx flags_user;
src1 = get_true_reg (&XEXP (pat_src, 0));
src2 = get_true_reg (&XEXP (pat_src, 1));
flags_user = next_flags_user (insn);
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 void
subst_stack_regs_pat (insn, regstack, pat)
rtx insn;
stack regstack;
rtx pat;
{
rtx *dest, *src;
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;
}
else if (get_hard_regnum (regstack, *src) == -1)
abort ();
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);
if (!note)
abort ();
}
remove_note (insn, note);
replace_reg (dest, LAST_STACK_REG);
}
else
{
if (! note
&& get_hard_regnum (regstack, *dest) == -1)
{
pat = gen_rtx_SET (VOIDmode,
FP_MODE_REG (REGNO (*dest), SFmode),
nan);
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 *) 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)
&& (GET_CODE (*src) == REG || GET_CODE (*src) == MEM
|| GET_CODE (*src) == CONST_DOUBLE)))
{
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:
if (*dest != cc0_rtx)
abort ();
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);
if (src1_hard_regnum == -1 || src2_hard_regnum == -1)
abort ();
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 (GET_RTX_CLASS (GET_CODE (pat_src)) == 'c'
&& 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_SIN:
case UNSPEC_COS:
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_SAHF:
pat_src = XVECEXP (pat_src, 0, 0);
if (GET_CODE (pat_src) != UNSPEC
|| XINT (pat_src, 1) != UNSPEC_FNSTSW)
abort ();
case UNSPEC_FNSTSW:
pat_src = XVECEXP (pat_src, 0, 0);
if (GET_CODE (pat_src) != COMPARE)
abort ();
compare_for_stack_reg (insn, regstack, pat_src);
break;
default:
abort ();
}
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));
if (regno != regstack->reg[regstack->top])
{
remove_regno_note (insn, REG_DEAD, regno);
emit_pop_insn (insn, regstack, XEXP (src_note[i], 0),
EMIT_AFTER);
}
else
abort ();
}
}
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:
abort ();
}
break;
}
default:
break;
}
}
static void
subst_asm_stack_regs (insn, regstack)
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;
if (alt < 0)
abort ();
for (i = 0; i < recog_data.n_operands; i++)
if (GET_CODE (recog_data.operand[i]) == SUBREG
&& GET_CODE (SUBREG_REG (recog_data.operand[i])) == REG)
{
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 = (rtx *) alloca (i * sizeof (rtx));
note_loc = (rtx **) alloca (i * sizeof (rtx *));
note_kind = (enum reg_note *) 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 && GET_CODE (SUBREG_REG (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 = (rtx *) alloca (XVECLEN (body, 0) * sizeof (rtx));
clobber_loc = (rtx **) 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 && GET_CODE (SUBREG_REG (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].class,
FLOAT_REGS)
&& recog_op_alt[i][alt].class != FLOAT_REGS)
{
int regno = get_hard_regnum (&temp_stack, recog_data.operand[i]);
if (regno < 0)
abort ();
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]);
if (regnum < 0)
abort ();
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]);
if (regnum < 0)
abort ();
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 void
subst_stack_regs (insn, regstack)
rtx insn;
stack regstack;
{
rtx *note_link, note;
int i;
if (GET_CODE (insn) == CALL_INSN)
{
int top = regstack->top;
if (top >= 0)
{
straighten_stack (PREV_INSN (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;
}
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)))
subst_stack_regs_pat (insn, regstack,
XVECEXP (PATTERN (insn), 0, i));
}
else
subst_stack_regs_pat (insn, regstack, PATTERN (insn));
}
if (GET_CODE (insn) == NOTE || INSN_DELETED_P (insn))
return;
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);
}
static void
change_stack (insn, old, new, where)
rtx insn;
stack old;
stack new;
enum emit_where where;
{
int reg;
int update_end = 0;
if (where == EMIT_AFTER)
{
if (current_block && current_block->end == insn)
update_end = 1;
insn = NEXT_INSN (insn);
}
for (reg = old->top; reg >= 0; reg--)
if (! TEST_HARD_REG_BIT (new->reg_set, old->reg[reg]))
emit_pop_insn (insn, old, FP_MODE_REG (old->reg[reg], 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);
abort ();
win:
if (old->top != new->top)
abort ();
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;
if (reg == -1)
abort ();
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--)
if (old->reg[reg] != new->reg[reg])
abort ();
}
if (update_end)
current_block->end = PREV_INSN (insn);
}
static void
print_stack (file, s)
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 ()
{
int inserted = 0;
edge e;
basic_block block;
FOR_EACH_BB_REVERSE (block)
{
block_info bi = BLOCK_INFO (block);
int reg;
bi->stack_in.top = -2;
for (reg = FIRST_STACK_REG; reg <= LAST_STACK_REG; reg++)
{
if (REGNO_REG_SET_P (block->global_live_at_end, reg))
SET_HARD_REG_BIT (bi->out_reg_set, reg);
if (REGNO_REG_SET_P (block->global_live_at_start, reg))
SET_HARD_REG_BIT (bi->stack_in.reg_set, reg);
}
}
for (e = ENTRY_BLOCK_PTR->succ; e ; e = e->succ_next)
{
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),
nan);
insert_insn_on_edge (init, e);
inserted = 1;
}
bi->stack_in.top = top;
}
return inserted;
}
static void
convert_regs_exit ()
{
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 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)
{
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");
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);
}
}
if ((e->flags & (EDGE_EH | EDGE_ABNORMAL_CALL)) == EDGE_EH)
target_stack->top = -1;
else if (e->flags & EDGE_ABNORMAL_CALL)
{
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);
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;
}
else if (block->succ->succ_next == NULL && !(e->flags & EDGE_ABNORMAL))
{
tmpstack = regstack;
change_stack (block->end, &tmpstack, target_stack,
(GET_CODE (block->end) == JUMP_INSN
? EMIT_BEFORE : EMIT_AFTER));
}
else
{
rtx seq, after;
if (e->flags & EDGE_ABNORMAL)
abort ();
current_block = NULL;
start_sequence ();
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;
}
static int
convert_regs_1 (file, block)
FILE *file;
basic_block block;
{
struct stack_def regstack;
block_info bi = BLOCK_INFO (block);
int inserted, reg;
rtx insn, next;
edge e, beste = NULL;
inserted = 0;
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;
}
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);
print_stack (file, &bi->stack_in);
}
next = block->head;
regstack = bi->stack_in;
do
{
insn = next;
next = NEXT_INSN (insn);
if (next == NULL)
abort ();
if (insn == block->end)
next = NULL;
if (stack_regs_mentioned (insn)
|| GET_CODE (insn) == CALL_INSN)
{
if (file)
{
fprintf (file, " insn %d input stack: ",
INSN_UID (insn));
print_stack (file, ®stack);
}
subst_stack_regs (insn, ®stack);
}
}
while (next);
if (file)
{
fprintf (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 (file, " %d", reg);
fprintf (file, " ]\nOutput stack: ");
print_stack (file, ®stack);
}
insn = block->end;
if (GET_CODE (insn) == JUMP_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 (file)
{
fprintf (file, "Emitting insn initializing reg %d\n",
reg);
}
set = gen_rtx_SET (VOIDmode, FP_MODE_REG (reg, SFmode),
nan);
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;
for (e = block->succ; e ; e = e->succ_next)
{
if (e->flags & EDGE_DFS_BACK
|| (e->dest == EXIT_BLOCK_PTR))
{
if (!BLOCK_INFO (e->dest)->done
&& e->dest != block)
abort ();
inserted |= compensate_edge (e, file);
}
}
for (e = block->pred; e ; e = e->pred_next)
{
if (e != beste && !(e->flags & EDGE_DFS_BACK)
&& e->src != ENTRY_BLOCK_PTR)
{
if (!BLOCK_INFO (e->src)->done)
abort ();
inserted |= compensate_edge (e, file);
}
}
return inserted;
}
static int
convert_regs_2 (file, block)
FILE *file;
basic_block block;
{
basic_block *stack, *sp;
int inserted;
stack = (basic_block *) xmalloc (sizeof (*stack) * n_basic_blocks);
sp = stack;
*sp++ = block;
inserted = 0;
do
{
edge e;
block = *--sp;
inserted |= convert_regs_1 (file, block);
BLOCK_INFO (block)->done = 1;
for (e = block->succ; e ; e = e->succ_next)
if (! (e->flags & EDGE_DFS_BACK))
{
BLOCK_INFO (e->dest)->predecessors--;
if (!BLOCK_INFO (e->dest)->predecessors)
*sp++ = e->dest;
}
}
while (sp != stack);
return inserted;
}
static int
convert_regs (file)
FILE *file;
{
int inserted;
basic_block b;
edge e;
inserted = convert_regs_entry ();
convert_regs_exit ();
BLOCK_INFO (EXIT_BLOCK_PTR)->done = 1;
for (e = ENTRY_BLOCK_PTR->succ; e ; e = e->succ_next)
inserted |= convert_regs_2 (file, e->dest);
FOR_EACH_BB (b)
{
block_info bi = BLOCK_INFO (b);
if (! bi->done)
{
int reg;
bi->stack_in.top = -1;
for (reg = LAST_STACK_REG; reg >= FIRST_STACK_REG; --reg)
if (TEST_HARD_REG_BIT (bi->stack_in.reg_set, reg))
bi->stack_in.reg[++bi->stack_in.top] = reg;
inserted |= convert_regs_2 (file, b);
}
}
clear_aux_for_blocks ();
fixup_abnormal_edges ();
if (inserted)
commit_edge_insertions ();
if (file)
fputc ('\n', file);
return inserted;
}
#endif
#include "gt-reg-stack.h"