/* Register to Stack convert for GNU compiler.
- Copyright (C) 1992, 93-99, 2000 Free Software Foundation, Inc.
+ Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+ 1999, 2000, 2001 Free Software Foundation, Inc.
This file is part of GNU CC.
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
-#include "insn-flags.h"
#include "toplev.h"
#include "recog.h"
#include "output.h"
#include "basic-block.h"
#include "varray.h"
+#include "reload.h"
#ifdef STACK_REGS
{
int top; /* index to top stack element */
HARD_REG_SET reg_set; /* set of live registers */
- char reg[REG_STACK_SIZE]; /* register - stack mapping */
+ unsigned char reg[REG_STACK_SIZE];/* register - stack mapping */
} *stack;
/* This is used to carry information about basic blocks. It is
static int get_asm_operand_n_inputs PARAMS ((rtx));
static rtx stack_result PARAMS ((tree));
static void replace_reg PARAMS ((rtx *, int));
-static void remove_regno_note PARAMS ((rtx, enum reg_note, int));
+static void remove_regno_note PARAMS ((rtx, enum reg_note,
+ unsigned int));
static int get_hard_regnum PARAMS ((stack, rtx));
static void delete_insn_for_stacker PARAMS ((rtx));
static rtx emit_pop_insn PARAMS ((rtx, stack, rtx,
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));
\f
/* Return non-zero if any stack register is mentioned somewhere within PAT. */
unsigned int uid, max;
int test;
- if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+ if (! INSN_P (insn))
return 0;
uid = INSN_UID (insn);
{
/* Search forward looking for the first use of this value.
Stop at block boundaries. */
- /* ??? This really cries for BLOCK_END! */
- while (1)
+ while (insn != current_block->end)
{
insn = NEXT_INSN (insn);
- if (!insn)
- return NULL_RTX;
- if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
- && reg_mentioned_p (ix86_flags_rtx, PATTERN (insn)))
+ if (INSN_P (insn) && reg_mentioned_p (ix86_flags_rtx, PATTERN (insn)))
return insn;
- if (GET_CODE (insn) == JUMP_INSN
- || GET_CODE (insn) == CODE_LABEL
- || GET_CODE (insn) == CALL_INSN)
+ if (GET_CODE (insn) == CALL_INSN)
return NULL_RTX;
}
+ return NULL_RTX;
}
\f
/* Reorganise the stack into ascending numbers,
/* Ok, floating point instructions exist. If not optimizing,
build the CFG and run life analysis. */
- find_basic_blocks (first, max_reg_num (), file, 0);
+ find_basic_blocks (first, max_reg_num (), file);
count_or_remove_death_notes (NULL, 1);
- life_analysis (first, max_reg_num (), file, 0);
+ life_analysis (first, file, PROP_DEATH_NOTES);
/* Set up block info for each basic block. */
bi = (block_info) xcalloc ((n_basic_blocks + 1), sizeof (*bi));
rtx subreg;
if (FP_REG_P (subreg = SUBREG_REG (*pat)))
{
- *pat = FP_MODE_REG (REGNO (subreg) + SUBREG_WORD (*pat),
+ int regno_off = subreg_regno_offset (REGNO (subreg),
+ GET_MODE (subreg),
+ SUBREG_BYTE (*pat),
+ GET_MODE (*pat));
+ *pat = FP_MODE_REG (REGNO (subreg) + regno_off,
GET_MODE (subreg));
default:
return pat;
malformed_asm = 1;
}
else
- reg_used_as_output[REGNO (recog_data.operand[i])] = 1;
+ {
+ int j;
+
+ for (j = 0; j < n_clobbers; j++)
+ if (REGNO (recog_data.operand[i]) == REGNO (clobber_reg[j]))
+ {
+ error_for_asm (insn, "Output constraint %d cannot be specified together with \"%s\" clobber",
+ i, reg_names [REGNO (clobber_reg[j])]);
+ malformed_asm = 1;
+ break;
+ }
+ if (j == n_clobbers)
+ reg_used_as_output[REGNO (recog_data.operand[i])] = 1;
+ }
}
if (aggregate_value_p (DECL_RESULT (decl)))
return 0;
- result = DECL_RTL (DECL_RESULT (decl));
- /* ?!? What is this code supposed to do? Can this code actually
- trigger if we kick out aggregates above? */
- if (result != 0
- && ! (GET_CODE (result) == REG
- && REGNO (result) < FIRST_PSEUDO_REGISTER))
+ result = DECL_RTL_IF_SET (DECL_RESULT (decl));
+ if (result != 0)
{
#ifdef FUNCTION_OUTGOING_VALUE
result
remove_regno_note (insn, note, regno)
rtx insn;
enum reg_note note;
- int regno;
+ unsigned int regno;
{
register rtx *note_link, this;
rtx pop_insn, pop_rtx;
int hard_regno;
+ /* For complex types take care to pop both halves. These may survive in
+ CLOBBER and USE expressions. */
+ if (COMPLEX_MODE_P (GET_MODE (reg)))
+ {
+ rtx reg1 = FP_MODE_REG (REGNO (reg), DFmode);
+ rtx reg2 = FP_MODE_REG (REGNO (reg) + 1, DFmode);
+
+ pop_insn = NULL_RTX;
+ if (get_hard_regnum (regstack, reg1) >= 0)
+ pop_insn = emit_pop_insn (insn, regstack, reg1, where);
+ if (get_hard_regnum (regstack, reg2) >= 0)
+ pop_insn = emit_pop_insn (insn, regstack, reg2, where);
+ if (!pop_insn)
+ abort ();
+ return pop_insn;
+ }
+
hard_regno = get_hard_regnum (regstack, reg);
if (hard_regno < FIRST_STACK_REG)
if (current_block && insn != current_block->head)
{
rtx tmp = PREV_INSN (insn);
- while (tmp != current_block->head)
+ rtx limit = PREV_INSN (current_block->head);
+ while (tmp != limit)
{
if (GET_CODE (tmp) == CODE_LABEL
- || (GET_CODE (tmp) == NOTE
- && NOTE_LINE_NUMBER (tmp) == NOTE_INSN_BASIC_BLOCK)
+ || GET_CODE (tmp) == CALL_INSN
+ || NOTE_INSN_BASIC_BLOCK_P (tmp)
|| (GET_CODE (tmp) == INSN
&& stack_regs_mentioned (tmp)))
{
if (i1)
emit_block_insn_after (swap_rtx, i1, current_block);
else if (current_block)
- {
- i1 = emit_insn_before (swap_rtx, current_block->head);
- current_block->head = i1;
- }
+ emit_block_insn_before (swap_rtx, current_block->head, current_block);
else
emit_insn_before (swap_rtx, insn);
}
regstack->top--;
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (src));
}
- else if (GET_MODE (src) == XFmode && regstack->top < REG_STACK_SIZE - 1)
+ else if ((GET_MODE (src) == XFmode || GET_MODE (src) == TFmode)
+ && regstack->top < REG_STACK_SIZE - 1)
{
/* A 387 cannot write an XFmode value to a MEM without
clobbering the source reg. The output code can handle
stack is not full, and then write the value to memory via
a pop. */
rtx push_rtx, push_insn;
- rtx top_stack_reg = FP_MODE_REG (FIRST_STACK_REG, XFmode);
+ rtx top_stack_reg = FP_MODE_REG (FIRST_STACK_REG, GET_MODE (src));
- push_rtx = gen_movxf (top_stack_reg, top_stack_reg);
+ if (GET_MODE (src) == TFmode)
+ push_rtx = gen_movtf (top_stack_reg, top_stack_reg);
+ else
+ push_rtx = gen_movxf (top_stack_reg, top_stack_reg);
push_insn = emit_insn_before (push_rtx, insn);
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_DEAD, top_stack_reg,
REG_NOTES (insn));
/* Search forward looking for the first use of this value.
Stop at block boundaries. */
- /* ??? This really cries for BLOCK_END! */
- while (1)
+ while (insn != current_block->end)
{
insn = NEXT_INSN (insn);
- if (insn == NULL_RTX)
- return 0;
- if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
- && reg_mentioned_p (dest, insn))
+ if (INSN_P (insn) && reg_mentioned_p (dest, insn))
break;
- if (GET_CODE (insn) == JUMP_INSN)
- return 0;
- if (GET_CODE (insn) == CODE_LABEL)
+ if (GET_CODE (insn) == CALL_INSN)
return 0;
}
pat = PATTERN (insn);
}
- return swap_rtx_condition_1 (pat);
+ if (swap_rtx_condition_1 (pat))
+ {
+ int fail = 0;
+ INSN_CODE (insn) = -1;
+ if (recog_memoized (insn) == -1)
+ fail = 1;
+ /* In case the flags don't die here, recurse to try fix
+ following user too. */
+ else if (! dead_or_set_p (insn, ix86_flags_rtx))
+ {
+ insn = next_flags_user (insn);
+ if (!insn || !swap_rtx_condition (insn))
+ fail = 1;
+ }
+ if (fail)
+ {
+ swap_rtx_condition_1 (pat);
+ return 0;
+ }
+ return 1;
+ }
+ return 0;
}
/* Handle a comparison. Special care needs to be taken to avoid
PATTERN (insn) = pat;
move_for_stack_reg (insn, regstack, pat);
}
+ if (! note && COMPLEX_MODE_P (GET_MODE (*dest))
+ && get_hard_regnum (regstack, FP_MODE_REG (REGNO (*dest), DFmode)) == -1)
+ {
+ pat = gen_rtx_SET (VOIDmode,
+ FP_MODE_REG (REGNO (*dest) + 1, SFmode),
+ nan);
+ PATTERN (insn) = pat;
+ move_for_stack_reg (insn, regstack, pat);
+ }
}
}
break;
case SET:
{
- rtx *src1 = (rtx *) NULL_PTR, *src2;
+ rtx *src1 = (rtx *) 0, *src2;
rtx src1_note, src2_note;
rtx pat_src;
&& REG_P (*src1) && REG_P (*src2)
&& REGNO (*src1) != REGNO (*dest))
{
- rtx tmp = *src1;
- *src1 = *src2;
- *src2 = tmp;
+ int tmp = REGNO (*src1);
+ replace_reg (src1, REGNO (*src2));
+ replace_reg (src2, tmp);
}
break;
}
}
- /* Care for EH edges specially. The normal return path may return
- a value in st(0), but the EH path will not, and there's no need
- to add popping code to the edge. */
- if (e->flags & EDGE_EH)
+ /* Care for non-call EH edges specially. The normal return path have
+ values in registers. These will be popped en masse by the unwind
+ library. */
+ if ((e->flags & (EDGE_EH | EDGE_ABNORMAL_CALL)) == EDGE_EH)
+ target_stack->top = -1;
+
+ /* Other calls may appear to have values live in st(0), but the
+ abnormal return path will not have actually loaded the values. */
+ else if (e->flags & EDGE_ABNORMAL_CALL)
{
/* Assert that the lifetimes are as we expect -- one value
live at st(0) on the end of the source block, and no