blob: b8f30aa7c5fcfc4f817a2fbcba63b4b56ff8ee59 [file] [log] [blame]
/* Definitions of target machine for Mitsubishi D30V.
Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
Contributed by Cygnus Solutions.
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include "config.h"
#include "system.h"
#include "rtl.h"
#include "tree.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
#include "recog.h"
#include "expr.h"
#include "obstack.h"
#include "tm_p.h"
#include "except.h"
#include "function.h"
#include "toplev.h"
#include "ggc.h"
static void d30v_print_operand_memory_reference PARAMS ((FILE *, rtx));
static void d30v_build_long_insn PARAMS ((HOST_WIDE_INT, HOST_WIDE_INT,
rtx, rtx));
static void d30v_add_gc_roots PARAMS ((void));
static void d30v_init_machine_status PARAMS ((struct function *));
static void d30v_mark_machine_status PARAMS ((struct function *));
static void d30v_free_machine_status PARAMS ((struct function *));
/* Define the information needed to generate branch and scc insns. This is
stored from the compare operation. */
struct rtx_def *d30v_compare_op0;
struct rtx_def *d30v_compare_op1;
/* Cached value of d30v_stack_info */
static d30v_stack_t *d30v_stack_cache = (d30v_stack_t *)0;
/* Values of the -mbranch-cost=n string. */
int d30v_branch_cost = D30V_DEFAULT_BRANCH_COST;
const char *d30v_branch_cost_string = (const char *)0;
/* Values of the -mcond-exec=n string. */
int d30v_cond_exec = D30V_DEFAULT_MAX_CONDITIONAL_EXECUTE;
const char *d30v_cond_exec_string = (const char *)0;
/* Whether or not a hard register can accept a register */
unsigned char hard_regno_mode_ok[ (int)MAX_MACHINE_MODE ][FIRST_PSEUDO_REGISTER];
/* Whether to try and avoid moves between two different modes */
unsigned char modes_tieable_p[ (NUM_MACHINE_MODES) * (NUM_MACHINE_MODES) ];
/* Map register number to smallest register class. */
enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER];
/* Map class letter into register class */
enum reg_class reg_class_from_letter[256];
/* Sometimes certain combinations of command options do not make
sense on a particular target machine. You can define a macro
`OVERRIDE_OPTIONS' to take account of this. This macro, if
defined, is executed once just after all the command options have
been parsed.
Don't use this macro to turn on various extra optimizations for
`-O'. That is what `OPTIMIZATION_OPTIONS' is for. */
void
override_options ()
{
int regno, i, ok_p;
enum machine_mode mode1, mode2;
/* Set up the branch cost information */
if (d30v_branch_cost_string)
d30v_branch_cost = atoi (d30v_branch_cost_string);
/* Set up max # instructions to use with conditional execution */
if (d30v_cond_exec_string)
d30v_cond_exec = atoi (d30v_cond_exec_string);
/* Setup hard_regno_mode_ok/modes_tieable_p */
for (mode1 = VOIDmode;
(int)mode1 < NUM_MACHINE_MODES;
mode1 = (enum machine_mode)((int)mode1 + 1))
{
int size = GET_MODE_SIZE (mode1);
int large_p = size > UNITS_PER_WORD;
int int_p = GET_MODE_CLASS (mode1) == MODE_INT;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
{
if (mode1 == VOIDmode)
ok_p = FALSE;
else if (GPR_P (regno))
{
if (!large_p)
ok_p = TRUE;
else
ok_p = (((regno - GPR_FIRST) & 1) == 0);
}
else if (FLAG_P (regno))
ok_p = (mode1 == CCmode);
else if (CR_P (regno))
ok_p = int_p && !large_p;
else if (ACCUM_P (regno))
ok_p = (mode1 == DImode);
else if (SPECIAL_REG_P (regno))
ok_p = (mode1 == SImode);
else
ok_p = FALSE;
hard_regno_mode_ok[ (int)mode1 ][ regno ] = ok_p;
}
/* A C expression that is nonzero if it is desirable to choose
register allocation so as to avoid move instructions between a
value of mode MODE1 and a value of mode MODE2.
If `HARD_REGNO_MODE_OK (R, MODE1)' and `HARD_REGNO_MODE_OK (R,
MODE2)' are ever different for any R, then `MODES_TIEABLE_P (MODE1,
MODE2)' must be zero. */
for (mode2 = VOIDmode;
(int)mode2 <= NUM_MACHINE_MODES;
mode2 = (enum machine_mode)((int)mode2 + 1))
{
if (mode1 == mode2)
ok_p = TRUE;
#if 0
else if (GET_MODE_CLASS (mode1) == MODE_INT
&& GET_MODE_SIZE (mode1) <= UNITS_PER_WORD
&& GET_MODE_CLASS (mode2) == MODE_INT
&& GET_MODE_SIZE (mode2) <= UNITS_PER_WORD)
ok_p = TRUE;
#endif
else
ok_p = FALSE;
modes_tieable_p[ ((int)mode1 * (NUM_MACHINE_MODES)) + (int)mode2 ] = ok_p;
}
}
#if 0
for (mode1 = VOIDmode;
(int)mode1 < NUM_MACHINE_MODES;
mode1 = (enum machine_mode)((int)mode1 + 1))
{
for (mode2 = VOIDmode;
(int)mode2 <= NUM_MACHINE_MODES;
mode2 = (enum machine_mode)((int)mode2 + 1))
{
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (ok_p
&& (hard_regno_mode_ok[(int)mode1][regno]
!= hard_regno_mode_ok[(int)mode2][regno]))
error ("Bad modes_tieable_p for register %s, mode1 %s, mode2 %s",
reg_names[regno], GET_MODE_NAME (mode1),
GET_MODE_NAME (mode2));
}
}
#endif
/* A C expression whose value is a register class containing hard
register REGNO. In general there is more than one such class;
choose a class which is "minimal", meaning that no smaller class
also contains the register. */
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
{
enum reg_class class;
if (GPR_P (regno))
class = (IN_RANGE_P (regno, GPR_FIRST+2, GPR_FIRST+62)
&& ((regno - GPR_FIRST) & 1) == 0) ? EVEN_REGS : GPR_REGS;
else if (regno == FLAG_F0)
class = F0_REGS;
else if (regno == FLAG_F1)
class = F1_REGS;
else if (FLAG_P (regno))
class = OTHER_FLAG_REGS;
else if (ACCUM_P (regno))
class = ACCUM_REGS;
else if (regno == CR_RPT_C)
class = REPEAT_REGS;
else if (CR_P (regno))
class = CR_REGS;
else if (SPECIAL_REG_P (regno))
class = GPR_REGS;
else
class = NO_REGS;
regno_reg_class[regno] = class;
#if 0
{
static char *names[] = REG_CLASS_NAMES;
fprintf (stderr, "Register %s class is %s, can hold modes", reg_names[regno], names[class]);
for (mode1 = VOIDmode;
(int)mode1 < NUM_MACHINE_MODES;
mode1 = (enum machine_mode)((int)mode1 + 1))
{
if (hard_regno_mode_ok[ (int)mode1 ][ regno ])
fprintf (stderr, " %s", GET_MODE_NAME (mode1));
}
fprintf (stderr, "\n");
}
#endif
}
/* A C expression which defines the machine-dependent operand
constraint letters for register classes. If CHAR is such a
letter, the value should be the register class corresponding to
it. Otherwise, the value should be `NO_REGS'. The register
letter `r', corresponding to class `GENERAL_REGS', will not be
passed to this macro; you do not need to handle it.
The following letters are unavailable, due to being used as
constraints:
'0'..'9'
'<', '>'
'E', 'F', 'G', 'H'
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'
'Q', 'R', 'S', 'T', 'U'
'V', 'X'
'g', 'i', 'm', 'n', 'o', 'p', 'r', 's' */
for (i = 0; i < 256; i++)
reg_class_from_letter[i] = NO_REGS;
reg_class_from_letter['a'] = ACCUM_REGS;
reg_class_from_letter['b'] = BR_FLAG_REGS;
reg_class_from_letter['c'] = CR_REGS;
reg_class_from_letter['d'] = GPR_REGS;
reg_class_from_letter['e'] = EVEN_REGS;
reg_class_from_letter['f'] = FLAG_REGS;
reg_class_from_letter['l'] = REPEAT_REGS;
reg_class_from_letter['x'] = F0_REGS;
reg_class_from_letter['y'] = F1_REGS;
reg_class_from_letter['z'] = OTHER_FLAG_REGS;
d30v_add_gc_roots ();
}
/* Return true if a memory operand is a short memory operand. */
int
short_memory_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) != MEM)
return FALSE;
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
return (d30v_legitimate_address_p (mode, XEXP (op, 0), reload_completed)
== 1);
}
/* Return true if a memory operand is a long operand. */
int
long_memory_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) != MEM)
return FALSE;
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
return (d30v_legitimate_address_p (mode, XEXP (op, 0), reload_completed)
== 2);
}
/* Return true if a memory operand is valid for the D30V. */
int
d30v_memory_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) != MEM)
return FALSE;
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
return (d30v_legitimate_address_p (mode, XEXP (op, 0), reload_completed)
!= 0);
}
/* Return true if a memory operand uses a single register for the
address. */
int
single_reg_memory_operand (op, mode)
rtx op;
enum machine_mode mode;
{
rtx addr;
if (GET_CODE (op) != MEM)
return FALSE;
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
addr = XEXP (op, 0);
if (! d30v_legitimate_address_p (mode, addr, reload_completed))
return FALSE;
if (GET_CODE (addr) == SUBREG)
addr = SUBREG_REG (addr);
return (GET_CODE (addr) == REG);
}
/* Return true if a memory operand uses a constant address. */
int
const_addr_memory_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) != MEM)
return FALSE;
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (! d30v_legitimate_address_p (mode, XEXP (op, 0), reload_completed))
return FALSE;
switch (GET_CODE (XEXP (op, 0)))
{
default:
break;
case SYMBOL_REF:
case LABEL_REF:
case CONST_INT:
case CONST:
return TRUE;
}
return FALSE;
}
/* Return true if operand is a memory reference suitable for a call. */
int
call_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) != MEM)
return FALSE;
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (! d30v_legitimate_address_p (mode, XEXP (op, 0), reload_completed))
return FALSE;
switch (GET_CODE (XEXP (op, 0)))
{
default:
break;
case SUBREG:
op = SUBREG_REG (op);
if (GET_CODE (op) != REG)
return FALSE;
/* fall through */
case REG:
return (GPR_OR_PSEUDO_P (REGNO (XEXP (op, 0))));
case SYMBOL_REF:
case LABEL_REF:
case CONST_INT:
case CONST:
return TRUE;
}
return FALSE;
}
/* Return true if operand is a GPR register. */
int
gpr_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
op = SUBREG_REG (op);
}
if (GET_CODE (op) != REG)
return FALSE;
return GPR_OR_PSEUDO_P (REGNO (op));
}
/* Return true if operand is an accumulator register. */
int
accum_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
op = SUBREG_REG (op);
}
if (GET_CODE (op) != REG)
return FALSE;
return ACCUM_OR_PSEUDO_P (REGNO (op));
}
/* Return true if operand is a GPR or an accumulator register. */
int
gpr_or_accum_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
op = SUBREG_REG (op);
}
if (GET_CODE (op) != REG)
return FALSE;
if (ACCUM_P (REGNO (op)))
return TRUE;
return GPR_OR_PSEUDO_P (REGNO (op));
}
/* Return true if operand is a CR register. */
int
cr_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
op = SUBREG_REG (op);
}
if (GET_CODE (op) != REG)
return FALSE;
return CR_OR_PSEUDO_P (REGNO (op));
}
/* Return true if operand is the repeat count register. */
int
repeat_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
op = SUBREG_REG (op);
}
if (GET_CODE (op) != REG)
return FALSE;
return (REGNO (op) == CR_RPT_C || REGNO (op) >= FIRST_PSEUDO_REGISTER);
}
/* Return true if operand is a FLAG register. */
int
flag_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
op = SUBREG_REG (op);
}
if (GET_CODE (op) != REG)
return FALSE;
return FLAG_OR_PSEUDO_P (REGNO (op));
}
/* Return true if operand is either F0 or F1. */
int
br_flag_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
op = SUBREG_REG (op);
}
if (GET_CODE (op) != REG)
return FALSE;
return BR_FLAG_OR_PSEUDO_P (REGNO (op));
}
/* Return true if operand is either F0/F1 or the constants 0/1. */
int
br_flag_or_constant_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
op = SUBREG_REG (op);
}
if (GET_CODE (op) == CONST_INT)
return (INTVAL (op) == 0 || INTVAL (op) == 1);
if (GET_CODE (op) != REG)
return FALSE;
return BR_FLAG_OR_PSEUDO_P (REGNO (op));
}
/* Return true if operand is either F0 or F1, or a GPR register. */
int
gpr_or_br_flag_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
op = SUBREG_REG (op);
}
if (GET_CODE (op) != REG)
return FALSE;
return GPR_OR_PSEUDO_P (REGNO (op)) || BR_FLAG_P (REGNO (op));
}
/* Return true if operand is the F0 register. */
int
f0_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
op = SUBREG_REG (op);
}
if (GET_CODE (op) != REG)
return FALSE;
return (REGNO (op) == FLAG_F0 || REGNO (op) >= FIRST_PSEUDO_REGISTER);
}
/* Return true if operand is the F1 register. */
int
f1_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
op = SUBREG_REG (op);
}
if (GET_CODE (op) != REG)
return FALSE;
return (REGNO (op) == FLAG_F1 || REGNO (op) >= FIRST_PSEUDO_REGISTER);
}
/* Return true if operand is the F1 register. */
int
carry_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
op = SUBREG_REG (op);
}
if (GET_CODE (op) != REG)
return FALSE;
return (REGNO (op) == FLAG_CARRY || REGNO (op) >= FIRST_PSEUDO_REGISTER);
}
/* Return true if operand is a register of any flavor or a 0 of the
appropriate type. */
int
reg_or_0_operand (op, mode)
rtx op;
enum machine_mode mode;
{
switch (GET_CODE (op))
{
default:
break;
case REG:
case SUBREG:
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
return register_operand (op, mode);
case CONST_INT:
return INTVAL (op) == 0;
case CONST_DOUBLE:
return CONST_DOUBLE_HIGH (op) == 0 && CONST_DOUBLE_LOW (op) == 0;
}
return FALSE;
}
/* Return true if operand is a GPR register or a signed 6 bit immediate. */
int
gpr_or_signed6_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
op = SUBREG_REG (op);
}
if (GET_CODE (op) == CONST_INT)
return IN_RANGE_P (INTVAL (op), -32, 31);
if (GET_CODE (op) != REG)
return FALSE;
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
return GPR_OR_PSEUDO_P (REGNO (op));
}
/* Return true if operand is a GPR register or an unsigned 5 bit immediate. */
int
gpr_or_unsigned5_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
op = SUBREG_REG (op);
}
if (GET_CODE (op) == CONST_INT)
return IN_RANGE_P (INTVAL (op), 0, 31);
if (GET_CODE (op) != REG)
return FALSE;
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
return GPR_OR_PSEUDO_P (REGNO (op));
}
/* Return true if operand is a GPR register or an unsigned 6 bit immediate. */
int
gpr_or_unsigned6_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
op = SUBREG_REG (op);
}
if (GET_CODE (op) == CONST_INT)
return IN_RANGE_P (INTVAL (op), 0, 63);
if (GET_CODE (op) != REG)
return FALSE;
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
return GPR_OR_PSEUDO_P (REGNO (op));
}
/* Return true if operand is a GPR register or a constant of some form. */
int
gpr_or_constant_operand (op, mode)
rtx op;
enum machine_mode mode;
{
switch (GET_CODE (op))
{
default:
break;
case CONST_INT:
case SYMBOL_REF:
case LABEL_REF:
case CONST:
return TRUE;
case SUBREG:
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
op = SUBREG_REG (op);
/* fall through */
case REG:
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
return GPR_OR_PSEUDO_P (REGNO (op));
}
return FALSE;
}
/* Return true if operand is a GPR register or a constant of some form,
including a CONST_DOUBLE, which gpr_or_constant_operand doesn't recognize. */
int
gpr_or_dbl_const_operand (op, mode)
rtx op;
enum machine_mode mode;
{
switch (GET_CODE (op))
{
default:
break;
case CONST_INT:
case CONST_DOUBLE:
case SYMBOL_REF:
case LABEL_REF:
case CONST:
return TRUE;
case SUBREG:
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
op = SUBREG_REG (op);
/* fall through */
case REG:
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
return GPR_OR_PSEUDO_P (REGNO (op));
}
return FALSE;
}
/* Return true if operand is a gpr register or a valid memory operation. */
int
gpr_or_memory_operand (op, mode)
rtx op;
enum machine_mode mode;
{
switch (GET_CODE (op))
{
default:
break;
case SUBREG:
if (GET_CODE (SUBREG_REG (op)) != REG)
return register_operand (op, mode);
op = SUBREG_REG (op);
/* fall through */
case REG:
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
return GPR_OR_PSEUDO_P (REGNO (op));
case MEM:
return d30v_legitimate_address_p (mode, XEXP (op, 0), reload_completed);
}
return FALSE;
}
/* Return true if operand is something that can be an input for a move
operation. */
int
move_input_operand (op, mode)
rtx op;
enum machine_mode mode;
{
rtx subreg;
enum rtx_code code;
switch (GET_CODE (op))
{
default:
break;
case CONST_INT:
case CONST_DOUBLE:
case SYMBOL_REF:
case LABEL_REF:
case CONST:
return TRUE;
case SUBREG:
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
subreg = SUBREG_REG (op);
code = GET_CODE (subreg);
if (code == MEM)
return d30v_legitimate_address_p ((int)mode, XEXP (subreg, 0),
reload_completed);
return (code == REG);
case REG:
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
return TRUE;
case MEM:
if (GET_CODE (XEXP (op, 0)) == ADDRESSOF)
return TRUE;
return d30v_legitimate_address_p (mode, XEXP (op, 0),
reload_completed);
}
return FALSE;
}
/* Return true if operand is something that can be an output for a move
operation. */
int
move_output_operand (op, mode)
rtx op;
enum machine_mode mode;
{
rtx subreg;
enum rtx_code code;
switch (GET_CODE (op))
{
default:
break;
case SUBREG:
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
subreg = SUBREG_REG (op);
code = GET_CODE (subreg);
if (code == MEM)
return d30v_legitimate_address_p ((int)mode, XEXP (subreg, 0),
reload_completed);
return (code == REG);
case REG:
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
return TRUE;
case MEM:
if (GET_CODE (XEXP (op, 0)) == ADDRESSOF)
return TRUE;
return d30v_legitimate_address_p (mode, XEXP (op, 0),
reload_completed);
}
return FALSE;
}
/* Return true if operand is a signed 6 bit immediate. */
int
signed6_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (op) == CONST_INT)
return IN_RANGE_P (INTVAL (op), -32, 31);
return FALSE;
}
/* Return true if operand is an unsigned 5 bit immediate. */
int
unsigned5_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (op) == CONST_INT)
return IN_RANGE_P (INTVAL (op), 0, 31);
return FALSE;
}
/* Return true if operand is an unsigned 6 bit immediate. */
int
unsigned6_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (op) == CONST_INT)
return IN_RANGE_P (INTVAL (op), 0, 63);
return FALSE;
}
/* Return true if operand is a constant with a single bit set. */
int
bitset_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (op) == CONST_INT)
return IN_RANGE_P (exact_log2 (INTVAL (op)), 0, 31);
return FALSE;
}
/* Return true if the operator is a ==/!= test against f0 or f1 that can be
used in conditional execution. */
int
condexec_test_operator (op, mode)
rtx op;
enum machine_mode mode;
{
rtx x0, x1;
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_CODE (op) != EQ && GET_CODE (op) != NE)
return FALSE;
x0 = XEXP (op, 0);
if (GET_CODE (x0) != REG || !BR_FLAG_OR_PSEUDO_P (REGNO (x0)))
return FALSE;
x1 = XEXP (op, 1);
if (GET_CODE (x1) != CONST_INT || INTVAL (x1) != 0)
return FALSE;
return TRUE;
}
/* Return true if the operator is a ==/!= test against f0, f1, or a general
register that can be used in a branch instruction. */
int
condexec_branch_operator (op, mode)
rtx op;
enum machine_mode mode;
{
rtx x0, x1;
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_CODE (op) != EQ && GET_CODE (op) != NE)
return FALSE;
x0 = XEXP (op, 0);
if (GET_CODE (x0) == REG)
{
int regno = REGNO (x0);
if (!GPR_OR_PSEUDO_P (regno) && !BR_FLAG_P (regno))
return FALSE;
}
/* Allow the optimizer to generate things like:
(if_then_else (ne (const_int 1) (const_int 0))) */
else if (GET_CODE (x0) != CONST_INT)
return FALSE;
x1 = XEXP (op, 1);
if (GET_CODE (x1) != CONST_INT || INTVAL (x1) != 0)
return FALSE;
return TRUE;
}
/* Return true if the unary operator can be executed with conditional
execution. */
int
condexec_unary_operator (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
rtx op0;
/* Only do this after register allocation, so that we can look at the register # */
if (!reload_completed)
return FALSE;
if (GET_RTX_CLASS (GET_CODE (op)) != '1')
return FALSE;
op0 = XEXP (op, 0);
if (GET_CODE (op0) == SUBREG)
op0 = SUBREG_REG (op0);
switch (GET_CODE (op))
{
default:
break;
case ABS:
case NOT:
if (GET_MODE (op) == SImode && GET_CODE (op0) == REG && GPR_P (REGNO (op0)))
return TRUE;
break;
}
return FALSE;
}
/* Return true if the add or subtraction can be executed with conditional
execution. */
int
condexec_addsub_operator (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
rtx op0, op1;
/* Only do this after register allocation, so that we can look at the register # */
if (!reload_completed)
return FALSE;
if (GET_RTX_CLASS (GET_CODE (op)) != '2' && GET_RTX_CLASS (GET_CODE (op)) != 'c')
return FALSE;
op0 = XEXP (op, 0);
op1 = XEXP (op, 1);
if (GET_CODE (op0) == SUBREG)
op0 = SUBREG_REG (op0);
if (GET_CODE (op1) == SUBREG)
op1 = SUBREG_REG (op1);
if (GET_CODE (op0) != REG)
return FALSE;
switch (GET_CODE (op))
{
default:
break;
case PLUS:
case MINUS:
return (GET_MODE (op) == SImode && GPR_P (REGNO (op0))
&& gpr_or_constant_operand (op1, SImode));
}
return FALSE;
}
/* Return true if the binary operator can be executed with conditional
execution. We don't include add/sub here, since they have extra
clobbers for the flags registers. */
int
condexec_binary_operator (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
rtx op0, op1;
/* Only do this after register allocation, so that we can look at the register # */
if (!reload_completed)
return FALSE;
if (GET_RTX_CLASS (GET_CODE (op)) != '2' && GET_RTX_CLASS (GET_CODE (op)) != 'c')
return FALSE;
op0 = XEXP (op, 0);
op1 = XEXP (op, 1);
if (GET_CODE (op0) == SUBREG)
op0 = SUBREG_REG (op0);
if (GET_CODE (op1) == SUBREG)
op1 = SUBREG_REG (op1);
if (GET_CODE (op0) != REG)
return FALSE;
/* MULT is not included here, because it is an IU only instruction. */
switch (GET_CODE (op))
{
default:
break;
case AND:
case IOR:
case XOR:
case ASHIFTRT:
case LSHIFTRT:
case ROTATERT:
return (GET_MODE (op) == SImode && GPR_P (REGNO (op0))
&& gpr_or_constant_operand (op1, SImode));
case ASHIFT:
case ROTATE:
return (GET_MODE (op) == SImode && GPR_P (REGNO (op0))
&& GET_CODE (op1) == CONST_INT);
}
return FALSE;
}
/* Return true if the shift/rotate left operator can be executed with
conditional execution. */
int
condexec_shiftl_operator (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
rtx op0, op1;
/* Only do this after register allocation, so that we can look at the register # */
if (!reload_completed)
return FALSE;
if (GET_RTX_CLASS (GET_CODE (op)) != '2' && GET_RTX_CLASS (GET_CODE (op)) != 'c')
return FALSE;
op0 = XEXP (op, 0);
op1 = XEXP (op, 1);
if (GET_CODE (op0) == SUBREG)
op0 = SUBREG_REG (op0);
if (GET_CODE (op1) == SUBREG)
op1 = SUBREG_REG (op1);
if (GET_CODE (op0) != REG)
return FALSE;
switch (GET_CODE (op))
{
default:
break;
case ASHIFT:
case ROTATE:
return (GET_MODE (op) == SImode && GPR_P (REGNO (op0))
&& GET_CODE (op1) == NEG
&& GET_CODE (XEXP (op1, 0)) == REG
&& GPR_P (REGNO (XEXP (op1, 0))));
}
return FALSE;
}
/* Return true if the {sign,zero} extend operator from memory can be
conditionally executed. */
int
condexec_extend_operator (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
/* Only do this after register allocation, so that we can look at the register # */
if (!reload_completed)
return FALSE;
if (GET_RTX_CLASS (GET_CODE (op)) != '1')
return FALSE;
switch (GET_CODE (op))
{
default:
break;
case SIGN_EXTEND:
case ZERO_EXTEND:
if ((GET_MODE (op) == SImode && GET_MODE (XEXP (op, 0)) == QImode)
|| (GET_MODE (op) == SImode && GET_MODE (XEXP (op, 0)) == HImode)
|| (GET_MODE (op) == HImode && GET_MODE (XEXP (op, 0)) == QImode))
return TRUE;
break;
}
return FALSE;
}
/* Return true for comparisons against 0 that can be turned into a
bratnz/bratzr instruction. */
int
branch_zero_operator (op, mode)
rtx op;
enum machine_mode mode;
{
rtx x0, x1;
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_CODE (op) != EQ && GET_CODE (op) != NE)
return FALSE;
x0 = XEXP (op, 0);
if (GET_CODE (x0) != REG || !GPR_OR_PSEUDO_P (REGNO (x0)))
return FALSE;
x1 = XEXP (op, 1);
if (GET_CODE (x1) != CONST_INT || INTVAL (x1) != 0)
return FALSE;
return TRUE;
}
/* Return true if an operand is simple, suitable for use as the destination of
a conditional move */
int
cond_move_dest_operand (op, mode)
register rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
rtx addr;
if (mode != QImode && mode != HImode && mode != SImode && mode != SFmode)
return FALSE;
switch (GET_CODE (op))
{
default:
break;
case REG:
case SUBREG:
return gpr_operand (op, mode);
/* Don't allow post dec/inc, since we might not get the side effects correct. */
case MEM:
addr = XEXP (op, 0);
return (GET_CODE (addr) != POST_DEC
&& GET_CODE (addr) != POST_INC
&& d30v_legitimate_address_p (mode, addr, reload_completed));
}
return FALSE;
}
/* Return true if an operand is simple, suitable for use in a conditional move */
int
cond_move_operand (op, mode)
register rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
rtx addr;
if (mode != QImode && mode != HImode && mode != SImode && mode != SFmode)
return FALSE;
switch (GET_CODE (op))
{
default:
break;
case REG:
case SUBREG:
return gpr_operand (op, mode);
case CONST_DOUBLE:
return GET_MODE (op) == SFmode;
case CONST_INT:
case SYMBOL_REF:
case LABEL_REF:
case CONST:
return TRUE;
/* Don't allow post dec/inc, since we might not get the side effects correct. */
case MEM:
addr = XEXP (op, 0);
return (GET_CODE (addr) != POST_DEC
&& GET_CODE (addr) != POST_INC
&& d30v_legitimate_address_p (mode, addr, reload_completed));
}
return FALSE;
}
/* Return true if an operand is simple, suitable for use in conditional execution.
Unlike cond_move, we can allow auto inc/dec. */
int
cond_exec_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
if (mode != QImode && mode != HImode && mode != SImode && mode != SFmode)
return FALSE;
switch (GET_CODE (op))
{
default:
break;
case REG:
case SUBREG:
return gpr_operand (op, mode);
case CONST_DOUBLE:
return GET_MODE (op) == SFmode;
case CONST_INT:
case SYMBOL_REF:
case LABEL_REF:
case CONST:
return TRUE;
case MEM:
return memory_operand (op, mode);
}
return FALSE;
}
/* Return true if operand is a SI mode signed relational test. */
int
srelational_si_operator (op, mode)
register rtx op;
enum machine_mode mode;
{
rtx x0, x1;
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
switch (GET_CODE (op))
{
default:
return FALSE;
case EQ:
case NE:
case LT:
case LE:
case GT:
case GE:
break;
}
x0 = XEXP (op, 0);
if (GET_CODE (x0) != REG && GET_CODE (x0) != SUBREG)
return FALSE;
if (GET_MODE (x0) != SImode)
return FALSE;
x1 = XEXP (op, 1);
switch (GET_CODE (x1))
{
default:
return FALSE;
case REG:
case SUBREG:
case CONST_INT:
case LABEL_REF:
case SYMBOL_REF:
case CONST:
break;
}
return TRUE;
}
/* Return true if operand is a SI mode unsigned relational test. */
int
urelational_si_operator (op, mode)
register rtx op;
enum machine_mode mode;
{
rtx x0, x1;
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
switch (GET_CODE (op))
{
default:
return FALSE;
case LTU:
case LEU:
case GTU:
case GEU:
break;
}
x0 = XEXP (op, 0);
if (GET_CODE (x0) != REG && GET_CODE (x0) != SUBREG)
return FALSE;
if (GET_MODE (x0) != SImode)
return FALSE;
x1 = XEXP (op, 1);
switch (GET_CODE (x1))
{
default:
return FALSE;
case REG:
case SUBREG:
case CONST_INT:
case LABEL_REF:
case SYMBOL_REF:
case CONST:
break;
}
return TRUE;
}
/* Return true if operand is a DI mode relational test. */
int
relational_di_operator (op, mode)
register rtx op;
enum machine_mode mode;
{
rtx x0, x1;
if (GET_MODE (op) != mode && mode != VOIDmode)
return FALSE;
if (GET_RTX_CLASS (GET_CODE (op)) != '<')
return FALSE;
x0 = XEXP (op, 0);
if (GET_CODE (x0) != REG && GET_CODE (x0) != SUBREG)
return FALSE;
if (GET_MODE (x0) != DImode)
return FALSE;
x1 = XEXP (op, 1);
if (GET_CODE (x1) != REG && GET_CODE (x1) != SUBREG
&& GET_CODE (x1) != CONST_INT && GET_CODE (x1) != CONST_DOUBLE)
return FALSE;
return TRUE;
}
/* Calculate the stack information for the current function.
D30V stack frames look like:
high | .... |
+-------------------------------+
| Argument word #19 |
+-------------------------------+
| Argument word #18 |
+-------------------------------+
| Argument word #17 |
+-------------------------------+
| Argument word #16 |
Prev sp +-------------------------------+
| |
| Save for arguments 1..16 if |
| the func. uses stdarg/varargs |
| |
+-------------------------------+
| |
| Save area for GPR registers |
| |
+-------------------------------+
| |
| Save area for accumulators |
| |
+-------------------------------+
| |
| Local variables |
| |
+-------------------------------+
| |
| alloca space if used |
| |
+-------------------------------+
| |
| Space for outgoing arguments |
| |
low SP----> +-------------------------------+
*/
d30v_stack_t *
d30v_stack_info ()
{
static d30v_stack_t info, zero_info;
d30v_stack_t *info_ptr = &info;
tree fndecl = current_function_decl;
tree fntype = TREE_TYPE (fndecl);
int varargs_p = 0;
tree cur_arg;
tree next_arg;
int saved_gprs;
int saved_accs;
int memrefs_2words;
int memrefs_1word;
unsigned char save_gpr_p[GPR_LAST];
int i;
/* If we've already calculated the values and reload is complete, just return now */
if (d30v_stack_cache)
return d30v_stack_cache;
/* Zero all fields */
info = zero_info;
if (profile_flag)
regs_ever_live[GPR_LINK] = 1;
/* Determine if this is a stdarg function */
if (TYPE_ARG_TYPES (fntype) != 0
&& (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node))
varargs_p = 1;
else
{
/* Find the last argument, and see if it is __builtin_va_alist. */
for (cur_arg = DECL_ARGUMENTS (fndecl); cur_arg != (tree)0; cur_arg = next_arg)
{
next_arg = TREE_CHAIN (cur_arg);
if (next_arg == (tree)0)
{
if (DECL_NAME (cur_arg)
&& !strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)), "__builtin_va_alist"))
varargs_p = 1;
break;
}
}
}
/* Calculate which registers need to be saved & save area size */
saved_accs = 0;
memrefs_2words = 0;
memrefs_1word = 0;
for (i = ACCUM_FIRST; i <= ACCUM_LAST; i++)
{
if (regs_ever_live[i] && !call_used_regs[i])
{
info_ptr->save_p[i] = 2;
saved_accs++;
memrefs_2words++;
}
}
saved_gprs = 0;
for (i = GPR_FIRST; i <= GPR_LAST; i++)
{
if (regs_ever_live[i] && (!call_used_regs[i] || i == GPR_LINK))
{
save_gpr_p[i] = 1;
saved_gprs++;
}
else
save_gpr_p[i] = 0;
}
/* Determine which register pairs can be saved together with ld2w/st2w */
for (i = GPR_FIRST; i <= GPR_LAST; i++)
{
if (((i - GPR_FIRST) & 1) == 0 && save_gpr_p[i] && save_gpr_p[i+1])
{
memrefs_2words++;
info_ptr->save_p[i++] = 2;
}
else if (save_gpr_p[i])
{
memrefs_1word++;
info_ptr->save_p[i] = 1;
}
}
/* Determine various sizes */
info_ptr->varargs_p = varargs_p;
info_ptr->varargs_size = ((varargs_p)
? (GPR_ARG_LAST + 1 - GPR_ARG_FIRST) * UNITS_PER_WORD
: 0);
info_ptr->accum_size = 2 * UNITS_PER_WORD * saved_accs;
info_ptr->gpr_size = D30V_ALIGN (UNITS_PER_WORD * saved_gprs,
2 * UNITS_PER_WORD);
info_ptr->vars_size = D30V_ALIGN (get_frame_size (), 2 * UNITS_PER_WORD);
info_ptr->parm_size = D30V_ALIGN (current_function_outgoing_args_size,
2 * UNITS_PER_WORD);
info_ptr->total_size = D30V_ALIGN ((info_ptr->gpr_size
+ info_ptr->accum_size
+ info_ptr->vars_size
+ info_ptr->parm_size
+ info_ptr->varargs_size
+ current_function_pretend_args_size),
(STACK_BOUNDARY / BITS_PER_UNIT));
info_ptr->save_offset = (info_ptr->total_size
- (current_function_pretend_args_size
+ info_ptr->varargs_size
+ info_ptr->gpr_size
+ info_ptr->accum_size));
/* The link register is the last GPR saved, but there might be some padding
bytes after it, so account for that. */
info_ptr->link_offset = (info_ptr->total_size
- (current_function_pretend_args_size
+ info_ptr->varargs_size
+ (info_ptr->gpr_size
- UNITS_PER_WORD * saved_gprs)
+ UNITS_PER_WORD));
info_ptr->memrefs_varargs = info_ptr->varargs_size / (2 * UNITS_PER_WORD);
info_ptr->memrefs_2words = memrefs_2words;
info_ptr->memrefs_1word = memrefs_1word;
if (reload_completed)
d30v_stack_cache = info_ptr;
return info_ptr;
}
/* Internal function to print all of the information about the stack */
void
debug_stack_info (info)
d30v_stack_t *info;
{
int i;
if (!info)
info = d30v_stack_info ();
fprintf (stderr, "\nStack information for function %s:\n",
((current_function_decl && DECL_NAME (current_function_decl))
? IDENTIFIER_POINTER (DECL_NAME (current_function_decl))
: "<unknown>"));
fprintf (stderr, "\tsave_offset = %d\n", info->save_offset);
fprintf (stderr, "\tmemrefs_varargs = %d\n", info->memrefs_varargs);
fprintf (stderr, "\tmemrefs_2words = %d\n", info->memrefs_2words);
fprintf (stderr, "\tmemrefs_1word = %d\n", info->memrefs_1word);
fprintf (stderr, "\tvarargs_p = %d\n", info->varargs_p);
fprintf (stderr, "\tvarargs_size = %d\n", info->varargs_size);
fprintf (stderr, "\tvars_size = %d\n", info->vars_size);
fprintf (stderr, "\tparm_size = %d\n", info->parm_size);
fprintf (stderr, "\tgpr_size = %d\n", info->gpr_size);
fprintf (stderr, "\taccum_size = %d\n", info->accum_size);
fprintf (stderr, "\ttotal_size = %d\n", info->total_size);
fprintf (stderr, "\tsaved registers =");
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
if (info->save_p[i] == 2)
{
fprintf (stderr, " %s-%s", reg_names[i], reg_names[i+1]);
i++;
}
else if (info->save_p[i])
fprintf (stderr, " %s", reg_names[i]);
}
putc ('\n', stderr);
fflush (stderr);
}
/* Return non-zero if this function is known to have a null or 1 instruction epilogue. */
int
direct_return ()
{
if (reload_completed)
{
d30v_stack_t *info = d30v_stack_info ();
/* If no epilogue code is needed, can use just a simple jump */
if (info->total_size == 0)
return 1;
#if 0
/* If just a small amount of local stack was allocated and no registers
saved, skip forward branch */
if (info->total_size == info->vars_size
&& IN_RANGE_P (info->total_size, 1, 31))
return 1;
#endif
}
return 0;
}
/* A C statement (sans semicolon) for initializing the variable CUM for the
state at the beginning of the argument list. The variable has type
`CUMULATIVE_ARGS'. The value of FNTYPE is the tree node for the data type
of the function which will receive the args, or 0 if the args are to a
compiler support library function. The value of INDIRECT is nonzero when
processing an indirect call, for example a call through a function pointer.
The value of INDIRECT is zero for a call to an explicitly named function, a
library function call, or when `INIT_CUMULATIVE_ARGS' is used to find
arguments for the function being compiled.
When processing a call to a compiler support library function, LIBNAME
identifies which one. It is a `symbol_ref' rtx which contains the name of
the function, as a string. LIBNAME is 0 when an ordinary C function call is
being processed. Thus, each time this macro is called, either LIBNAME or
FNTYPE is nonzero, but never both of them at once. */
void
d30v_init_cumulative_args (cum, fntype, libname, indirect, incoming)
CUMULATIVE_ARGS *cum;
tree fntype;
rtx libname;
int indirect;
int incoming;
{
*cum = GPR_ARG_FIRST;
if (TARGET_DEBUG_ARG)
{
fprintf (stderr, "\ninit_cumulative_args:");
if (indirect)
fputs (" indirect", stderr);
if (incoming)
fputs (" incoming", stderr);
if (fntype)
{
tree ret_type = TREE_TYPE (fntype);
fprintf (stderr, " return=%s,",
tree_code_name[ (int)TREE_CODE (ret_type) ]);
}
if (libname && GET_CODE (libname) == SYMBOL_REF)
fprintf (stderr, " libname=%s", XSTR (libname, 0));
putc ('\n', stderr);
}
}
/* If defined, a C expression that gives the alignment boundary, in bits, of an
argument with the specified mode and type. If it is not defined,
`PARM_BOUNDARY' is used for all arguments. */
int
d30v_function_arg_boundary (mode, type)
enum machine_mode mode;
tree type;
{
int size = ((mode == BLKmode && type)
? int_size_in_bytes (type)
: GET_MODE_SIZE (mode));
return (size > UNITS_PER_WORD) ? 2*UNITS_PER_WORD : UNITS_PER_WORD;
}
/* A C expression that controls whether a function argument is passed in a
register, and which register.
The arguments are CUM, which summarizes all the previous arguments; MODE,
the machine mode of the argument; TYPE, the data type of the argument as a
tree node or 0 if that is not known (which happens for C support library
functions); and NAMED, which is 1 for an ordinary argument and 0 for
nameless arguments that correspond to `...' in the called function's
prototype.
The value of the expression should either be a `reg' RTX for the hard
register in which to pass the argument, or zero to pass the argument on the
stack.
For machines like the Vax and 68000, where normally all arguments are
pushed, zero suffices as a definition.
The usual way to make the ANSI library `stdarg.h' work on a machine where
some arguments are usually passed in registers, is to cause nameless
arguments to be passed on the stack instead. This is done by making
`FUNCTION_ARG' return 0 whenever NAMED is 0.
You may use the macro `MUST_PASS_IN_STACK (MODE, TYPE)' in the definition of
this macro to determine if this argument is of a type that must be passed in
the stack. If `REG_PARM_STACK_SPACE' is not defined and `FUNCTION_ARG'
returns non-zero for such an argument, the compiler will abort. If
`REG_PARM_STACK_SPACE' is defined, the argument will be computed in the
stack and then loaded into a register. */
rtx
d30v_function_arg (cum, mode, type, named, incoming)
CUMULATIVE_ARGS *cum;
enum machine_mode mode;
tree type;
int named;
int incoming ATTRIBUTE_UNUSED;
{
int size = ((mode == BLKmode && type)
? int_size_in_bytes (type)
: GET_MODE_SIZE (mode));
int adjust = (size > UNITS_PER_WORD && (*cum & 1) != 0);
rtx ret;
/* Return a marker for use in the call instruction. */
if (mode == VOIDmode)
ret = const0_rtx;
else if (*cum + adjust <= GPR_ARG_LAST)
ret = gen_rtx (REG, mode, *cum + adjust);
else
ret = NULL_RTX;
if (TARGET_DEBUG_ARG)
fprintf (stderr,
"function_arg: words = %2d, mode = %4s, named = %d, size = %3d, adjust = %1d, arg = %s\n",
*cum, GET_MODE_NAME (mode), named, size, adjust,
(ret) ? ((ret == const0_rtx) ? "<0>" : reg_names[ REGNO (ret) ]) : "memory");
return ret;
}
/* A C expression for the number of words, at the beginning of an argument,
must be put in registers. The value must be zero for arguments that are
passed entirely in registers or that are entirely pushed on the stack.
On some machines, certain arguments must be passed partially in registers
and partially in memory. On these machines, typically the first N words of
arguments are passed in registers, and the rest on the stack. If a
multi-word argument (a `double' or a structure) crosses that boundary, its
first few words must be passed in registers and the rest must be pushed.
This macro tells the compiler when this occurs, and how many of the words
should go in registers.
`FUNCTION_ARG' for these arguments should return the first register to be
used by the caller for this argument; likewise `FUNCTION_INCOMING_ARG', for
the called function. */
int
d30v_function_arg_partial_nregs (cum, mode, type, named)
CUMULATIVE_ARGS *cum;
enum machine_mode mode;
tree type;
int named ATTRIBUTE_UNUSED;
{
int bytes = ((mode == BLKmode)
? int_size_in_bytes (type)
: GET_MODE_SIZE (mode));
int words = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
int adjust = (bytes > UNITS_PER_WORD && (*cum & 1) != 0);
int arg_num = *cum + adjust;
int ret;
ret = ((arg_num <= GPR_ARG_LAST && arg_num + words > GPR_ARG_LAST+1)
? GPR_ARG_LAST - arg_num + 1
: 0);
if (TARGET_DEBUG_ARG && ret)
fprintf (stderr, "function_arg_partial_nregs: %d\n", ret);
return ret;
}
/* A C expression that indicates when an argument must be passed by reference.
If nonzero for an argument, a copy of that argument is made in memory and a
pointer to the argument is passed instead of the argument itself. The
pointer is passed in whatever way is appropriate for passing a pointer to
that type.
On machines where `REG_PARM_STACK_SPACE' is not defined, a suitable
definition of this macro might be
#define FUNCTION_ARG_PASS_BY_REFERENCE\
(CUM, MODE, TYPE, NAMED) \
MUST_PASS_IN_STACK (MODE, TYPE) */
int
d30v_function_arg_pass_by_reference (cum, mode, type, named)
CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED;
enum machine_mode mode;
tree type;
int named ATTRIBUTE_UNUSED;
{
int ret = MUST_PASS_IN_STACK (mode, type);
if (TARGET_DEBUG_ARG && ret)
fprintf (stderr, "function_arg_pass_by_reference: %d\n", ret);
return ret;
}
/* A C statement (sans semicolon) to update the summarizer variable CUM to
advance past an argument in the argument list. The values MODE, TYPE and
NAMED describe that argument. Once this is done, the variable CUM is
suitable for analyzing the *following* argument with `FUNCTION_ARG', etc.
This macro need not do anything if the argument in question was passed on
the stack. The compiler knows how to track the amount of stack space used
for arguments without any special help. */
void
d30v_function_arg_advance (cum, mode, type, named)
CUMULATIVE_ARGS *cum;
enum machine_mode mode;
tree type;
int named;
{
int bytes = ((mode == BLKmode)
? int_size_in_bytes (type)
: GET_MODE_SIZE (mode));
int words = D30V_ALIGN (bytes, UNITS_PER_WORD) / UNITS_PER_WORD;
int adjust = (bytes > UNITS_PER_WORD && (*cum & 1) != 0);
*cum += words + adjust;
if (TARGET_DEBUG_ARG)
fprintf (stderr,
"function_adv: words = %2d, mode = %4s, named = %d, size = %3d, adjust = %1d\n",
*cum, GET_MODE_NAME (mode), named, words * UNITS_PER_WORD, adjust);
}
/* If defined, is a C expression that produces the machine-specific code for a
call to `__builtin_saveregs'. This code will be moved to the very beginning
of the function, before any parameter access are made. The return value of
this function should be an RTX that contains the value to use as the return
of `__builtin_saveregs'.
If this macro is not defined, the compiler will output an ordinary call to
the library function `__builtin_saveregs'. */
rtx
d30v_expand_builtin_saveregs ()
{
int offset = UNITS_PER_WORD * (GPR_ARG_LAST + 1 - GPR_ARG_FIRST);
if (TARGET_DEBUG_ARG)
fprintf (stderr, "expand_builtin_saveregs: offset from ap = %d\n",
offset);
return gen_rtx (PLUS, Pmode, virtual_incoming_args_rtx, GEN_INT (- offset));
}
/* This macro offers an alternative to using `__builtin_saveregs' and defining
the macro `EXPAND_BUILTIN_SAVEREGS'. Use it to store the anonymous register
arguments into the stack so that all the arguments appear to have been
passed consecutively on the stack. Once this is done, you can use the
standard implementation of varargs that works for machines that pass all
their arguments on the stack.
The argument ARGS_SO_FAR is the `CUMULATIVE_ARGS' data structure, containing
the values that obtain after processing of the named arguments. The
arguments MODE and TYPE describe the last named argument--its machine mode
and its data type as a tree node.
The macro implementation should do two things: first, push onto the stack
all the argument registers *not* used for the named arguments, and second,
store the size of the data thus pushed into the `int'-valued variable whose
name is supplied as the argument PRETEND_ARGS_SIZE. The value that you
store here will serve as additional offset for setting up the stack frame.
Because you must generate code to push the anonymous arguments at compile
time without knowing their data types, `SETUP_INCOMING_VARARGS' is only
useful on machines that have just a single category of argument register and
use it uniformly for all data types.
If the argument SECOND_TIME is nonzero, it means that the arguments of the
function are being analyzed for the second time. This happens for an inline
function, which is not actually compiled until the end of the source file.
The macro `SETUP_INCOMING_VARARGS' should not generate any instructions in
this case. */
void
d30v_setup_incoming_varargs (cum, mode, type, pretend_size, second_time)
CUMULATIVE_ARGS *cum;
enum machine_mode mode;
tree type ATTRIBUTE_UNUSED;
int *pretend_size ATTRIBUTE_UNUSED;
int second_time;
{
if (TARGET_DEBUG_ARG)
fprintf (stderr,
"setup_vararg: words = %2d, mode = %4s, second_time = %d\n",
*cum, GET_MODE_NAME (mode), second_time);
}
/* Create the va_list data type. */
tree
d30v_build_va_list ()
{
tree f_arg_ptr, f_arg_num, record, type_decl;
tree int_type_node;
record = make_lang_type (RECORD_TYPE);
type_decl = build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record);
int_type_node = make_signed_type (INT_TYPE_SIZE);
f_arg_ptr = build_decl (FIELD_DECL, get_identifier ("__va_arg_ptr"),
ptr_type_node);
f_arg_num = build_decl (FIELD_DECL, get_identifier ("__va_arg_num"),
int_type_node);
DECL_FIELD_CONTEXT (f_arg_ptr) = record;
DECL_FIELD_CONTEXT (f_arg_num) = record;
TREE_CHAIN (record) = type_decl;
TYPE_NAME (record) = type_decl;
TYPE_FIELDS (record) = f_arg_ptr;
TREE_CHAIN (f_arg_ptr) = f_arg_num;
layout_type (record);
/* The correct type is an array type of one element. */
return build_array_type (record, build_index_type (size_zero_node));
}
/* Expand __builtin_va_start to do the va_start macro. */
void
d30v_expand_builtin_va_start (stdarg_p, valist, nextarg)
int stdarg_p ATTRIBUTE_UNUSED;
tree valist;
rtx nextarg ATTRIBUTE_UNUSED;
{
HOST_WIDE_INT words;
tree f_arg_ptr, f_arg_num;
tree arg_ptr, arg_num, saveregs, t;
f_arg_ptr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
f_arg_num = TREE_CHAIN (f_arg_ptr);
valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist);
arg_ptr = build (COMPONENT_REF, TREE_TYPE (f_arg_ptr), valist, f_arg_ptr);
arg_num = build (COMPONENT_REF, TREE_TYPE (f_arg_num), valist, f_arg_num);
words = current_function_args_info; /* __builtin_args_info (0) */
/* (AP)->__va_arg_ptr = (int *) __builtin_saveregs (); */
saveregs = make_tree (TREE_TYPE (arg_ptr), d30v_expand_builtin_saveregs ());
t = build (MODIFY_EXPR, TREE_TYPE (arg_ptr), arg_ptr, saveregs);
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
/* (AP)->__va_arg_num = __builtin_args_info (0) - 2; */
t = build (PLUS_EXPR, TREE_TYPE (arg_num), build_int_2 (words, 0),
build_int_2 (-GPR_ARG_FIRST, 0));
t = build (MODIFY_EXPR, TREE_TYPE (arg_num), arg_num, t);
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
/* Expand __builtin_va_arg to do the va_arg macro. */
rtx
d30v_expand_builtin_va_arg(valist, type)
tree valist;
tree type;
{
tree f_arg_ptr, f_arg_num;
tree arg_ptr, arg_num, t, ptr;
int num, size;
rtx lab_false, ptr_rtx, r;
f_arg_ptr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
f_arg_num = TREE_CHAIN (f_arg_ptr);
valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist);
arg_ptr = build (COMPONENT_REF, TREE_TYPE (f_arg_ptr), valist, f_arg_ptr);
arg_num = build (COMPONENT_REF, TREE_TYPE (f_arg_num), valist, f_arg_num);
size = int_size_in_bytes (type);
lab_false = gen_label_rtx ();
ptr_rtx = gen_reg_rtx (Pmode);
/* if (sizeof (TYPE) > 4 && ((AP)->__va_arg_num & 1) != 0)
(AP)->__va_arg_num++; */
if (size > UNITS_PER_WORD)
{
t = build (BIT_AND_EXPR, TREE_TYPE (arg_num), arg_num,
build_int_2 (1, 0));
emit_cmp_and_jump_insns (expand_expr (t, NULL_RTX, QImode, EXPAND_NORMAL),
GEN_INT (0), EQ, const1_rtx, QImode, 1, 1,
lab_false);
t = build (POSTINCREMENT_EXPR, TREE_TYPE (arg_num), arg_num,
build_int_2 (1, 0));
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
emit_label (lab_false);
}
/* __ptr = (TYPE *)(((char *)(void *)((AP)->__va_arg_ptr
+ (AP)->__va_arg_num))); */
t = build (MULT_EXPR, TREE_TYPE (arg_num), arg_num, build_int_2 (4, 0));
t = build (PLUS_EXPR, ptr_type_node, arg_ptr, t);
/* if (sizeof (TYPE) < 4)
__ptr = (void *)__ptr + 4 - sizeof (TYPE); */
if (size < UNITS_PER_WORD)
t = build (PLUS_EXPR, ptr_type_node, t,
build_int_2 (UNITS_PER_WORD - size, 0));
TREE_SIDE_EFFECTS (t) = 1;
ptr = build1 (NOP_EXPR, build_pointer_type (type), t);
t = build (MODIFY_EXPR, type, ptr, t);
r = expand_expr (t, ptr_rtx, Pmode, EXPAND_NORMAL);
if (r != ptr_rtx)
emit_move_insn (ptr_rtx, r);
/* (AP)->__va_arg_num += (sizeof (TYPE) + 3) / 4; */
num = (size + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
t = build (POSTINCREMENT_EXPR, TREE_TYPE (arg_num), arg_num,
build_int_2 (num, 0));
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
return ptr_rtx;
}
/* A C compound statement that outputs the assembler code for entry to a
function. The prologue is responsible for setting up the stack frame,
initializing the frame pointer register, saving registers that must be
saved, and allocating SIZE additional bytes of storage for the local
variables. SIZE is an integer. FILE is a stdio stream to which the
assembler code should be output.
The label for the beginning of the function need not be output by this
macro. That has already been done when the macro is run.
To determine which registers to save, the macro can refer to the array
`regs_ever_live': element R is nonzero if hard register R is used anywhere
within the function. This implies the function prologue should save
register R, provided it is not one of the call-used registers.
(`FUNCTION_EPILOGUE' must likewise use `regs_ever_live'.)
On machines that have "register windows", the function entry code does not
save on the stack the registers that are in the windows, even if they are
supposed to be preserved by function calls; instead it takes appropriate
steps to "push" the register stack, if any non-call-used registers are used
in the function.
On machines where functions may or may not have frame-pointers, the function
entry code must vary accordingly; it must set up the frame pointer if one is
wanted, and not otherwise. To determine whether a frame pointer is in
wanted, the macro can refer to the variable `frame_pointer_needed'. The
variable's value will be 1 at run time in a function that needs a frame
pointer. *Note Elimination::.
The function entry code is responsible for allocating any stack space
required for the function. This stack space consists of the regions listed
below. In most cases, these regions are allocated in the order listed, with
the last listed region closest to the top of the stack (the lowest address
if `STACK_GROWS_DOWNWARD' is defined, and the highest address if it is not
defined). You can use a different order for a machine if doing so is more
convenient or required for compatibility reasons. Except in cases where
required by standard or by a debugger, there is no reason why the stack
layout used by GCC need agree with that used by other compilers for a
machine.
* A region of `current_function_pretend_args_size' bytes of
uninitialized space just underneath the first argument
arriving on the stack. (This may not be at the very start of
the allocated stack region if the calling sequence has pushed
anything else since pushing the stack arguments. But
usually, on such machines, nothing else has been pushed yet,
because the function prologue itself does all the pushing.)
This region is used on machines where an argument may be
passed partly in registers and partly in memory, and, in some
cases to support the features in `varargs.h' and `stdargs.h'.
* An area of memory used to save certain registers used by the
function. The size of this area, which may also include
space for such things as the return address and pointers to
previous stack frames, is machine-specific and usually
depends on which registers have been used in the function.
Machines with register windows often do not require a save
area.
* A region of at least SIZE bytes, possibly rounded up to an
allocation boundary, to contain the local variables of the
function. On some machines, this region and the save area
may occur in the opposite order, with the save area closer to
the top of the stack.
* Optionally, when `ACCUMULATE_OUTGOING_ARGS' is defined, a
region of `current_function_outgoing_args_size' bytes to be
used for outgoing argument lists of the function. *Note
Stack Arguments::.
Normally, it is necessary for the macros `FUNCTION_PROLOGUE' and
`FUNCTION_EPILOGUE' to treat leaf functions specially. The C variable
`leaf_function' is nonzero for such a function. */
/* For the d30v, move all of the prologue processing into separate insns. */
void
d30v_function_prologue (stream, size)
FILE *stream ATTRIBUTE_UNUSED;
int size ATTRIBUTE_UNUSED;
{
}
/* Called after register allocation to add any instructions needed for the
prologue. Using a prologue insn is favored compared to putting all of the
instructions in the FUNCTION_PROLOGUE macro, since it allows the scheduler
to intermix instructions with the saves of the caller saved registers. In
some cases, it might be necessary to emit a barrier instruction as the last
insn to prevent such scheduling. */
void
d30v_expand_prologue ()
{
rtx sp = stack_pointer_rtx;
d30v_stack_t *info = d30v_stack_info ();
int i;
rtx mem_di = NULL_RTX;
rtx mem_si = NULL_RTX;
int num_memrefs = (info->memrefs_2words
+ info->memrefs_1word
+ info->memrefs_varargs);
if (TARGET_DEBUG_STACK)
debug_stack_info (info);
/* Grow the stack. */
if (info->total_size)
emit_insn (gen_addsi3 (sp, sp, GEN_INT (- info->total_size)));
/* If there is more than one save, use post-increment addressing which will
result in smaller code, than would the normal references. If there is
only one save, just do the store as normal. */
if (num_memrefs > 1)
{
rtx save_tmp = gen_rtx (REG, Pmode, GPR_STACK_TMP);
rtx post_inc = gen_rtx (POST_INC, Pmode, save_tmp);
mem_di = gen_rtx (MEM, DImode, post_inc);
mem_si = gen_rtx (MEM, SImode, post_inc);
emit_insn (gen_addsi3 (save_tmp, sp, GEN_INT (info->save_offset)));
}
else if (num_memrefs == 1)
{
rtx addr = plus_constant (sp, info->save_offset);
mem_di = gen_rtx (MEM, DImode, addr);
mem_si = gen_rtx (MEM, SImode, addr);
}
/* Save the accumulators. */
for (i = ACCUM_FIRST; i <= ACCUM_LAST; i++)
if (info->save_p[i])
{
rtx acc_tmp = gen_rtx (REG, DImode, GPR_ATMP_FIRST);
emit_insn (gen_movdi (acc_tmp, gen_rtx (REG, DImode, i)));
emit_insn (gen_movdi (mem_di, acc_tmp));
}
/* Save the GPR registers that are adjacent to each other with st2w. */
for (i = GPR_FIRST; i <= GPR_LAST; i += 2)
if (info->save_p[i] == 2)
emit_insn (gen_movdi (mem_di, gen_rtx (REG, DImode, i)));
/* Save the GPR registers that need to be saved with a single word store. */
for (i = GPR_FIRST; i <= GPR_LAST; i++)
if (info->save_p[i] == 1)
emit_insn (gen_movsi (mem_si, gen_rtx (REG, SImode, i)));
/* Save the argument registers if this function accepts variable args. */
if (info->varargs_p)
{
/* Realign r22 if an odd # of GPRs were saved. */
if ((info->memrefs_1word & 1) != 0)
{
rtx save_tmp = XEXP (XEXP (mem_si, 0), 0);
emit_insn (gen_addsi3 (save_tmp, save_tmp, GEN_INT (UNITS_PER_WORD)));
}
for (i = GPR_ARG_FIRST; i <= GPR_ARG_LAST; i += 2)
emit_insn (gen_movdi (mem_di, gen_rtx (REG, DImode, i)));
}
/* Update the frame pointer. */
if (frame_pointer_needed)
emit_move_insn (frame_pointer_rtx, sp);
/* Hack for now, to prevent scheduler from being too cleaver */
emit_insn (gen_blockage ());
}
/* A C compound statement that outputs the assembler code for exit from a
function. The epilogue is responsible for restoring the saved registers and
stack pointer to their values when the function was called, and returning
control to the caller. This macro takes the same arguments as the macro
`FUNCTION_PROLOGUE', and the registers to restore are determined from
`regs_ever_live' and `CALL_USED_REGISTERS' in the same way.
On some machines, there is a single instruction that does all the work of
returning from the function. On these machines, give that instruction the
name `return' and do not define the macro `FUNCTION_EPILOGUE' at all.
Do not define a pattern named `return' if you want the `FUNCTION_EPILOGUE'
to be used. If you want the target switches to control whether return
instructions or epilogues are used, define a `return' pattern with a
validity condition that tests the target switches appropriately. If the
`return' pattern's validity condition is false, epilogues will be used.
On machines where functions may or may not have frame-pointers, the function
exit code must vary accordingly. Sometimes the code for these two cases is
completely different. To determine whether a frame pointer is wanted, the
macro can refer to the variable `frame_pointer_needed'. The variable's
value will be 1 when compiling a function that needs a frame pointer.
Normally, `FUNCTION_PROLOGUE' and `FUNCTION_EPILOGUE' must treat leaf
functions specially. The C variable `leaf_function' is nonzero for such a
function. *Note Leaf Functions::.
On some machines, some functions pop their arguments on exit while others
leave that for the caller to do. For example, the 68020 when given `-mrtd'
pops arguments in functions that take a fixed number of arguments.
Your definition of the macro `RETURN_POPS_ARGS' decides which functions pop
their own arguments. `FUNCTION_EPILOGUE' needs to know what was decided.
The variable that is called `current_function_pops_args' is the number of
bytes of its arguments that a function should pop. *Note Scalar Return::. */
/* For the d30v, move all processing to be as insns, but do any cleanup
here, since it is done after handling all of the insns. */
void
d30v_function_epilogue (stream, size)
FILE *stream ATTRIBUTE_UNUSED;
int size ATTRIBUTE_UNUSED;
{
d30v_stack_cache = (d30v_stack_t *)0; /* reset stack cache */
}
/* Called after register allocation to add any instructions needed for the
epilogue. Using a epilogue insn is favored compared to putting all of the
instructions in the FUNCTION_PROLOGUE macro, since it allows the scheduler
to intermix instructions with the saves of the caller saved registers. In
some cases, it might be necessary to emit a barrier instruction as the last
insn to prevent such scheduling. */
void
d30v_expand_epilogue ()
{
rtx sp = stack_pointer_rtx;
d30v_stack_t *info = d30v_stack_info ();
int i;
rtx mem_di = NULL_RTX;
rtx mem_si = NULL_RTX;
rtx post_inc;
int extra_stack;
/* Hack for now, to prevent scheduler from being too cleaver */
emit_insn (gen_blockage ());
/* Restore sp from fp. */
if (frame_pointer_needed)
emit_move_insn (sp, frame_pointer_rtx);
/* For the epilogue, use post-increment addressing all of the time. First
adjust the sp, to eliminate all of the stack, except for the save area. */
if (info->save_offset)
emit_insn (gen_addsi3 (sp, sp, GEN_INT (info->save_offset)));
post_inc = gen_rtx (POST_INC, Pmode, sp);
mem_di = gen_rtx (MEM, DImode, post_inc);
mem_si = gen_rtx (MEM, SImode, post_inc);
/* Restore the accumulators. */
for (i = ACCUM_FIRST; i <= ACCUM_LAST; i++)
if (info->save_p[i])
{
rtx acc_tmp = gen_rtx (REG, DImode, GPR_ATMP_FIRST);
emit_insn (gen_movdi (acc_tmp, mem_di));
emit_insn (gen_movdi (gen_rtx (REG, DImode, i), acc_tmp));
}
/* Restore the GPR registers that are adjacent to each other with ld2w. */
for (i = GPR_FIRST; i <= GPR_LAST; i += 2)
if (info->save_p[i] == 2)
emit_insn (gen_movdi (gen_rtx (REG, DImode, i), mem_di));
/* Save the GPR registers that need to be saved with a single word store. */
extra_stack = 0;
for (i = GPR_FIRST; i <= GPR_LAST; i++)
if (info->save_p[i] == 1)
{
if (cfun->machine->eh_epilogue_sp_ofs && i == GPR_LINK)
extra_stack = 4;
else
{
if (extra_stack)
{
emit_insn (gen_addsi3 (sp, sp, GEN_INT (extra_stack)));
extra_stack = 0;
}
emit_insn (gen_movsi (gen_rtx (REG, SImode, i), mem_si));
}
}
/* Release any remaining stack that was allocated for saving the
varargs registers or because an odd # of registers were stored. */
if ((info->memrefs_1word & 1) != 0)
extra_stack += UNITS_PER_WORD;
extra_stack += current_function_pretend_args_size + info->varargs_size;
if (extra_stack)
{
if (cfun->machine->eh_epilogue_sp_ofs)
emit_insn (gen_addsi3 (cfun->machine->eh_epilogue_sp_ofs,
cfun->machine->eh_epilogue_sp_ofs,
GEN_INT (extra_stack)));
else
emit_insn (gen_addsi3 (sp, sp, GEN_INT (extra_stack)));
}
if (cfun->machine->eh_epilogue_sp_ofs)
emit_insn (gen_addsi3 (sp, sp, cfun->machine->eh_epilogue_sp_ofs));
/* Now emit the return instruction. */
emit_jump_insn (gen_rtx_RETURN (VOIDmode));
}
/* A C statement or compound statement to output to FILE some assembler code to
call the profiling subroutine `mcount'. Before calling, the assembler code
must load the address of a counter variable into a register where `mcount'
expects to find the address. The name of this variable is `LP' followed by
the number LABELNO, so you would generate the name using `LP%d' in a
`fprintf'.
The details of how the address should be passed to `mcount' are determined
by your operating system environment, not by GNU CC. To figure them out,
compile a small program for profiling using the system's installed C
compiler and look at the assembler code that results. */
void
d30v_function_profiler (stream, labelno)
FILE *stream;
int labelno ATTRIBUTE_UNUSED;
{
fprintf (stream, "# profile\n");
}
/* Split a 64 bit item into an upper and a lower part. We specifically do not
want to call gen_highpart/gen_lowpart on CONST_DOUBLEs since it will give us
the wrong part for floating point in cross compilers, and split_double does
not handle registers. Also abort if the register is not a general purpose
register. */
void
d30v_split_double (value, p_high, p_low)
rtx value;
rtx *p_high;
rtx *p_low;
{
int offset = 0;
int regno;
if (!reload_completed)
abort ();
switch (GET_CODE (value))
{
case SUBREG:
if (GET_CODE (SUBREG_REG (value)) != REG)
abort ();
offset = subreg_regno_offset (REGNO (SUBREG_REG (value)),
GET_MODE (SUBREG_REG (value)),
SUBREG_BYTE (value),
GET_MODE (value));
value = SUBREG_REG (value);
/* fall through */
case REG:
regno = REGNO (value) + offset;
if (!GPR_P (regno))
abort ();
*p_high = gen_rtx (REG, SImode, regno);
*p_low = gen_rtx (REG, SImode, regno+1);
break;
case CONST_INT:
case CONST_DOUBLE:
split_double (value, p_high, p_low);
break;
default:
abort ();
}
}
/* A C compound statement to output to stdio stream STREAM the assembler syntax
for an instruction operand that is a memory reference whose address is X. X
is an RTL expression.
On some machines, the syntax for a symbolic address depends on the section
that the address refers to. On these machines, define the macro
`ENCODE_SECTION_INFO' to store the information into the `symbol_ref', and
then check for it here. *Note Assembler Format::. */
void
d30v_print_operand_address (stream, x)
FILE *stream;
rtx x;
{
if (GET_CODE (x) == MEM)
x = XEXP (x, 0);
switch (GET_CODE (x))
{
default:
break;
case REG:
fputs (reg_names[ REGNO (x) ], stream);
return;
case CONST_INT:
fprintf (stream, "%ld", (long) INTVAL (x));
return;
/* We wrap simple symbol refs inside a parenthesis, so that a name
like `r2' is not taken for a register name. */
case SYMBOL_REF:
fputs ("(", stream);
assemble_name (stream, XSTR (x, 0));
fputs (")", stream);
return;
case LABEL_REF:
case CONST:
output_addr_const (stream, x);
return;
}
fatal_insn ("Bad insn to d30v_print_operand_address:", x);
}
/* Print a memory reference suitable for the ld/st instructions. */
static void
d30v_print_operand_memory_reference (stream, x)
FILE *stream;
rtx x;
{
rtx x0 = NULL_RTX;
rtx x1 = NULL_RTX;
switch (GET_CODE (x))
{
default:
fatal_insn ("Bad insn to d30v_print_operand_memory_reference:", x);
break;
case SUBREG:
case REG:
case POST_DEC:
case POST_INC:
x0 = x;
break;
case CONST_INT:
case SYMBOL_REF:
case LABEL_REF:
case CONST:
x1 = x;
break;
case PLUS:
x0 = XEXP (x, 0);
x1 = XEXP (x, 1);
if (GET_CODE (x0) == CONST_INT || GET_CODE (x0) == SYMBOL_REF
|| GET_CODE (x0) == CONST || GET_CODE (x0) == LABEL_REF)
{
x0 = XEXP (x, 1);
x1 = XEXP (x, 0);
}
break;
}
fputs ("@(", stream);
if (!x0)
fputs (reg_names[GPR_R0], stream);
else
{
char *suffix = "";
int offset0 = 0;
if (GET_CODE (x0) == SUBREG)
{
offset0 = subreg_regno_offset (REGNO (SUBREG_REG (x0)),
GET_MODE (SUBREG_REG (x0)),
SUBREG_BYTE (x0),
GET_MODE (x0));
x0 = SUBREG_REG (x0);
}
if (GET_CODE (x0) == POST_INC)
{
x0 = XEXP (x0, 0);
suffix = "+";
}
else if (GET_CODE (x0) == POST_DEC)
{
x0 = XEXP (x0, 0);
suffix = "-";
}
if (GET_CODE (x0) == REG && GPR_P (REGNO (x0)))
fprintf (stream, "%s%s", reg_names[REGNO (x0) + offset0], suffix);
else
fatal_insn ("Bad insn to d30v_print_operand_memory_reference:", x);
}
fputs (",", stream);
if (!x1)
fputs (reg_names[GPR_R0], stream);
else
{
int offset1 = 0;
switch (GET_CODE (x1))
{
case SUBREG:
offset1 = subreg_regno_offset (REGNO (SUBREG_REG (x1)),
GET_MODE (SUBREG_REG (x1)),
SUBREG_BYTE (x1),
GET_MODE (x1));
x1 = SUBREG_REG (x1);
if (GET_CODE (x1) != REG)
fatal_insn ("Bad insn to d30v_print_operand_memory_reference:", x);
/* fall through */
case REG:
fputs (reg_names[REGNO (x1) + offset1], stream);
break;
case CONST_INT:
fprintf (stream, "%ld", (long) INTVAL (x1));
break;
case SYMBOL_REF:
case LABEL_REF:
case CONST:
d30v_print_operand_address (stream, x1);
break;
default:
fatal_insn ("Bad insn to d30v_print_operand_memory_reference:", x);
}
}
fputs (")", stream);
}
/* A C compound statement to output to stdio stream STREAM the assembler syntax
for an instruction operand X. X is an RTL expression.
LETTER is a value that can be used to specify one of several ways of
printing the operand. It is used when identical operands must be printed
differently depending on the context. LETTER comes from the `%'
specification that was used to request printing of the operand. If the
specification was just `%DIGIT' then LETTER is 0; if the specification was
`%LTR DIGIT' then LETTER is the ASCII code for LTR.
If X is a register, this macro should print the register's name. The names
can be found in an array `reg_names' whose type is `char *[]'. `reg_names'
is initialized from `REGISTER_NAMES'.
When the machine description has a specification `%PUNCT' (a `%' followed by
a punctuation character), this macro is called with a null pointer for X and
the punctuation character for LETTER.
Standard operand flags that are handled elsewhere:
`=' Output a number unique to each instruction in the compilation.
`a' Substitute an operand as if it were a memory reference.
`c' Omit the syntax that indicates an immediate operand.
`l' Substitute a LABEL_REF into a jump instruction.
`n' Like %cDIGIT, except negate the value before printing.
The d30v specific operand flags are:
`.' Print r0.
`f' Print a SF constant as an int.
`s' Subtract 32 and negate.
`A' Print accumulator number without an `a' in front of it.
`B' Print bit offset for BSET, etc. instructions.
`E' Print u if this is zero extend, nothing if this is sign extend.
`F' Emit /{f,t,x}{f,t,x} for executing a false condition.
`L' Print the lower half of a 64 bit item.
`M' Print a memory reference for ld/st instructions.
`R' Return appropriate cmp instruction for relational test.
`S' Subtract 32.
`T' Emit /{f,t,x}{f,t,x} for executing a true condition.
`U' Print the upper half of a 64 bit item. */
void
d30v_print_operand (stream, x, letter)
FILE *stream;
rtx x;
int letter;
{
enum rtx_code code = (x) ? GET_CODE (x) : NIL;
rtx split_values[2];
REAL_VALUE_TYPE rv;
long num;
int log;
switch (letter)
{
case '.': /* Output r0 */
fputs (reg_names[GPR_R0], stream);
break;
case 'f': /* Print a SF floating constant as an int */
if (GET_CODE (x) != CONST_DOUBLE)
fatal_insn ("Bad insn to d30v_print_operand, 'f' modifier:", x);
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
REAL_VALUE_TO_TARGET_SINGLE (rv, num);
fprintf (stream, "%ld", num);
break;
case 'A': /* Print accumulator number without an `a' in front of it. */
if (GET_CODE (x) != REG || !ACCUM_P (REGNO (x)))
fatal_insn ("Bad insn to d30v_print_operand, 'A' modifier:", x);
putc ('0' + REGNO (x) - ACCUM_FIRST, stream);
break;
case 'M': /* Print a memory reference for ld/st */
if (GET_CODE (x) != MEM)
fatal_insn ("Bad insn to d30v_print_operand, 'M' modifier:", x);
d30v_print_operand_memory_reference (stream, XEXP (x, 0));
break;
case 'L': /* print lower part of 64 bit item. */
case 'U': /* print upper part of 64 bit item. */
d30v_split_double (x, &split_values[0], &split_values[1]);
d30v_print_operand (stream, split_values[ letter == 'L' ], '\0');
break;
case ':': /* Output the condition for the current insn. */
x = current_insn_predicate;
if (x == NULL_RTX)
break;
letter = 'T';
/* FALLTHRU */
case 'F': /* Print an appropriate suffix for a false comparision. */
case 'T': /* Print an appropriate suffix for a true comparision. */
/* Note that the sense of appropriate suffix is for conditional execution
and opposite of what branches want. Branches just use the inverse
operation. */
if ((GET_CODE (x) == NE || GET_CODE (x) == EQ)
&& GET_MODE (x) == CCmode
&& GET_CODE (XEXP (x, 0)) == REG
&& (GPR_P (REGNO (XEXP (x, 0))) || BR_FLAG_P (REGNO (XEXP (x, 0))))
&& GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) == 0)
{
int true_false = (letter == 'T');
if (GET_CODE (x) == EQ)
true_false = !true_false;
if (REGNO (XEXP (x, 0)) == FLAG_F0)
fprintf (stream, "/%cx", (true_false) ? 'f' : 't');
else if (REGNO (XEXP (x, 0)) == FLAG_F1)
fprintf (stream, "/x%c", (true_false) ? 'f' : 't');
else
fputs ((true_false) ? "tnz" : "tzr", stream);
}
else if (GET_CODE (x) == REG && REGNO (x) == FLAG_F0)
fprintf (stream, "/%cx", (letter == 'T') ? 't' : 'f');
else if (GET_CODE (x) == REG && REGNO (x) == FLAG_F1)
fprintf (stream, "/x%c", (letter == 'T') ? 't' : 'f');
else if (GET_CODE (x) == REG && GPR_P (REGNO (x)))
fputs ((letter == 'T') ? "tnz" : "tzr", stream);
else
fatal_insn ("Bad insn to print_operand, 'F' or 'T' modifier:", x);
break;
case 'B': /* emit offset single bit to change */
if (GET_CODE (x) == CONST_INT && (log = exact_log2 (INTVAL (x))) >= 0)
fprintf (stream, "%d", 31 - log);
else if (GET_CODE (x) == CONST_INT && (log = exact_log2 (~ INTVAL (x))) >= 0)
fprintf (stream, "%d", 31 - log);
else
fatal_insn ("Bad insn to print_operand, 'B' modifier:", x);
break;
case 'E': /* Print u if this is zero extend, nothing if sign extend. */
if (GET_CODE (x) == ZERO_EXTEND)
putc ('u', stream);
else if (GET_CODE (x) != SIGN_EXTEND)
fatal_insn ("Bad insn to print_operand, 'E' modifier:", x);
break;
case 'R': /* Return appropriate cmp instruction for relational test. */
switch (GET_CODE (x))
{
case EQ: fputs ("cmpeq", stream); break;
case NE: fputs ("cmpne", stream); break;
case LT: fputs ("cmplt", stream); break;
case LE: fputs ("cmple", stream); break;
case GT: fputs ("cmpgt", stream); break;
case GE: fputs ("cmpge", stream); break;
case LTU: fputs ("cmpult", stream); break;
case LEU: fputs ("cmpule", stream); break;
case GTU: fputs ("cmpugt", stream); break;
case GEU: fputs ("cmpuge", stream); break;
default:
fatal_insn ("Bad insn to print_operand, 'R' modifier:", x);
}
break;
case 's': /* Subtract 32 and negate (for 64 bit shifts). */
if (GET_CODE (x) == CONST_INT)
fprintf (stream, "%d", (int) (32 - INTVAL (x)));
else
fatal_insn ("Bad insn to print_operand, 's' modifier:", x);
break;
case 'S': /* Subtract 32. */
if (GET_CODE (x) == CONST_INT)
fprintf (stream, "%d", (int)(INTVAL (x) - 32));
else
fatal_insn ("Bad insn to print_operand, 's' modifier:", x);
break;
case 'z': /* If arg is 0 or 0.0, print r0, otherwise print as normal */
if ((GET_CODE (x) == CONST_INT && INTVAL (x) == 0)
|| (GET_CODE (x) == CONST_DOUBLE && CONST_DOUBLE_LOW (x) == 0
&& CONST_DOUBLE_HIGH (x) == 0))
{
fputs (reg_names[GPR_FIRST], stream);
return;
}
/* fall through */
case '\0':
if (code == REG)
fputs (reg_names[ REGNO (x) ], stream);
else if (code == CONST_INT)
fprintf (stream, "%d", (int)INTVAL (x));
else if (code == MEM)
d30v_print_operand_address (stream, XEXP (x, 0));
else if (CONSTANT_ADDRESS_P (x))
d30v_print_operand_address (stream, x);
else
fatal_insn ("Bad insn in d30v_print_operand, 0 case", x);
return;
default:
{
char buf[80];
sprintf (buf, "Invalid asm template character '%%%c'", letter);
fatal_insn (buf, x);
}
}
}
/* A C expression for the size in bytes of the trampoline, as an integer. */
int
d30v_trampoline_size ()
{
return 16;
}
/* Create a long instruction for building up a trampoline. */
static void
d30v_build_long_insn (high_bits, low_bits, imm, mem)
HOST_WIDE_INT high_bits;
HOST_WIDE_INT low_bits;
rtx imm;
rtx mem;
{
rtx reg = gen_reg_rtx (DImode);
rtx high_word = gen_highpart (SImode, reg);
rtx low_word = gen_lowpart (SImode, reg);
rtx tmp1 = gen_reg_rtx (SImode);
rtx tmp2 = gen_reg_rtx (SImode);
rtx tmp3 = gen_reg_rtx (SImode);
rtx tmp4 = gen_reg_rtx (SImode);
rtx tmp5 = gen_reg_rtx (SImode);
rtx tmp6 = gen_reg_rtx (SImode);
imm = force_reg (SImode, imm);
/* Stuff top 6 bits of immediate value into high word */
emit_insn (gen_lshrsi3 (tmp1, imm, GEN_INT (26)));
emit_insn (gen_andsi3 (tmp2, tmp1, GEN_INT (0x3F)));
emit_insn (gen_iorsi3 (high_word, tmp2, GEN_INT (high_bits)));
/* Now get the next 8 bits for building the low word */
emit_insn (gen_andsi3 (tmp3, imm, GEN_INT (0x03FC0000)));
emit_insn (gen_ashlsi3 (tmp4, tmp3, GEN_INT (2)));
/* And the bottom 18 bits */
emit_insn (gen_andsi3 (tmp5, imm, GEN_INT (0x0003FFFF)));
emit_insn (gen_iorsi3 (tmp6, tmp4, tmp5));
emit_insn (gen_iorsi3 (low_word, tmp6, GEN_INT (low_bits)));
/* Store the instruction */
emit_insn (gen_movdi (mem, reg));
}
/* A C statement to initialize the variable parts of a trampoline. ADDR is an
RTX for the address of the trampoline; FNADDR is an RTX for the address of
the nested function; STATIC_CHAIN is an RTX for the static chain value that
should be passed to the function when it is called. */
void
d30v_initialize_trampoline (addr, fnaddr, static_chain)
rtx addr;
rtx fnaddr;
rtx static_chain;
{
/* The instruction space can only be accessed by ld2w/st2w.
Generate on the fly:
or r18,r0,<static-chain>
jmp <fnaddr> */
d30v_build_long_insn (0x83A80000 | ((STATIC_CHAIN_REGNUM - GPR_FIRST) << 12),
0x80000000, static_chain,
gen_rtx (MEM, DImode, addr));
d30v_build_long_insn (0x80180000, 0x80000000, fnaddr,
gen_rtx (MEM, DImode, plus_constant (addr, 8)));
}
/* A C compound statement with a conditional `goto LABEL;' executed if X (an
RTX) is a legitimate memory address on the target machine for a memory
operand of mode MODE.
It usually pays to define several simpler macros to serve as subroutines for
this one. Otherwise it may be too complicated to understand.
This macro must exist in two variants: a strict variant and a non-strict
one. The strict variant is used in the reload pass. It must be defined so
that any pseudo-register that has not been allocated a hard register is
considered a memory reference. In contexts where some kind of register is
required, a pseudo-register with no hard register must be rejected.
The non-strict variant is used in other passes. It must be defined to
accept all pseudo-registers in every context where some kind of register is
required.
Compiler source files that want to use the strict variant of this macro
define the macro `REG_OK_STRICT'. You should use an `#ifdef REG_OK_STRICT'
conditional to define the strict variant in that case and the non-strict
variant otherwise.
Subroutines to check for acceptable registers for various purposes (one for
base registers, one for index registers, and so on) are typically among the
subroutines used to define `GO_IF_LEGITIMATE_ADDRESS'. Then only these
subroutine macros need have two variants; the higher levels of macros may be
the same whether strict or not.
Normally, constant addresses which are the sum of a `symbol_ref' and an
integer are stored inside a `const' RTX to mark them as constant.
Therefore, there is no need to recognize such sums specifically as
legitimate addresses. Normally you would simply recognize any `const' as
legitimate.
Usually `PRINT_OPERAND_ADDRESS' is not prepared to handle constant sums that
are not marked with `const'. It assumes that a naked `plus' indicates
indexing. If so, then you *must* reject such naked constant sums as
illegitimate addresses, so that none of them will be given to
`PRINT_OPERAND_ADDRESS'.
On some machines, whether a symbolic address is legitimate depends on the
section that the address refers to. On these machines, define the macro
`ENCODE_SECTION_INFO' to store the information into the `symbol_ref', and
then check for it here. When you see a `const', you will have to look
inside it to find the `symbol_ref' in order to determine the section. *Note
Assembler Format::.
The best way to modify the name string is by adding text to the beginning,
with suitable punctuation to prevent any ambiguity. Allocate the new name
in `saveable_obstack'. You will have to modify `ASM_OUTPUT_LABELREF' to
remove and decode the added text and output the name accordingly, and define
`STRIP_NAME_ENCODING' to access the original name string.
You can check the information stored here into the `symbol_ref' in the
definitions of the macros `GO_IF_LEGITIMATE_ADDRESS' and
`PRINT_OPERAND_ADDRESS'.
Return 0 if the address is not legitimate, 1 if the address would fit
in a short instruction, or 2 if the address would fit in a long
instruction. */
#define XREGNO_OK_FOR_BASE_P(REGNO, STRICT_P) \
((STRICT_P) \
? REGNO_OK_FOR_BASE_P (REGNO) \
: GPR_OR_PSEUDO_P (REGNO))
int
d30v_legitimate_address_p (mode, x, strict_p)
enum machine_mode mode;
rtx x;
int strict_p;
{
rtx x0, x1;
int ret = 0;
switch (GET_CODE (x))
{
default:
break;
case SUBREG:
x = SUBREG_REG (x);
if (GET_CODE (x) != REG)
break;
/* fall through */
case REG:
ret = XREGNO_OK_FOR_BASE_P (REGNO (x), strict_p);
break;
case PLUS:
x0 = XEXP (x, 0);
x1 = XEXP (x, 1);
if (GET_CODE (x0) == SUBREG)
x0 = SUBREG_REG (x0);
if (GET_CODE (x0) == POST_INC || GET_CODE (x0) == POST_DEC)
x0 = XEXP (x0, 0);
if (GET_CODE (x0) != REG || !XREGNO_OK_FOR_BASE_P (REGNO (x0), strict_p))
break;
switch (GET_CODE (x1))
{
default:
break;
case SUBREG:
x1 = SUBREG_REG (x1);
if (GET_CODE (x1) != REG)
break;
/* fall through */
case REG:
ret = XREGNO_OK_FOR_BASE_P (REGNO (x1), strict_p);
break;
case CONST_INT:
ret = (IN_RANGE_P (INTVAL (x1), -32, 31)) ? 1 : 2;
break;
case SYMBOL_REF:
case LABEL_REF:
case CONST:
ret = 2;
break;
}
break;
case CONST_INT:
ret = (IN_RANGE_P (INTVAL (x), -32, 31)) ? 1 : 2;
break;
case SYMBOL_REF:
case LABEL_REF:
case CONST:
ret = 2;
break;
case POST_INC:
case POST_DEC:
x0 = XEXP (x, 0);
if (GET_CODE (x0) == REG && XREGNO_OK_FOR_BASE_P (REGNO (x0), strict_p))
ret = 1;
break;
}
if (TARGET_DEBUG_ADDR)
{
fprintf (stderr, "\n========== GO_IF_LEGITIMATE_ADDRESS, mode = %s, result = %d, addresses are %sstrict\n",
GET_MODE_NAME (mode), ret, (strict_p) ? "" : "not ");
debug_rtx (x);
}
return ret;
}
/* A C compound statement that attempts to replace X with a valid memory
address for an operand of mode MODE. WIN will be a C statement label
elsewhere in the code; the macro definition may use
GO_IF_LEGITIMATE_ADDRESS (MODE, X, WIN);
to avoid further processing if the address has become legitimate.
X will always be the result of a call to `break_out_memory_refs', and OLDX
will be the operand that was given to that function to produce X.
The code generated by this macro should not alter the substructure of X. If
it transforms X into a more legitimate form, it should assign X (which will
always be a C variable) a new value.
It is not necessary for this macro to come up with a legitimate address.
The compiler has standard ways of doing so in all cases. In fact, it is
safe for this macro to do nothing. But often a machine-dependent strategy
can generate better code. */
rtx
d30v_legitimize_address (x, oldx, mode, strict_p)
rtx x;
rtx oldx ATTRIBUTE_UNUSED;
enum machine_mode mode ATTRIBUTE_UNUSED;
int strict_p ATTRIBUTE_UNUSED;
{
rtx ret = NULL_RTX;
if (TARGET_DEBUG_ADDR)
{
if (ret)
{
fprintf (stderr, "\n========== LEGITIMIZE_ADDRESS, transformed:\n");
debug_rtx (x);
fprintf (stderr, "\ninto:\n");
debug_rtx (ret);
}
else
{
fprintf (stderr, "\n========== LEGITIMIZE_ADDRESS, did nothing with:\n");
debug_rtx (x);
}
}
return ret;
}
/* A C statement or compound statement with a conditional `goto LABEL;'
executed if memory address X (an RTX) can have different meanings depending
on the machine mode of the memory reference it is used for or if the address
is valid for some modes but not others.
Autoincrement and autodecrement addresses typically have mode-dependent
effects because the amount of the increment or decrement is the size of the
operand being addressed. Some machines have other mode-dependent addresses.
Many RISC machines have no mode-dependent addresses.
You may assume that ADDR is a valid address for the machine. */
int
d30v_mode_dependent_address_p (addr)
rtx addr;
{
switch (GET_CODE (addr))
{
default:
break;
case POST_INC:
case POST_DEC:
return TRUE;
}
return FALSE;
}
/* Generate the appropriate comparison code for a test. */
rtx
d30v_emit_comparison (test_int, result, arg1, arg2)
int test_int;
rtx result;
rtx arg1;
rtx arg2;
{
enum rtx_code test = (enum rtx_code) test_int;
enum machine_mode mode = GET_MODE (arg1);
rtx rtx_test = gen_rtx (SET, VOIDmode, result, gen_rtx (test, CCmode, arg1, arg2));
if (mode == SImode
|| (mode == DImode && (test == EQ || test == NE))
|| (mode == DImode && (test == LT || test == GE)
&& GET_CODE (arg2) == CONST_INT && INTVAL (arg2) == 0))
return rtx_test;
else if (mode == DImode)
return gen_rtx (PARALLEL, VOIDmode,
gen_rtvec (2,
rtx_test,
gen_rtx (CLOBBER, VOIDmode,
gen_reg_rtx (CCmode))));
else
fatal_insn ("d30v_emit_comparison", rtx_test);
}
/* Return appropriate code to move 2 words. Since DImode registers must start
on even register numbers, there is no possibility of overlap. */
char *
d30v_move_2words (operands, insn)
rtx operands[];
rtx insn;
{
if (GET_CODE (operands[0]) == REG && GPR_P (REGNO (operands[0])))
{
if (GET_CODE (operands[1]) == REG && GPR_P (REGNO (operands[1])))
return "or %U0,%.,%U1\n\tor %L0,%.,%L1";
else if (GET_CODE (operands[1]) == REG && ACCUM_P (REGNO (operands[1])))
return "mvfacc %L0,%1,%.\n\tmvfacc %U0,%1,32";
else if (GET_CODE (operands[1]) == MEM)
return "ld2w %0,%M1";
else if (GET_CODE (operands[1]) == CONST_INT
|| GET_CODE (operands[1]) == CONST_DOUBLE)
return "or %U0,%.,%U1\n\tor %L0,%.,%L1";
}
else if (GET_CODE (operands[0]) == REG && ACCUM_P (REGNO (operands[0])))
{
if (GET_CODE (operands[1]) == REG
&& GPR_P (REGNO (operands[1])))
return "mvtacc %0,%U1,%L1";
if (GET_CODE (operands[1]) == CONST_INT
&& INTVAL (operands[1]) == 0)
return "mvtacc %0,%.,%.";
}
else if (GET_CODE (operands[0]) == MEM
&& GET_CODE (operands[1]) == REG
&& GPR_P (REGNO (operands[1])))
return "st2w %1,%M0";
fatal_insn ("Bad call to d30v_move_2words", insn);
}
/* Emit the code to do a conditional move instruction. Return FALSE
if the conditional move could not be executed. */
int
d30v_emit_cond_move (dest, test, true_value, false_value)
rtx dest;
rtx test;
rtx true_value;
rtx false_value;
{
rtx br_reg;
enum machine_mode mode = GET_MODE (dest);
int two_mem_moves_p = FALSE;
if (GET_CODE (dest) == MEM)
{
if (!reg_or_0_operand (true_value, mode))
return FALSE;
if (rtx_equal_p (dest, false_value))
two_mem_moves_p = TRUE;
else if (!reg_or_0_operand (false_value, mode))
return FALSE;
}
/* We used to try to optimize setting 0/1 by using mvfsys, but that turns out
to be slower than just doing the conditional execution. */
br_reg = gen_reg_rtx (CCmode);
emit_insn (d30v_emit_comparison (GET_CODE (test), br_reg,
d30v_compare_op0, d30v_compare_op1));
if (!two_mem_moves_p)
emit_insn (gen_rtx_SET (VOIDmode,
dest,
gen_rtx_IF_THEN_ELSE (mode,
gen_rtx_NE (CCmode, br_reg,
const0_rtx),
true_value,
false_value)));
else
{
/* Emit conditional stores as two separate stores. This avoids a problem
where you have a conditional store, and one of the arms of the
conditional store is spilled to memory. */
emit_insn (gen_rtx_SET (VOIDmode,
dest,
gen_rtx_IF_THEN_ELSE (mode,
gen_rtx_NE (CCmode, br_reg,
const0_rtx),
true_value,
dest)));
emit_insn (gen_rtx_SET (VOIDmode,
dest,
gen_rtx_IF_THEN_ELSE (mode,
gen_rtx_EQ (CCmode, br_reg,
const0_rtx),
false_value,
dest)));
}
return TRUE;
}
/* In rare cases, correct code generation requires extra machine dependent
processing between the second jump optimization pass and delayed branch
scheduling. On those machines, define this macro as a C statement to act on
the code starting at INSN. */
void
d30v_machine_dependent_reorg (insn)
rtx insn ATTRIBUTE_UNUSED;
{
}
/* A C statement (sans semicolon) to update the integer variable COST based on
the relationship between INSN that is dependent on DEP_INSN through the
dependence LINK. The default is to make no adjustment to COST. This can be
used for example to specify to the scheduler that an output- or
anti-dependence does not incur the same cost as a data-dependence. */
/* For the d30v, try to insure that the source operands for a load/store are
set 2 cycles before the memory reference. */
int
d30v_adjust_cost (insn, link, dep_insn, cost)
rtx insn;
rtx link ATTRIBUTE_UNUSED;
rtx dep_insn;
int cost;
{
rtx set_dep = single_set (dep_insn);
rtx set_insn = single_set (insn);
if (set_dep != NULL_RTX && set_insn != NULL_RTX
&& GET_CODE (SET_DEST (set_dep)) == REG)
{
rtx reg = SET_DEST (set_dep);
rtx mem;
if ((GET_CODE (mem = SET_SRC (set_insn)) == MEM
&& reg_mentioned_p (reg, XEXP (mem, 0)))
|| (GET_CODE (mem = SET_DEST (set_insn)) == MEM
&& reg_mentioned_p (reg, XEXP (mem, 0))))
{
return cost + ((HAIFA_P) ? 2 : 4);
}
}
return cost;
}
/* Routine to allocate, mark and free a per-function,
machine specific structure. */
static void
d30v_init_machine_status (p)
struct function *p;
{
p->machine =
(machine_function *) xcalloc (1, sizeof (machine_function));
}
static void
d30v_mark_machine_status (p)
struct function * p;
{
if (p->machine == NULL)
return;
ggc_mark_rtx (p->machine->ra_rtx);
ggc_mark_rtx (p->machine->eh_epilogue_sp_ofs);
}
static void
d30v_free_machine_status (p)
struct function *p;
{
struct machine_function *machine = p->machine;
if (machine == NULL)
return;
free (machine);
p->machine = NULL;
}
/* Do anything needed before RTL is emitted for each function. */
void
d30v_init_expanders ()
{
/* Arrange to save and restore machine status around nested functions. */
init_machine_status = d30v_init_machine_status;
mark_machine_status = d30v_mark_machine_status;
free_machine_status = d30v_free_machine_status;
}
/* Find the current function's return address.
??? It would be better to arrange things such that if we would ordinarily
have been a leaf function and we didn't spill the hard reg that we
wouldn't have to save the register in the prolog. But it's not clear
how to get the right information at the right time. */
rtx
d30v_return_addr ()
{
rtx ret;
ret = cfun->machine->ra_rtx;
if (ret == NULL)
{
rtx init;
cfun->machine->ra_rtx = ret = gen_reg_rtx (Pmode);
init = gen_rtx (SET, VOIDmode, ret, gen_rtx (REG, Pmode, GPR_LINK));
push_topmost_sequence ();
emit_insn_after (init, get_insns ());
pop_topmost_sequence ();
}
return ret;
}
/* Called to register all of our global variables with the garbage
collector. */
static void
d30v_add_gc_roots ()
{
ggc_add_rtx_root (&d30v_compare_op0, 1);
ggc_add_rtx_root (&d30v_compare_op1, 1);
}