mirror of
https://github.com/pmret/gcc-papermario.git
synced 2024-11-09 20:32:32 +01:00
1537 lines
40 KiB
C
1537 lines
40 KiB
C
/* Subroutines used for code generation on AMD Am29000.
|
||
Copyright (C) 1987, 88, 90-94, 1995, 1997 Free Software Foundation, Inc.
|
||
Contributed by Richard Kenner (kenner@nyu.edu)
|
||
|
||
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 <stdio.h>
|
||
#include "rtl.h"
|
||
#include "regs.h"
|
||
#include "hard-reg-set.h"
|
||
#include "real.h"
|
||
#include "insn-config.h"
|
||
#include "conditions.h"
|
||
#include "insn-flags.h"
|
||
#include "output.h"
|
||
#include "insn-attr.h"
|
||
#include "flags.h"
|
||
#include "recog.h"
|
||
#include "expr.h"
|
||
#include "obstack.h"
|
||
#include "tree.h"
|
||
#include "reload.h"
|
||
|
||
#define min(A,B) ((A) < (B) ? (A) : (B))
|
||
|
||
/* This gives the size in words of the register stack for the current
|
||
procedure. */
|
||
|
||
static int a29k_regstack_size;
|
||
|
||
/* True if the current procedure has a call instruction. */
|
||
|
||
static int a29k_makes_calls;
|
||
|
||
/* This points to the last insn of the insn prologue. It is set when
|
||
an insn without a filled delay slot is found near the start of the
|
||
function. */
|
||
|
||
static char *a29k_last_prologue_insn;
|
||
|
||
/* This points to the first insn that will be in the epilogue. It is null if
|
||
no epilogue is required. */
|
||
|
||
static char *a29k_first_epilogue_insn;
|
||
|
||
/* This is nonzero if a a29k_first_epilogue_insn was put in a delay slot. It
|
||
indicates that an intermediate label needs to be written. */
|
||
|
||
static int a29k_first_epilogue_insn_used;
|
||
|
||
/* Location to hold the name of the current function. We need this prolog to
|
||
contain the tag words prior to the declaration. So the name must be stored
|
||
away. */
|
||
|
||
char *a29k_function_name;
|
||
|
||
/* Mapping of registers to debug register numbers. The only change is
|
||
for the frame pointer and the register numbers used for the incoming
|
||
arguments. */
|
||
|
||
int a29k_debug_reg_map[FIRST_PSEUDO_REGISTER];
|
||
|
||
/* Save information from a "cmpxx" operation until the branch or scc is
|
||
emitted. */
|
||
|
||
rtx a29k_compare_op0, a29k_compare_op1;
|
||
int a29k_compare_fp_p;
|
||
|
||
/* Gives names for registers. */
|
||
extern char *reg_names[];
|
||
|
||
/* Returns 1 if OP is a 8-bit constant. */
|
||
|
||
int
|
||
cint_8_operand (op, mode)
|
||
register rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
return GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffffff00) == 0;
|
||
}
|
||
|
||
/* Returns 1 if OP is a 16-bit constant. */
|
||
|
||
int
|
||
cint_16_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
return GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffff0000) == 0;
|
||
}
|
||
|
||
/* Returns 1 if OP is a constant that cannot be moved in a single insn. */
|
||
|
||
int
|
||
long_const_operand (op, mode)
|
||
register rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
if (! CONSTANT_P (op))
|
||
return 0;
|
||
|
||
if (TARGET_29050 && GET_CODE (op) == CONST_INT
|
||
&& (INTVAL (op) & 0xffff) == 0)
|
||
return 0;
|
||
|
||
return (GET_CODE (op) != CONST_INT
|
||
|| ((INTVAL (op) & 0xffff0000) != 0
|
||
&& (INTVAL (op) & 0xffff0000) != 0xffff0000
|
||
&& INTVAL (op) != 0x80000000));
|
||
}
|
||
|
||
/* The following four functions detect constants of 0, 8, 16, and 24 used as
|
||
a position in ZERO_EXTRACT operations. They can either be the appropriate
|
||
constant integer or a shift (which will be produced by combine). */
|
||
|
||
static int
|
||
shift_constant_operand (op, mode, val)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
int val;
|
||
{
|
||
return ((GET_CODE (op) == CONST_INT && INTVAL (op) == val)
|
||
|| (GET_CODE (op) == ASHIFT
|
||
&& GET_CODE (XEXP (op, 0)) == CONST_INT
|
||
&& INTVAL (XEXP (op, 0)) == val / 8
|
||
&& GET_CODE (XEXP (op, 1)) == CONST_INT
|
||
&& INTVAL (XEXP (op, 1)) == 3));
|
||
}
|
||
|
||
int
|
||
const_0_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
return shift_constant_operand (op, mode, 0);
|
||
}
|
||
|
||
int
|
||
const_8_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
return shift_constant_operand (op, mode, 8);
|
||
}
|
||
|
||
int
|
||
const_16_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
return shift_constant_operand (op, mode, 16);
|
||
}
|
||
|
||
int
|
||
const_24_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
return shift_constant_operand (op, mode, 24);
|
||
}
|
||
|
||
/* Returns 1 if OP is a floating-point constant of the proper mode. */
|
||
|
||
int
|
||
float_const_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
return GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == mode;
|
||
}
|
||
|
||
/* Returns 1 if OP is a floating-point constant of the proper mode or a
|
||
general-purpose register. */
|
||
|
||
int
|
||
gpc_reg_or_float_constant_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
return float_const_operand (op, mode) || gpc_reg_operand (op, mode);
|
||
}
|
||
|
||
/* Returns 1 if OP is an integer constant of the proper mode or a
|
||
general-purpose register. */
|
||
|
||
int
|
||
gpc_reg_or_integer_constant_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
return ((GET_MODE (op) == VOIDmode
|
||
&& (GET_CODE (op) == CONST_INT || GET_CODE (op) == CONST_DOUBLE))
|
||
|| gpc_reg_operand (op, mode));
|
||
}
|
||
|
||
/* Returns 1 if OP is a special machine register. */
|
||
|
||
int
|
||
spec_reg_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
if (GET_CODE (op) != REG || GET_MODE (op) != mode)
|
||
return 0;
|
||
|
||
switch (GET_MODE_CLASS (mode))
|
||
{
|
||
case MODE_PARTIAL_INT:
|
||
return REGNO (op) >= R_BP && REGNO (op) <= R_CR;
|
||
case MODE_INT:
|
||
return REGNO (op) >= R_Q && REGNO (op) <= R_EXO;
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/* Returns 1 if OP is an accumulator register. */
|
||
|
||
int
|
||
accum_reg_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
return (GET_CODE (op) == REG
|
||
&& REGNO (op) >= R_ACU (0) && REGNO (op) <= R_ACU (3));
|
||
}
|
||
|
||
/* Returns 1 if OP is a normal data register. */
|
||
|
||
int
|
||
gpc_reg_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
int regno;
|
||
|
||
if (GET_MODE (op) != mode && mode != VOIDmode)
|
||
return 0;
|
||
|
||
if (GET_CODE (op) == REG)
|
||
regno = REGNO (op);
|
||
else if (GET_CODE (op) == SUBREG && GET_CODE (SUBREG_REG (op)) == REG)
|
||
{
|
||
regno = REGNO (SUBREG_REG (op));
|
||
if (regno < FIRST_PSEUDO_REGISTER)
|
||
regno += SUBREG_WORD (op);
|
||
}
|
||
else
|
||
return 0;
|
||
|
||
return (regno >= FIRST_PSEUDO_REGISTER || regno < R_BP
|
||
|| (regno >= R_KR (0) && regno <= R_KR (31)));
|
||
}
|
||
|
||
/* Returns 1 if OP is either an 8-bit constant integer or a general register.
|
||
If a register, it must be in the proper mode unless MODE is VOIDmode. */
|
||
|
||
int
|
||
srcb_operand (op, mode)
|
||
register rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
if (GET_CODE (op) == CONST_INT
|
||
&& (mode == QImode
|
||
|| (INTVAL (op) & 0xffffff00) == 0))
|
||
return 1;
|
||
|
||
if (GET_MODE (op) != mode && mode != VOIDmode)
|
||
return 0;
|
||
|
||
return gpc_reg_operand (op, mode);
|
||
}
|
||
|
||
int
|
||
cmplsrcb_operand (op, mode)
|
||
register rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
if (GET_CODE (op) == CONST_INT
|
||
&& (mode == QImode
|
||
|| (INTVAL (op) & 0xffffff00) == 0xffffff00))
|
||
return 1;
|
||
|
||
if (GET_MODE (op) != mode && mode != VOIDmode)
|
||
return 0;
|
||
|
||
return gpc_reg_operand (op, mode);
|
||
}
|
||
|
||
/* Return 1 if OP is either an immediate or a general register. This is used
|
||
for the input operand of mtsr/mtrsim. */
|
||
|
||
int
|
||
gpc_reg_or_immediate_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
return gpc_reg_operand (op, mode) || immediate_operand (op, mode);
|
||
}
|
||
|
||
/* Return 1 if OP can be used as the second operand of and AND insn. This
|
||
includes srcb_operand and a constant whose complement fits in 8 bits. */
|
||
|
||
int
|
||
and_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
return (srcb_operand (op, mode)
|
||
|| (GET_CODE (op) == CONST_INT
|
||
&& ((unsigned) ((~ INTVAL (op)) & GET_MODE_MASK (mode)) < 256)));
|
||
}
|
||
|
||
/* Return 1 if OP can be used as the second operand of an ADD insn.
|
||
This is the same as above, except we use negative, rather than
|
||
complement. */
|
||
|
||
int
|
||
add_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
return (srcb_operand (op, mode)
|
||
|| (GET_CODE (op) == CONST_INT
|
||
&& ((unsigned) ((- INTVAL (op)) & GET_MODE_MASK (mode)) < 256)));
|
||
}
|
||
|
||
/* Return 1 if OP is a valid address in a CALL_INSN. These are a SYMBOL_REF
|
||
to the current function, all SYMBOL_REFs if TARGET_SMALL_MEMORY, or
|
||
a sufficiently-small constant. */
|
||
|
||
int
|
||
call_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
switch (GET_CODE (op))
|
||
{
|
||
case SYMBOL_REF:
|
||
return (TARGET_SMALL_MEMORY
|
||
|| (! TARGET_LARGE_MEMORY
|
||
&& ((GET_CODE (op) == SYMBOL_REF && SYMBOL_REF_FLAG (op))
|
||
|| ! strcmp (XSTR (op, 0), current_function_name))));
|
||
|
||
case CONST_INT:
|
||
return (unsigned HOST_WIDE_INT) INTVAL (op) < 0x40000;
|
||
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/* Return 1 if OP can be used as the input operand for a move insn. */
|
||
|
||
int
|
||
in_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
rtx orig_op = op;
|
||
|
||
if (! general_operand (op, mode))
|
||
return 0;
|
||
|
||
while (GET_CODE (op) == SUBREG)
|
||
op = SUBREG_REG (op);
|
||
|
||
switch (GET_CODE (op))
|
||
{
|
||
case REG:
|
||
return 1;
|
||
|
||
case MEM:
|
||
return (GET_MODE_SIZE (mode) >= UNITS_PER_WORD || TARGET_DW_ENABLE);
|
||
|
||
case CONST_INT:
|
||
if (GET_MODE_CLASS (mode) != MODE_INT
|
||
&& GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
|
||
return 0;
|
||
|
||
return 1;
|
||
|
||
case CONST:
|
||
case SYMBOL_REF:
|
||
case LABEL_REF:
|
||
return (GET_MODE (op) == mode
|
||
|| mode == SImode || mode == HImode || mode == QImode);
|
||
|
||
case CONST_DOUBLE:
|
||
return ((GET_MODE_CLASS (mode) == MODE_FLOAT
|
||
&& mode == GET_MODE (op))
|
||
|| (GET_MODE (op) == VOIDmode
|
||
&& GET_MODE_CLASS (mode) == MODE_INT));
|
||
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/* Return 1 if OP can be used as the output operand for a move insn. */
|
||
|
||
int
|
||
out_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
rtx orig_op = op;
|
||
|
||
if (! general_operand (op, mode))
|
||
return 0;
|
||
|
||
while (GET_CODE (op) == SUBREG)
|
||
op = SUBREG_REG (op);
|
||
|
||
if (GET_CODE (op) == REG)
|
||
return (gpc_reg_operand (orig_op, mode)
|
||
|| spec_reg_operand (orig_op, mode)
|
||
|| (GET_MODE_CLASS (mode) == MODE_FLOAT
|
||
&& accum_reg_operand (orig_op, mode)));
|
||
|
||
else if (GET_CODE (op) == MEM)
|
||
return (GET_MODE_SIZE (mode) >= UNITS_PER_WORD || TARGET_DW_ENABLE);
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
/* Return 1 if OP is an item in memory, given that we are in reload. */
|
||
|
||
int
|
||
reload_memory_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
int regno = true_regnum (op);
|
||
|
||
return (! CONSTANT_P (op)
|
||
&& (regno == -1
|
||
|| (GET_CODE (op) == REG
|
||
&& REGNO (op) >= FIRST_PSEUDO_REGISTER)));
|
||
}
|
||
|
||
/* Given an object for which reload_memory_operand is true, return the address
|
||
of the operand, taking into account anything that reload may do. */
|
||
|
||
rtx
|
||
a29k_get_reloaded_address (op)
|
||
rtx op;
|
||
{
|
||
if (GET_CODE (op) == SUBREG)
|
||
{
|
||
if (SUBREG_WORD (op) != 0)
|
||
abort ();
|
||
|
||
op = SUBREG_REG (op);
|
||
}
|
||
|
||
if (GET_CODE (op) == REG)
|
||
op = reg_equiv_mem[REGNO (op)];
|
||
|
||
return find_replacement (&XEXP (op, 0));
|
||
}
|
||
|
||
/* Subfunction of the following function. Update the flags of any MEM
|
||
found in part of X. */
|
||
|
||
static void
|
||
a29k_set_memflags_1 (x, in_struct_p, volatile_p, unchanging_p)
|
||
rtx x;
|
||
int in_struct_p, volatile_p, unchanging_p;
|
||
{
|
||
int i;
|
||
|
||
switch (GET_CODE (x))
|
||
{
|
||
case SEQUENCE:
|
||
case PARALLEL:
|
||
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
|
||
a29k_set_memflags_1 (XVECEXP (x, 0, i), in_struct_p, volatile_p,
|
||
unchanging_p);
|
||
break;
|
||
|
||
case INSN:
|
||
a29k_set_memflags_1 (PATTERN (x), in_struct_p, volatile_p,
|
||
unchanging_p);
|
||
break;
|
||
|
||
case SET:
|
||
a29k_set_memflags_1 (SET_DEST (x), in_struct_p, volatile_p,
|
||
unchanging_p);
|
||
a29k_set_memflags_1 (SET_SRC (x), in_struct_p, volatile_p, unchanging_p);
|
||
break;
|
||
|
||
case MEM:
|
||
MEM_IN_STRUCT_P (x) = in_struct_p;
|
||
MEM_VOLATILE_P (x) = volatile_p;
|
||
RTX_UNCHANGING_P (x) = unchanging_p;
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Given INSN, which is either an INSN or a SEQUENCE generated to
|
||
perform a memory operation, look for any MEMs in either a SET_DEST or
|
||
a SET_SRC and copy the in-struct, unchanging, and volatile flags from
|
||
REF into each of the MEMs found. If REF is not a MEM, don't do
|
||
anything. */
|
||
|
||
void
|
||
a29k_set_memflags (insn, ref)
|
||
rtx insn;
|
||
rtx ref;
|
||
{
|
||
/* Note that it is always safe to get these flags, though they won't
|
||
be what we think if REF is not a MEM. */
|
||
int in_struct_p = MEM_IN_STRUCT_P (ref);
|
||
int volatile_p = MEM_VOLATILE_P (ref);
|
||
int unchanging_p = RTX_UNCHANGING_P (ref);
|
||
|
||
if (GET_CODE (ref) != MEM
|
||
|| (! in_struct_p && ! volatile_p && ! unchanging_p))
|
||
return;
|
||
|
||
a29k_set_memflags_1 (insn, in_struct_p, volatile_p, unchanging_p);
|
||
}
|
||
|
||
/* Return 1 if OP is a comparison operator that we have in floating-point. */
|
||
|
||
int
|
||
fp_comparison_operator (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
return ((mode == VOIDmode || mode == GET_MODE (op))
|
||
&& (GET_CODE (op) == EQ || GET_CODE (op) == GT ||
|
||
GET_CODE (op) == GE));
|
||
}
|
||
|
||
/* Return 1 if OP is a valid branch comparison. */
|
||
|
||
int
|
||
branch_operator (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
return ((mode == VOIDmode || mode == GET_MODE (op))
|
||
&& (GET_CODE (op) == GE || GET_CODE (op) == LT));
|
||
}
|
||
|
||
/* Return 1 if OP is a load multiple operation. It is known to be a
|
||
PARALLEL and the first three sections will be tested. */
|
||
|
||
int
|
||
load_multiple_operation (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
int count = XVECLEN (op, 0) - 2;
|
||
int dest_regno;
|
||
rtx src_addr;
|
||
int i;
|
||
|
||
/* Perform a quick check so we don't blow up below. */
|
||
if (count <= 1
|
||
|| GET_CODE (XVECEXP (op, 0, 0)) != SET
|
||
|| GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG
|
||
|| GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM)
|
||
return 0;
|
||
|
||
dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0)));
|
||
src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0);
|
||
|
||
for (i = 1; i < count; i++)
|
||
{
|
||
rtx elt = XVECEXP (op, 0, i + 2);
|
||
|
||
if (GET_CODE (elt) != SET
|
||
|| GET_CODE (SET_DEST (elt)) != REG
|
||
|| GET_MODE (SET_DEST (elt)) != SImode
|
||
|| REGNO (SET_DEST (elt)) != dest_regno + i
|
||
|| GET_CODE (SET_SRC (elt)) != MEM
|
||
|| GET_MODE (SET_SRC (elt)) != SImode
|
||
|| GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
|
||
|| ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
|
||
|| GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
|
||
|| INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != i * 4)
|
||
return 0;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
/* Similar, but tests for store multiple. */
|
||
|
||
int
|
||
store_multiple_operation (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
int num_special = TARGET_NO_STOREM_BUG ? 2 : 1;
|
||
int count = XVECLEN (op, 0) - num_special;
|
||
int src_regno;
|
||
rtx dest_addr;
|
||
int i;
|
||
|
||
/* Perform a quick check so we don't blow up below. */
|
||
if (count <= 1
|
||
|| GET_CODE (XVECEXP (op, 0, 0)) != SET
|
||
|| GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM
|
||
|| GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG)
|
||
return 0;
|
||
|
||
src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0)));
|
||
dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0);
|
||
|
||
for (i = 1; i < count; i++)
|
||
{
|
||
rtx elt = XVECEXP (op, 0, i + num_special);
|
||
|
||
if (GET_CODE (elt) != SET
|
||
|| GET_CODE (SET_SRC (elt)) != REG
|
||
|| GET_MODE (SET_SRC (elt)) != SImode
|
||
|| REGNO (SET_SRC (elt)) != src_regno + i
|
||
|| GET_CODE (SET_DEST (elt)) != MEM
|
||
|| GET_MODE (SET_DEST (elt)) != SImode
|
||
|| GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
|
||
|| ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
|
||
|| GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
|
||
|| INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != i * 4)
|
||
return 0;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
/* Given a special register REG and MASK, a value being masked against a
|
||
quantity to which the special register is set, return 1 if the masking
|
||
operation is built-in to the setting of that special register. */
|
||
|
||
int
|
||
masks_bits_for_special (reg, mask)
|
||
rtx reg;
|
||
rtx mask;
|
||
{
|
||
int needed_mask_value;
|
||
|
||
if (GET_CODE (reg) != REG || GET_CODE (mask) != CONST_INT)
|
||
abort ();
|
||
|
||
switch (REGNO (reg))
|
||
{
|
||
case R_BP:
|
||
case R_INT:
|
||
needed_mask_value = 3;
|
||
break;
|
||
|
||
case R_FC:
|
||
needed_mask_value = 31;
|
||
break;
|
||
|
||
case R_CR:
|
||
case R_LRU:
|
||
needed_mask_value = 255;
|
||
break;
|
||
|
||
case R_FPE:
|
||
needed_mask_value = 511;
|
||
break;
|
||
|
||
case R_MMU:
|
||
needed_mask_value = 0x3ff;
|
||
break;
|
||
|
||
case R_OPS:
|
||
case R_CPS:
|
||
case R_RBP:
|
||
case R_FPS:
|
||
needed_mask_value = 0xffff;
|
||
break;
|
||
|
||
case R_VAB:
|
||
needed_mask_value = 0xffff0000;
|
||
break;
|
||
|
||
case R_Q:
|
||
case R_CFG:
|
||
case R_CHA:
|
||
case R_CHD:
|
||
case R_CHC:
|
||
case R_TMC:
|
||
case R_TMR:
|
||
case R_PC0:
|
||
case R_PC1:
|
||
case R_PC2:
|
||
return 0;
|
||
|
||
default:
|
||
abort ();
|
||
}
|
||
|
||
return (INTVAL (mask) & ~ needed_mask_value) == 0;
|
||
}
|
||
|
||
/* Return nonzero if this label is that of the return point, but there is
|
||
a non-null epilogue. */
|
||
|
||
int
|
||
epilogue_operand (op, mode)
|
||
rtx op;
|
||
enum machine_mode mode;
|
||
{
|
||
return next_active_insn (op) == 0 && a29k_first_epilogue_insn != 0;
|
||
}
|
||
|
||
/* Return the register class of a scratch register needed to copy IN into
|
||
or out of a register in CLASS in MODE. If it can be done directly,
|
||
NO_REGS is returned. */
|
||
|
||
enum reg_class
|
||
secondary_reload_class (class, mode, in)
|
||
enum reg_class class;
|
||
enum machine_mode mode;
|
||
rtx in;
|
||
{
|
||
int regno = -1;
|
||
enum rtx_code code = GET_CODE (in);
|
||
|
||
if (! CONSTANT_P (in))
|
||
{
|
||
regno = true_regnum (in);
|
||
|
||
/* A pseudo is the same as memory. */
|
||
if (regno == -1 || regno >= FIRST_PSEUDO_REGISTER)
|
||
code = MEM;
|
||
}
|
||
|
||
/* If we are transferring between memory and a multi-word mode, we need
|
||
CR. */
|
||
|
||
if (code == MEM && GET_MODE_SIZE (mode) > UNITS_PER_WORD)
|
||
return CR_REGS;
|
||
|
||
/* If between memory and a mode smaller than a word without DW being
|
||
enabled, we need BP. */
|
||
|
||
if (code == MEM && ! TARGET_DW_ENABLE
|
||
&& GET_MODE_SIZE (mode) < UNITS_PER_WORD)
|
||
return BP_REGS;
|
||
|
||
/* Otherwise, we can place anything into GENERAL_REGS and can put
|
||
GENERAL_REGS into anything. */
|
||
if (class == GENERAL_REGS
|
||
|| (regno != -1
|
||
&& (regno < R_BP
|
||
|| (regno >= R_KR (0) && regno <= R_KR (31)))))
|
||
return NO_REGS;
|
||
|
||
/* We can place 16-bit constants into a special register. */
|
||
if (code == CONST_INT
|
||
&& (GET_MODE_BITSIZE (mode) <= 16 || (unsigned) INTVAL (in) <= 65535)
|
||
&& (class == BP_REGS || class == Q_REGS || class == SPECIAL_REGS))
|
||
return NO_REGS;
|
||
|
||
/* Otherwise, we need GENERAL_REGS. */
|
||
return GENERAL_REGS;
|
||
}
|
||
|
||
/* START is the zero-based incoming argument register index used (0 is 160,
|
||
i.e., the first incoming argument register) and COUNT is the number used.
|
||
|
||
Mark the corresponding incoming registers as neither fixed nor call used.
|
||
For each register used for incoming arguments, we have one less local
|
||
register that can be used. So also mark some high-numbered registers as
|
||
fixed.
|
||
|
||
Return the first register number to use for the argument. */
|
||
|
||
int
|
||
incoming_reg (start, count)
|
||
int start;
|
||
int count;
|
||
{
|
||
int i;
|
||
|
||
/* We only use 16 argument registers, so truncate at the end of the
|
||
area. */
|
||
if (start + count > 16)
|
||
count = 16 - start;
|
||
|
||
if (! TARGET_NO_REUSE_ARGS)
|
||
/* Mark all the used registers as not fixed and saved over calls. */
|
||
for (i = R_AR (start); i < R_AR (start + count); i++)
|
||
{
|
||
fixed_regs[i] = call_used_regs[i] = call_fixed_regs[i] = 0;
|
||
CLEAR_HARD_REG_BIT (fixed_reg_set, i);
|
||
CLEAR_HARD_REG_BIT (call_used_reg_set, i);
|
||
CLEAR_HARD_REG_BIT (call_fixed_reg_set, i);
|
||
}
|
||
|
||
/* Shorten the maximum size of the frame.
|
||
Remember that R_AR(-1,-2) are place holders for the caller's lr0,lr1.
|
||
Make sure to keep the frame rounded to an even boundary. Rounding up
|
||
to an 8 byte boundary will use a slot. Otherwise a frame with 121 local
|
||
regs and 5 arguments will overrun the stack (121+1 + 5 + 2 > 128). */
|
||
/* ??? An alternative would be to never allocate one reg. */
|
||
for (i = (R_AR (0) - 2 - start - count) & ~1; i < R_AR (0) - 2 - start; i++)
|
||
{
|
||
fixed_regs[i] = call_used_regs[i] = call_fixed_regs[i] = 1;
|
||
SET_HARD_REG_BIT (fixed_reg_set, i);
|
||
SET_HARD_REG_BIT (call_used_reg_set, i);
|
||
SET_HARD_REG_BIT (call_fixed_reg_set, i);
|
||
}
|
||
|
||
return R_AR (start);
|
||
}
|
||
|
||
/* Add CLOBBERs to CALL_INSN_FUNCTION_USAGE chain of INSN indicating
|
||
that LR2 up to, but not including, OP are clobbered. If OP is
|
||
zero, indicate all parameter registers are clobbered. */
|
||
|
||
void
|
||
a29k_clobbers_to (insn, op)
|
||
rtx insn;
|
||
rtx op;
|
||
{
|
||
int i;
|
||
int high_regno;
|
||
|
||
if (op == 0)
|
||
high_regno = R_LR (18);
|
||
else if (GET_CODE (op) != REG || REGNO (op) < R_LR (0)
|
||
|| REGNO (op) > R_LR (18))
|
||
abort ();
|
||
else
|
||
high_regno = REGNO (op);
|
||
|
||
for (i = R_LR (2); i < high_regno; i++)
|
||
CALL_INSN_FUNCTION_USAGE (insn)
|
||
= gen_rtx (EXPR_LIST, VOIDmode,
|
||
gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, SImode, i)),
|
||
CALL_INSN_FUNCTION_USAGE (insn));
|
||
}
|
||
|
||
/* These routines are used in finding insns to fill delay slots in the
|
||
epilogue. */
|
||
|
||
/* Return 1 if the current function will adjust the register stack. */
|
||
|
||
int
|
||
needs_regstack_p ()
|
||
{
|
||
int i;
|
||
rtx insn;
|
||
|
||
if (frame_pointer_needed)
|
||
return 1;
|
||
|
||
/* If any local register is used, we need to adjust the regstack. */
|
||
for (i = R_LR (127); i >= R_LR (0); i --)
|
||
if (regs_ever_live[i])
|
||
return 1;
|
||
|
||
/* We need a register stack if we make any calls. */
|
||
for (insn = get_insns (); insn; insn = next_insn (insn))
|
||
if (GET_CODE (insn) == CALL_INSN
|
||
|| (GET_CODE (insn) == INSN
|
||
&& GET_CODE (PATTERN (insn)) == SEQUENCE
|
||
&& GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == CALL_INSN))
|
||
return 1;
|
||
|
||
/* Otherwise, we don't. */
|
||
return 0;
|
||
}
|
||
|
||
/* Return 1 if X uses a local register. */
|
||
|
||
int
|
||
uses_local_reg_p (x)
|
||
rtx x;
|
||
{
|
||
char *fmt;
|
||
int i, j;
|
||
|
||
switch (GET_CODE (x))
|
||
{
|
||
case REG:
|
||
return REGNO (x) >= R_LR (0) && REGNO (x) <= R_FP;
|
||
|
||
case CONST_INT:
|
||
case CONST:
|
||
case PC:
|
||
case CC0:
|
||
case LABEL_REF:
|
||
case SYMBOL_REF:
|
||
return 0;
|
||
}
|
||
|
||
fmt = GET_RTX_FORMAT (GET_CODE (x));
|
||
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
|
||
{
|
||
if (fmt[i] == 'e')
|
||
{
|
||
if (uses_local_reg_p (XEXP (x, i)))
|
||
return 1;
|
||
}
|
||
else if (fmt[i] == 'E')
|
||
{
|
||
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
|
||
if (uses_local_reg_p (XVECEXP (x, i, j)))
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Returns 1 if this function is known to have a null epilogue. */
|
||
|
||
int
|
||
null_epilogue ()
|
||
{
|
||
return (reload_completed && ! needs_regstack_p ()
|
||
&& get_frame_size () == 0
|
||
&& current_function_pretend_args_size == 0);
|
||
}
|
||
|
||
/* Write out the assembler form of an operand. Recognize the following
|
||
special options:
|
||
|
||
%N means write the low-order 8 bits of the negative of the constant
|
||
%Q means write a QImode operand (truncate constants to 8 bits)
|
||
%M means write the low-order 16 bits of the constant
|
||
%m means write the low-order 16 bits shifted left 16 bits
|
||
%C means write the low-order 8 bits of the complement of the constant
|
||
%b means write `f' is this is a reversed condition, `t' otherwise
|
||
%B means write `t' is this is a reversed condition, `f' otherwise
|
||
%J means write the 29k opcode part for a comparison operation
|
||
%e means write the label with an extra `X' is this is the epilogue
|
||
otherwise the normal label name
|
||
%E means write nothing if this insn has a delay slot,
|
||
a nop unless this is the epilogue label, in which case
|
||
write the first epilogue insn
|
||
%F means write just the normal operand if the insn has a delay slot;
|
||
otherwise, this is a recursive call so output the
|
||
symbol + 4 and write the first prologue insn in the
|
||
delay slot.
|
||
%L means write the register number plus one ("low order" register)
|
||
or the low-order part of a multi-word constant
|
||
%O means write the register number plus two
|
||
%P means write the register number plus three ("low order" of TImode)
|
||
%S means write the number of words in the mode of the operand,
|
||
minus one (for CR)
|
||
%V means write the number of elements in a PARALLEL minus 1
|
||
%# means write nothing if we have a delay slot, "\n\tnop" otherwise
|
||
%* means write the register name for TPC. */
|
||
|
||
void
|
||
print_operand (file, x, code)
|
||
FILE *file;
|
||
rtx x;
|
||
char code;
|
||
{
|
||
char buf[100];
|
||
|
||
/* These macros test for integers and extract the low-order bits. */
|
||
#define INT_P(X) \
|
||
((GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST_DOUBLE) \
|
||
&& GET_MODE (X) == VOIDmode)
|
||
|
||
#define INT_LOWPART(X) \
|
||
(GET_CODE (X) == CONST_INT ? INTVAL (X) : CONST_DOUBLE_LOW (X))
|
||
|
||
switch (code)
|
||
{
|
||
case 'Q':
|
||
if (GET_CODE (x) == REG)
|
||
break;
|
||
else if (! INT_P (x))
|
||
output_operand_lossage ("invalid %%Q value");
|
||
fprintf (file, "%d", INT_LOWPART (x) & 0xff);
|
||
return;
|
||
|
||
case 'C':
|
||
if (! INT_P (x))
|
||
output_operand_lossage ("invalid %%C value");
|
||
fprintf (file, "%d", (~ INT_LOWPART (x)) & 0xff);
|
||
return;
|
||
|
||
case 'N':
|
||
if (! INT_P (x))
|
||
output_operand_lossage ("invalid %%N value");
|
||
fprintf (file, "%d", (- INT_LOWPART (x)) & 0xff);
|
||
return;
|
||
|
||
case 'M':
|
||
if (! INT_P (x))
|
||
output_operand_lossage ("invalid %%M value");
|
||
fprintf (file, "%d", INT_LOWPART (x) & 0xffff);
|
||
return;
|
||
|
||
case 'm':
|
||
if (! INT_P (x))
|
||
output_operand_lossage ("invalid %%m value");
|
||
fprintf (file, "%d", (INT_LOWPART (x) & 0xffff) << 16);
|
||
return;
|
||
|
||
case 'b':
|
||
if (GET_CODE (x) == GE)
|
||
fprintf (file, "f");
|
||
else
|
||
fprintf (file, "t");
|
||
return;
|
||
|
||
case 'B':
|
||
if (GET_CODE (x) == GE)
|
||
fprintf (file, "t");
|
||
else
|
||
fprintf (file, "f");
|
||
return;
|
||
|
||
case 'J':
|
||
/* It so happens that the RTX names for the conditions are the same as
|
||
the 29k's insns except for "ne", which requires "neq". */
|
||
fprintf (file, GET_RTX_NAME (GET_CODE (x)));
|
||
if (GET_CODE (x) == NE)
|
||
fprintf (file, "q");
|
||
return;
|
||
|
||
case 'e':
|
||
if (optimize && flag_delayed_branch
|
||
&& a29k_last_prologue_insn == 0 && epilogue_operand (x, VOIDmode)
|
||
&& dbr_sequence_length () == 0)
|
||
{
|
||
/* We need to output the label number of the last label in the
|
||
function, which is not necessarily X since there might be
|
||
a USE insn in between. First go forward to the last insn, then
|
||
back up to a label. */
|
||
while (NEXT_INSN (x) != 0)
|
||
x = NEXT_INSN (x);
|
||
|
||
while (GET_CODE (x) != CODE_LABEL)
|
||
x = PREV_INSN (x);
|
||
|
||
ASM_GENERATE_INTERNAL_LABEL (buf, "LX", CODE_LABEL_NUMBER (x));
|
||
assemble_name (file, buf);
|
||
}
|
||
else
|
||
output_asm_label (x);
|
||
return;
|
||
|
||
case 'E':
|
||
if (dbr_sequence_length ())
|
||
;
|
||
else if (a29k_last_prologue_insn)
|
||
{
|
||
fprintf (file, "\n\t%s", a29k_last_prologue_insn);
|
||
a29k_last_prologue_insn = 0;
|
||
}
|
||
else if (optimize && flag_delayed_branch
|
||
&& epilogue_operand (x, VOIDmode))
|
||
{
|
||
fprintf (file, "\n\t%s", a29k_first_epilogue_insn);
|
||
a29k_first_epilogue_insn_used = 1;
|
||
}
|
||
else
|
||
fprintf (file, "\n\tnop");
|
||
return;
|
||
|
||
case 'F':
|
||
output_addr_const (file, x);
|
||
if (dbr_sequence_length () == 0)
|
||
{
|
||
/* If this doesn't have its delay slot filled, see if we need to
|
||
put the last insn of the prolog in it. If not, see if this is
|
||
a recursive call. If so, we can put the first insn of its
|
||
prolog in the delay slot. Otherwise, write a nop. */
|
||
if (a29k_last_prologue_insn)
|
||
{
|
||
fprintf (file, "\n\t%s", a29k_last_prologue_insn);
|
||
a29k_last_prologue_insn = 0;
|
||
}
|
||
else if (GET_CODE (x) == SYMBOL_REF
|
||
&& ! strcmp (XSTR (x, 0), current_function_name))
|
||
fprintf (file, "+4\n\t%s,%d",
|
||
a29k_regstack_size >= 64 ? "const gr121" : "sub gr1,gr1",
|
||
a29k_regstack_size * 4);
|
||
else
|
||
fprintf (file, "\n\tnop");
|
||
}
|
||
return;
|
||
|
||
case 'L':
|
||
if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == DFmode)
|
||
{
|
||
union real_extract u;
|
||
|
||
bcopy ((char *) &CONST_DOUBLE_LOW (x), (char *) &u, sizeof u);
|
||
fprintf (file, "$double1(%.20e)", u.d);
|
||
}
|
||
else if (GET_CODE (x) == REG)
|
||
fprintf (file, "%s", reg_names[REGNO (x) + 1]);
|
||
else
|
||
output_operand_lossage ("invalid %%L value");
|
||
return;
|
||
|
||
case 'O':
|
||
if (GET_CODE (x) != REG)
|
||
output_operand_lossage ("invalid %%O value");
|
||
fprintf (file, "%s", reg_names[REGNO (x) + 2]);
|
||
return;
|
||
|
||
case 'P':
|
||
if (GET_CODE (x) != REG)
|
||
output_operand_lossage ("invalid %%P value");
|
||
fprintf (file, "%s", reg_names[REGNO (x) + 3]);
|
||
return;
|
||
|
||
case 'S':
|
||
fprintf (file, "%d", (GET_MODE_SIZE (GET_MODE (x)) / UNITS_PER_WORD)-1);
|
||
return;
|
||
|
||
case 'V':
|
||
if (GET_CODE (x) != PARALLEL)
|
||
output_operand_lossage ("invalid %%V value");
|
||
fprintf (file, "%d", XVECLEN (x, 0) - 2);
|
||
return;
|
||
|
||
case '#':
|
||
if (dbr_sequence_length () == 0)
|
||
{
|
||
if (a29k_last_prologue_insn)
|
||
{
|
||
fprintf (file, "\n\t%s", a29k_last_prologue_insn);
|
||
a29k_last_prologue_insn = 0;
|
||
}
|
||
else
|
||
fprintf (file, "\n\tnop");
|
||
}
|
||
return;
|
||
|
||
case '*':
|
||
fprintf (file, "%s", reg_names [R_TPC]);
|
||
return;
|
||
}
|
||
|
||
if (GET_CODE (x) == REG)
|
||
fprintf (file, "%s", reg_names [REGNO (x)]);
|
||
|
||
else if (GET_CODE (x) == MEM)
|
||
output_address (XEXP (x, 0));
|
||
|
||
else if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == SUBREG
|
||
&& GET_CODE (SUBREG_REG (XEXP (x, 0))) == CONST_DOUBLE)
|
||
{
|
||
union real_extract u;
|
||
|
||
if (GET_MODE (SUBREG_REG (XEXP (x, 0))) == SFmode)
|
||
fprintf (file, "$float");
|
||
else
|
||
fprintf (file, "$double%d", SUBREG_WORD (XEXP (x, 0)));
|
||
bcopy ((char *) &CONST_DOUBLE_LOW (SUBREG_REG (XEXP (x, 0))),
|
||
(char *) &u, sizeof u);
|
||
fprintf (file, "(%.20e)", u.d);
|
||
}
|
||
|
||
else if (GET_CODE (x) == CONST_DOUBLE
|
||
&& GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
|
||
{
|
||
union real_extract u;
|
||
|
||
bcopy ((char *) &CONST_DOUBLE_LOW (x), (char *) &u, sizeof u);
|
||
fprintf (file, "$%s(%.20e)",
|
||
GET_MODE (x) == SFmode ? "float" : "double0", u.d);
|
||
}
|
||
|
||
else
|
||
output_addr_const (file, x);
|
||
}
|
||
|
||
/* This page contains routines to output function prolog and epilog code. */
|
||
|
||
/* Compute the size of the register stack, and determine if there are any
|
||
call instructions. */
|
||
|
||
static void
|
||
compute_regstack_size ()
|
||
{
|
||
int i;
|
||
rtx insn;
|
||
|
||
/* See if we make any calls. We need to set lr1 if so. */
|
||
a29k_makes_calls = 0;
|
||
for (insn = get_insns (); insn; insn = next_insn (insn))
|
||
if (GET_CODE (insn) == CALL_INSN
|
||
|| (GET_CODE (insn) == INSN
|
||
&& GET_CODE (PATTERN (insn)) == SEQUENCE
|
||
&& GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == CALL_INSN))
|
||
{
|
||
a29k_makes_calls = 1;
|
||
break;
|
||
}
|
||
|
||
/* Find the highest local register used. */
|
||
for (i = R_LR (127); i >= R_LR (0); i--)
|
||
if (regs_ever_live[i])
|
||
break;
|
||
|
||
a29k_regstack_size = i - (R_LR (0) - 1);
|
||
|
||
/* If calling routines, ensure we count lr0 & lr1. */
|
||
if (a29k_makes_calls && a29k_regstack_size < 2)
|
||
a29k_regstack_size = 2;
|
||
|
||
/* Count frame pointer and align to 8 byte boundary (even number of
|
||
registers). */
|
||
a29k_regstack_size += frame_pointer_needed;
|
||
if (a29k_regstack_size & 1) a29k_regstack_size++;
|
||
}
|
||
|
||
/* Sets register names for incoming arguments and frame pointer.
|
||
This can't be computed until after register allocation. */
|
||
|
||
void
|
||
a29k_compute_reg_names ()
|
||
{
|
||
int i;
|
||
|
||
compute_regstack_size ();
|
||
|
||
/* Set the names and numbers of the frame pointer and incoming argument
|
||
registers. */
|
||
|
||
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
||
a29k_debug_reg_map[i] = i;
|
||
|
||
reg_names[FRAME_POINTER_REGNUM] = reg_names[R_LR (a29k_regstack_size - 1)];
|
||
a29k_debug_reg_map[FRAME_POINTER_REGNUM] = R_LR (a29k_regstack_size - 1);
|
||
|
||
for (i = 0; i < 16; i++)
|
||
{
|
||
reg_names[R_AR (i)] = reg_names[R_LR (a29k_regstack_size + i + 2)];
|
||
a29k_debug_reg_map[R_AR (i)] = R_LR (a29k_regstack_size + i + 2);
|
||
}
|
||
|
||
/* If using kernel register map, swap numbers for kernel and user
|
||
registers. */
|
||
if (TARGET_KERNEL_REGISTERS)
|
||
for (i = 0; i < 32; i++)
|
||
{
|
||
int tem = a29k_debug_reg_map[i];
|
||
a29k_debug_reg_map[i] = a29k_debug_reg_map[R_KR (i)];
|
||
a29k_debug_reg_map[R_KR (i)] = tem;
|
||
}
|
||
}
|
||
|
||
/* Output function prolog code to file FILE. Memory stack size is SIZE. */
|
||
|
||
void
|
||
output_prolog (file, size)
|
||
FILE *file;
|
||
int size;
|
||
{
|
||
int i;
|
||
int arg_count = 0;
|
||
rtx insn;
|
||
unsigned int tag_word;
|
||
|
||
/* See how many incoming arguments we have in registers. */
|
||
for (i = R_AR (0); i < R_AR (16); i++)
|
||
if (! fixed_regs[i])
|
||
arg_count++;
|
||
|
||
/* The argument count includes the caller's lr0 and lr1. */
|
||
arg_count += 2;
|
||
|
||
/* Compute memory stack size. Add in number of bytes that the we should
|
||
push and pretend the caller did and the size of outgoing arguments.
|
||
Then round to a doubleword boundary. */
|
||
size += (current_function_pretend_args_size
|
||
+ current_function_outgoing_args_size);
|
||
size = (size + 7) & ~7;
|
||
|
||
/* Write header words. See if one or two word form. */
|
||
tag_word = (frame_pointer_needed ? 0x400000 : 0) + (arg_count << 16);
|
||
|
||
if (size / 8 > 0xff)
|
||
fprintf (file, "\t.word %d, 0x%0x\n", (size / 8) << 2,
|
||
0x800000 + tag_word);
|
||
else
|
||
fprintf (file, "\t.word 0x%0x\n", tag_word + ((size / 8) << 3));
|
||
|
||
/* Define the function name. */
|
||
assemble_name (file, a29k_function_name);
|
||
fprintf (file, ":\n");
|
||
|
||
/* Push the register stack by the proper amount. There are two possible
|
||
ways to do this. */
|
||
if (a29k_regstack_size >= 256/4)
|
||
fprintf (file, "\tconst %s,%d\n\tsub gr1,gr1,%s\n",
|
||
reg_names[R_TAV], a29k_regstack_size * 4, reg_names[R_TAV]);
|
||
else if (a29k_regstack_size)
|
||
fprintf (file, "\tsub gr1,gr1,%d\n", a29k_regstack_size * 4);
|
||
|
||
/* Test that the registers are available. */
|
||
if (a29k_regstack_size)
|
||
fprintf (file, "\tasgeu V_%sSPILL,gr1,%s\n",
|
||
TARGET_KERNEL_REGISTERS ? "K" : "", reg_names[R_RAB]);
|
||
|
||
/* Set up frame pointer, if one is needed. */
|
||
if (frame_pointer_needed)
|
||
fprintf (file, "\tsll %s,%s,0\n", reg_names[FRAME_POINTER_REGNUM],
|
||
reg_names[R_MSP]);
|
||
|
||
/* Make room for any frame space. There are three ways to do this. */
|
||
if (size >= 256)
|
||
{
|
||
fprintf (file, "\tconst %s,%d\n", reg_names[R_TAV], size);
|
||
if (size >= 65536)
|
||
fprintf (file, "\tconsth %s,%d\n", reg_names[R_TAV], size);
|
||
if (TARGET_STACK_CHECK)
|
||
fprintf (file, "\tcall %s,__msp_check\n", reg_names[R_TPC]);
|
||
fprintf (file, "\tsub %s,%s,%s\n",
|
||
reg_names[R_MSP], reg_names[R_MSP], reg_names[R_TAV]);
|
||
}
|
||
else if (size)
|
||
{
|
||
if (TARGET_STACK_CHECK)
|
||
fprintf (file, "\tcall %s,__msp_check\n", reg_names[R_TPC]);
|
||
fprintf (file, "\tsub %s,%s,%d\n",
|
||
reg_names[R_MSP], reg_names[R_MSP], size);
|
||
}
|
||
|
||
/* If this routine will make calls, set lr1. If we see an insn that
|
||
can use a delay slot before a call or jump, save this insn for that
|
||
slot (this condition is equivalent to seeing if we have an insn that
|
||
needs delay slots before an insn that has a filled delay slot). */
|
||
a29k_last_prologue_insn = 0;
|
||
if (a29k_makes_calls)
|
||
{
|
||
i = (a29k_regstack_size + arg_count) * 4;
|
||
if (i >= 256)
|
||
fprintf (file, "\tconst %s,%d\n\tadd lr1,gr1,%s\n",
|
||
reg_names[R_TAV], i, reg_names[R_TAV]);
|
||
else
|
||
{
|
||
if (optimize && flag_delayed_branch)
|
||
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
||
{
|
||
if (GET_CODE (insn) == CODE_LABEL
|
||
|| (GET_CODE (insn) == INSN
|
||
&& GET_CODE (PATTERN (insn)) == SEQUENCE))
|
||
break;
|
||
|
||
if (GET_CODE (insn) == NOTE
|
||
|| (GET_CODE (insn) == INSN
|
||
&& (GET_CODE (PATTERN (insn)) == USE
|
||
|| GET_CODE (PATTERN (insn)) == CLOBBER)))
|
||
continue;
|
||
|
||
if (num_delay_slots (insn) > 0)
|
||
{
|
||
a29k_last_prologue_insn = (char *) oballoc (100);
|
||
sprintf (a29k_last_prologue_insn, "add lr1,gr1,%d", i);
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (a29k_last_prologue_insn == 0)
|
||
fprintf (file, "\tadd lr1,gr1,%d\n", i);
|
||
}
|
||
}
|
||
|
||
/* Compute the first insn of the epilogue. */
|
||
a29k_first_epilogue_insn_used = 0;
|
||
|
||
if (size == 0 && a29k_regstack_size == 0 && ! frame_pointer_needed)
|
||
a29k_first_epilogue_insn = 0;
|
||
else
|
||
a29k_first_epilogue_insn = (char *) oballoc (100);
|
||
|
||
if (frame_pointer_needed)
|
||
sprintf (a29k_first_epilogue_insn, "sll %s,%s,0",
|
||
reg_names[R_MSP], reg_names[FRAME_POINTER_REGNUM]);
|
||
else if (a29k_regstack_size)
|
||
{
|
||
if (a29k_regstack_size >= 256 / 4)
|
||
sprintf (a29k_first_epilogue_insn, "const %s,%d",
|
||
reg_names[R_TAV], a29k_regstack_size * 4);
|
||
else
|
||
sprintf (a29k_first_epilogue_insn, "add gr1,gr1,%d",
|
||
a29k_regstack_size * 4);
|
||
}
|
||
else if (size)
|
||
{
|
||
if (size >= 256)
|
||
sprintf (a29k_first_epilogue_insn, "const %s,%d",
|
||
reg_names[R_TAV], size);
|
||
else
|
||
sprintf (a29k_first_epilogue_insn, "add %s,%s,%d",
|
||
reg_names[R_MSP], reg_names[R_MSP], size);
|
||
}
|
||
}
|
||
|
||
/* Call this after writing what might be the first instruction of the
|
||
epilogue. If that first insn was used in a delay slot, an intermediate
|
||
label is written. */
|
||
|
||
static void
|
||
check_epilogue_internal_label (file)
|
||
FILE *file;
|
||
{
|
||
rtx insn;
|
||
|
||
if (! a29k_first_epilogue_insn_used)
|
||
return;
|
||
|
||
for (insn = get_last_insn ();
|
||
GET_CODE (insn) != CODE_LABEL;
|
||
insn = PREV_INSN (insn))
|
||
;
|
||
|
||
ASM_OUTPUT_INTERNAL_LABEL (file, "LX", CODE_LABEL_NUMBER (insn));
|
||
a29k_first_epilogue_insn_used = 0;
|
||
}
|
||
|
||
/* Output the epilog of the last procedure to file FILE. SIZE is the memory
|
||
stack size. The register stack size is in the variable
|
||
A29K_REGSTACK_SIZE. */
|
||
|
||
void
|
||
output_epilog (file, size)
|
||
FILE *file;
|
||
int size;
|
||
{
|
||
rtx insn;
|
||
int locals_unavailable = 0; /* True until after first insn
|
||
after gr1 update. */
|
||
|
||
/* If we hit a BARRIER before a real insn or CODE_LABEL, we don't
|
||
need to do anything because we are never jumped to. */
|
||
insn = get_last_insn ();
|
||
if (GET_CODE (insn) == NOTE)
|
||
insn = prev_nonnote_insn (insn);
|
||
|
||
if (insn && GET_CODE (insn) == BARRIER)
|
||
return;
|
||
|
||
/* If a frame pointer was needed we must restore the memory stack pointer
|
||
before adjusting the register stack. */
|
||
if (frame_pointer_needed)
|
||
{
|
||
fprintf (file, "\tsll %s,%s,0\n",
|
||
reg_names[R_MSP], reg_names[FRAME_POINTER_REGNUM]);
|
||
check_epilogue_internal_label (file);
|
||
}
|
||
|
||
/* Restore the register stack. There are two ways to do this. */
|
||
if (a29k_regstack_size)
|
||
{
|
||
if (a29k_regstack_size >= 256/4)
|
||
{
|
||
fprintf (file, "\tconst %s,%d\n",
|
||
reg_names[R_TAV], a29k_regstack_size * 4);
|
||
check_epilogue_internal_label (file);
|
||
fprintf (file, "\tadd gr1,gr1,%s\n", reg_names[R_TAV]);
|
||
}
|
||
else
|
||
{
|
||
fprintf (file, "\tadd gr1,gr1,%d\n", a29k_regstack_size * 4);
|
||
check_epilogue_internal_label (file);
|
||
}
|
||
locals_unavailable = 1;
|
||
}
|
||
|
||
/* Restore the memory stack pointer if there is no frame pointer.
|
||
Adjust the size to include any pretend arguments and pushed
|
||
arguments and round to doubleword boundary. */
|
||
size += (current_function_pretend_args_size
|
||
+ current_function_outgoing_args_size);
|
||
size = (size + 7) & ~7;
|
||
|
||
if (size && ! frame_pointer_needed)
|
||
{
|
||
if (size >= 256)
|
||
{
|
||
fprintf (file, "\tconst %s,%d\n", reg_names[R_TAV], size);
|
||
check_epilogue_internal_label (file);
|
||
locals_unavailable = 0;
|
||
if (size >= 65536)
|
||
fprintf (file, "\tconsth %s,%d\n", reg_names[R_TAV], size);
|
||
fprintf (file, "\tadd %s,%s,%s\n",
|
||
reg_names[R_MSP], reg_names[R_MSP], reg_names[R_TAV]);
|
||
}
|
||
else
|
||
{
|
||
fprintf (file, "\tadd %s,%s,%d\n",
|
||
reg_names[R_MSP], reg_names[R_MSP], size);
|
||
check_epilogue_internal_label (file);
|
||
locals_unavailable = 0;
|
||
}
|
||
}
|
||
|
||
if (locals_unavailable)
|
||
{
|
||
/* If we have an insn for this delay slot, write it. */
|
||
if (current_function_epilogue_delay_list)
|
||
final_scan_insn (XEXP (current_function_epilogue_delay_list, 0),
|
||
file, 1, -2, 1);
|
||
else
|
||
fprintf (file, "\tnop\n");
|
||
}
|
||
|
||
fprintf (file, "\tjmpi lr0\n");
|
||
if (a29k_regstack_size)
|
||
fprintf (file, "\tasleu V_%sFILL,lr1,%s\n",
|
||
TARGET_KERNEL_REGISTERS ? "K" : "", reg_names[R_RFB]);
|
||
else if (current_function_epilogue_delay_list)
|
||
final_scan_insn (XEXP (current_function_epilogue_delay_list, 0),
|
||
file, 1, -2, 1);
|
||
else
|
||
fprintf (file, "\tnop\n");
|
||
}
|