Commit ff9940b0 by Richard Earnshaw

Major rewrite -- See ChangeLog for details

From-SVN: r5564
parent b980bec0
/* Output routines for GCC for ARM/RISCiX. /* Output routines for GCC for ARM/RISCiX.
Copyright (C) 1991 Free Software Foundation, Inc. Copyright (C) 1991, 1993 Free Software Foundation, Inc.
Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl) Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
and Martin Simmons (@harleqn.co.uk). and Martin Simmons (@harleqn.co.uk).
More major hacks by Richard Earnshaw (rwe11@cl.cam.ac.uk)
This file is part of GNU CC. This file is part of GNU CC.
...@@ -18,7 +19,7 @@ GNU General Public License for more details. ...@@ -18,7 +19,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <stdio.h> #include <stdio.h>
#include "assert.h" #include "assert.h"
#include "config.h" #include "config.h"
...@@ -43,6 +44,16 @@ extern char *output_multi_immediate (); ...@@ -43,6 +44,16 @@ extern char *output_multi_immediate ();
extern char *arm_output_asm_insn (); extern char *arm_output_asm_insn ();
extern void arm_increase_location (); extern void arm_increase_location ();
/* Define the information needed to generate branch insns. This is
stored from the compare operation. */
rtx arm_compare_op0, arm_compare_op1;
int arm_compare_fp;
/* What type of cpu are we compiling for? */
enum processor_type arm_cpu;
/* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we /* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we
must report the mode of the memory reference from PRINT_OPERAND to must report the mode of the memory reference from PRINT_OPERAND to
PRINT_OPERAND_ADDRESS. */ PRINT_OPERAND_ADDRESS. */
...@@ -54,6 +65,10 @@ int current_function_anonymous_args; ...@@ -54,6 +65,10 @@ int current_function_anonymous_args;
/* Location counter of .text segment. */ /* Location counter of .text segment. */
int arm_text_location = 0; int arm_text_location = 0;
/* Set to one if we think that lr is only saved because of subroutine calls,
but all of these can be `put after' return insns */
int lr_save_eliminated;
/* A hash table is used to store text segment labels and their associated /* A hash table is used to store text segment labels and their associated
offset from the start of the text segment. */ offset from the start of the text segment. */
struct label_offset struct label_offset
...@@ -67,13 +82,38 @@ struct label_offset ...@@ -67,13 +82,38 @@ struct label_offset
static struct label_offset *offset_table[LABEL_HASH_SIZE]; static struct label_offset *offset_table[LABEL_HASH_SIZE];
/* Set to 1 when a return insn is output, this means that the epilogue
is not needed. */
static int return_used_this_function;
/* For an explanation of these variables, see final_prescan_insn below. */ /* For an explanation of these variables, see final_prescan_insn below. */
int arm_ccfsm_state; int arm_ccfsm_state;
int arm_current_cc; int arm_current_cc;
rtx arm_target_insn; rtx arm_target_insn;
int arm_target_label; int arm_target_label;
char *arm_condition_codes[];
/* Return 1 if it is possible to return using a single instruction */
int
use_return_insn ()
{
int regno;
if (!reload_completed ||current_function_pretend_args_size
|| current_function_anonymous_args
|| (get_frame_size () && !(TARGET_APCS || frame_pointer_needed)))
return 0;
/* Can't be done if any of the FPU regs are pushed, since this also
requires an insn */
for (regno = 20; regno < 24; regno++)
if (regs_ever_live[regno])
return 0;
return 1;
}
/* Return the number of mov instructions needed to get the constant VALUE into /* Return the number of mov instructions needed to get the constant VALUE into
a register. */ a register. */
...@@ -96,38 +136,134 @@ arm_const_nmoves (value) ...@@ -96,38 +136,134 @@ arm_const_nmoves (value)
int int
const_ok_for_arm (i) const_ok_for_arm (i)
int i; HOST_WIDE_INT i;
{ {
unsigned int mask = ~0xFF; unsigned HOST_WIDE_INT mask = ~0xFF;
do do
{ {
if ((i & mask) == 0) if ((i & mask & 0xffffffffu) == 0)
return(TRUE); return(TRUE);
mask = (mask << 2) | (mask >> (32 - 2)); mask = (mask << 2) | ((mask & 0xffffffffu) >> (32 - 2)) | ~0xffffffffu;
} while (mask != ~0xFF); } while (mask != ~0xFF);
return (FALSE); return (FALSE);
} /* const_ok_for_arm */ } /* const_ok_for_arm */
/* This code has been fixed for cross compilation. */
static int fpa_consts_inited = 0;
char *strings_fpa[8] = {
"0.0",
"1.0",
"2.0",
"3.0",
"4.0",
"5.0",
"0.5",
"10.0"
};
static REAL_VALUE_TYPE values_fpa[8];
static void
init_fpa_table ()
{
int i;
REAL_VALUE_TYPE r;
for (i = 0; i < 8; i++)
{
r = REAL_VALUE_ATOF (strings_fpa[i], DFmode);
values_fpa[i] = r;
}
fpa_consts_inited = 1;
}
/* Return TRUE if rtx X is a valid immediate FPU constant. */ /* Return TRUE if rtx X is a valid immediate FPU constant. */
int int
const_double_rtx_ok_for_fpu (x) const_double_rtx_ok_for_fpu (x)
rtx x; rtx x;
{ {
double d; REAL_VALUE_TYPE r;
union real_extract u; int i;
u.i[0] = CONST_DOUBLE_LOW(x);
u.i[1] = CONST_DOUBLE_HIGH(x); if (!fpa_consts_inited)
d = u.d; init_fpa_table ();
return (d == 0.0 || d == 1.0 || d == 2.0 || d == 3.0 REAL_VALUE_FROM_CONST_DOUBLE (r, x);
|| d == 4.0 || d == 5.0 || d == 0.5 || d == 10.0); if (REAL_VALUE_MINUS_ZERO (r))
return 0;
for (i = 0; i < 8; i++)
if (REAL_VALUES_EQUAL (r, values_fpa[i]))
return 1;
return 0;
} /* const_double_rtx_ok_for_fpu */ } /* const_double_rtx_ok_for_fpu */
/* Return TRUE if rtx X is a valid immediate FPU constant. */
int
neg_const_double_rtx_ok_for_fpu (x)
rtx x;
{
REAL_VALUE_TYPE r;
int i;
if (!fpa_consts_inited)
init_fpa_table ();
REAL_VALUE_FROM_CONST_DOUBLE (r, x);
r = REAL_VALUE_NEGATE (r);
if (REAL_VALUE_MINUS_ZERO (r))
return 0;
for (i = 0; i < 8; i++)
if (REAL_VALUES_EQUAL (r, values_fpa[i]))
return 1;
return 0;
} /* neg_const_double_rtx_ok_for_fpu */
/* Predicates for `match_operand' and `match_operator'. */ /* Predicates for `match_operand' and `match_operator'. */
/* s_register_operand is the same as register_operand, but it doesn't accept
(SUBREG (MEM)...). */
int
s_register_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return 0;
if (GET_CODE (op) == SUBREG)
{
op = SUBREG_REG (op);
}
/* We don't consider registers whose class is NO_REGS
to be a register operand. */
return (GET_CODE (op) == REG
&& (REGNO (op) >= FIRST_PSEUDO_REGISTER
|| REGNO_REG_CLASS (REGNO (op)) != NO_REGS));
}
/* 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)));
}
/* Return TRUE for valid operands for the rhs of an ARM instruction. */ /* Return TRUE for valid operands for the rhs of an ARM instruction. */
int int
...@@ -135,10 +271,48 @@ arm_rhs_operand (op, mode) ...@@ -135,10 +271,48 @@ arm_rhs_operand (op, mode)
rtx op; rtx op;
enum machine_mode mode; enum machine_mode mode;
{ {
return (register_operand (op, mode) return (s_register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op)))); || (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op))));
} /* arm_rhs_operand */ } /* arm_rhs_operand */
/* Return TRUE for valid operands for the rhs of an ARM instruction, or a load.
*/
int
arm_rhsm_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (s_register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op)))
|| memory_operand (op, mode));
} /* arm_rhs_operand */
/* Return TRUE for valid operands for the rhs of an ARM instruction, or if a
constant that is valid when negated. */
int
arm_add_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (s_register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT
&& (const_ok_for_arm (INTVAL (op))
|| const_ok_for_arm (-INTVAL (op)))));
} /* arm_rhs_operand */
int
arm_not_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (s_register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT
&& (const_ok_for_arm (INTVAL (op))
|| const_ok_for_arm (~INTVAL (op)))));
} /* arm_rhs_operand */
/* Return TRUE for valid operands for the rhs of an FPU instruction. */ /* Return TRUE for valid operands for the rhs of an FPU instruction. */
int int
...@@ -146,13 +320,26 @@ fpu_rhs_operand (op, mode) ...@@ -146,13 +320,26 @@ fpu_rhs_operand (op, mode)
rtx op; rtx op;
enum machine_mode mode; enum machine_mode mode;
{ {
if (register_operand (op, mode)) if (s_register_operand (op, mode))
return(TRUE); return(TRUE);
else if (GET_CODE (op) == CONST_DOUBLE) else if (GET_CODE (op) == CONST_DOUBLE)
return (const_double_rtx_ok_for_fpu (op)); return (const_double_rtx_ok_for_fpu (op));
else return (FALSE); else return (FALSE);
} /* fpu_rhs_operand */ } /* fpu_rhs_operand */
int
fpu_add_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (s_register_operand (op, mode))
return(TRUE);
else if (GET_CODE (op) == CONST_DOUBLE)
return const_double_rtx_ok_for_fpu (op)
|| neg_const_double_rtx_ok_for_fpu (op);
return (FALSE);
}
/* Return nonzero if OP is a constant power of two. */ /* Return nonzero if OP is a constant power of two. */
int int
...@@ -169,15 +356,16 @@ power_of_two_operand (op, mode) ...@@ -169,15 +356,16 @@ power_of_two_operand (op, mode)
} /* power_of_two_operand */ } /* power_of_two_operand */
/* Return TRUE for a valid operand of a DImode operation. /* Return TRUE for a valid operand of a DImode operation.
Either: REG, CONST_DOUBLE or MEM(offsettable). Either: REG, CONST_DOUBLE or MEM(DImode_address).
Note that this disallows MEM(REG+REG). */ Note that this disallows MEM(REG+REG), but allows
MEM(PRE/POST_INC/DEC(REG)). */
int int
di_operand (op, mode) di_operand (op, mode)
rtx op; rtx op;
enum machine_mode mode; enum machine_mode mode;
{ {
if (register_operand (op, mode)) if (s_register_operand (op, mode))
return (TRUE); return (TRUE);
switch (GET_CODE (op)) switch (GET_CODE (op))
...@@ -186,8 +374,7 @@ di_operand (op, mode) ...@@ -186,8 +374,7 @@ di_operand (op, mode)
case CONST_INT: case CONST_INT:
return (TRUE); return (TRUE);
case MEM: case MEM:
return (memory_address_p (DImode, XEXP (op, 0)) return (memory_address_p (DImode, XEXP (op, 0)));
&& offsettable_address_p (FALSE, DImode, XEXP (op, 0)));
default: default:
return (FALSE); return (FALSE);
} }
...@@ -200,10 +387,25 @@ index_operand (op, mode) ...@@ -200,10 +387,25 @@ index_operand (op, mode)
rtx op; rtx op;
enum machine_mode mode; enum machine_mode mode;
{ {
return (register_operand(op, mode) return (s_register_operand(op, mode)
|| (immediate_operand (op, mode) && abs (INTVAL (op)) < 4096)); || (immediate_operand (op, mode)
&& INTVAL (op) < 4096 && INTVAL (op) > -4096));
} /* index_operand */ } /* index_operand */
/* Return TRUE for valid shifts by a constant. This also accepts any
power of two on the (somewhat overly relaxed) assumption that the
shift operator in this case was a mult. */
int
const_shift_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (power_of_two_operand (op, mode)
|| (immediate_operand (op, mode)
&& (INTVAL (op) < 32 && INTVAL (op) > 0)));
} /* const_shift_operand */
/* Return TRUE for arithmetic operators which can be combined with a multiply /* Return TRUE for arithmetic operators which can be combined with a multiply
(shift). */ (shift). */
...@@ -236,13 +438,384 @@ shift_operator (x, mode) ...@@ -236,13 +438,384 @@ shift_operator (x, mode)
{ {
enum rtx_code code = GET_CODE (x); enum rtx_code code = GET_CODE (x);
if (code == MULT)
return power_of_two_operand (XEXP (x, 1));
return (code == ASHIFT || code == LSHIFT return (code == ASHIFT || code == LSHIFT
|| code == ASHIFTRT || code == LSHIFTRT); || code == ASHIFTRT || code == LSHIFTRT);
} }
} /* shift_operator */ } /* shift_operator */
int equality_operator (x, mode)
rtx x;
enum machine_mode mode;
{
return (GET_CODE (x) == EQ || GET_CODE (x) == NE);
}
/* Return TRUE for SMIN SMAX UMIN UMAX operators. */
int
minmax_operator (x, mode)
rtx x;
enum machine_mode mode;
{
enum rtx_code code = GET_CODE (x);
if (GET_MODE (x) != mode)
return FALSE;
return code == SMIN || code == SMAX || code == UMIN || code == UMAX;
} /* minmax_operator */
/* return TRUE if x is EQ or NE */
/* Return TRUE if this is the condition code register, if we aren't given
a mode, accept any class CCmode register */
int
cc_register (x, mode)
rtx x;
enum machine_mode mode;
{
if (mode == VOIDmode)
{
mode = GET_MODE (x);
if (GET_MODE_CLASS (mode) != MODE_CC)
return FALSE;
}
if (mode == GET_MODE (x) && GET_CODE (x) == REG && REGNO (x) == 24)
return TRUE;
return FALSE;
}
enum rtx_code
minmax_code (x)
rtx x;
{
enum rtx_code code = GET_CODE (x);
if (code == SMAX)
return GE;
if (code == SMIN)
return LE;
if (code == UMIN)
return LEU;
if (code == UMAX)
return GEU;
abort ();
}
/* Return 1 if memory locations are adjacent */
adjacent_mem_locations (a, b)
rtx a, b;
{
int val0 = 0, val1 = 0;
int reg0, reg1;
if ((GET_CODE (XEXP (a, 0)) == REG
|| (GET_CODE (XEXP (a, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (a, 0), 1)) == CONST_INT))
&& (GET_CODE (XEXP (b, 0)) == REG
|| (GET_CODE (XEXP (b, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (b, 0), 1)) == CONST_INT)))
{
if (GET_CODE (XEXP (a, 0)) == PLUS)
{
reg0 = REGNO (XEXP (XEXP (a, 0), 0));
val0 = INTVAL (XEXP (XEXP (a, 0), 1));
}
else
reg0 = REGNO (XEXP (a, 0));
if (GET_CODE (XEXP (b, 0)) == PLUS)
{
reg1 = REGNO (XEXP (XEXP (b, 0), 0));
val1 = INTVAL (XEXP (XEXP (b, 0), 1));
}
else
reg1 = REGNO (XEXP (b, 0));
return (reg0 == reg1) && ((val1 - val0) == 4 || (val0 - val1) == 4);
}
return 0;
}
/* Return 1 if OP is a load multiple operation. It is known to be
parallel and the first section will be tested. */
load_multiple_operation (op, mode)
rtx op;
enum machine_mode mode;
{
int count = XVECLEN (op, 0);
int dest_regno;
rtx src_addr;
int i = 1, base = 0;
rtx elt;
if (count <= 1
|| GET_CODE (XVECEXP (op, 0, 0)) != SET)
return 0;
/* Check to see if this might be a write-back */
if (GET_CODE (SET_SRC (elt = XVECEXP (op, 0, 0))) == PLUS)
{
i++;
base = 1;
/* Now check it more carefully */
if (GET_CODE (SET_DEST (elt)) != REG
|| GET_CODE (XEXP (SET_SRC (elt), 0)) != REG
|| REGNO (XEXP (SET_SRC (elt), 0)) != REGNO (SET_DEST (elt))
|| GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT
|| INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 2) * 4
|| GET_CODE (XVECEXP (op, 0, count - 1)) != CLOBBER
|| GET_CODE (XEXP (XVECEXP (op, 0, count - 1), 0)) != REG
|| REGNO (XEXP (XVECEXP (op, 0, count - 1), 0))
!= REGNO (SET_DEST (elt)))
return 0;
count--;
}
/* Perform a quick check so we don't blow up below. */
if (count <= i
|| GET_CODE (XVECEXP (op, 0, i - 1)) != SET
|| GET_CODE (SET_DEST (XVECEXP (op, 0, i - 1))) != REG
|| GET_CODE (SET_SRC (XVECEXP (op, 0, i - 1))) != MEM)
return 0;
dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, i - 1)));
src_addr = XEXP (SET_SRC (XVECEXP (op, 0, i - 1)), 0);
for (; i < count; i++)
{
rtx elt = XVECEXP (op, 0, i);
if (GET_CODE (elt) != SET
|| GET_CODE (SET_DEST (elt)) != REG
|| GET_MODE (SET_DEST (elt)) != SImode
|| REGNO (SET_DEST (elt)) != dest_regno + i - base
|| 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 - base) * 4)
return 0;
}
return 1;
}
/* Return 1 if OP is a store multiple operation. It is known to be
parallel and the first section will be tested. */
store_multiple_operation (op, mode)
rtx op;
enum machine_mode mode;
{
int count = XVECLEN (op, 0);
int src_regno;
rtx dest_addr;
int i = 1, base = 0;
rtx elt;
if (count <= 1
|| GET_CODE (XVECEXP (op, 0, 0)) != SET)
return 0;
/* Check to see if this might be a write-back */
if (GET_CODE (SET_SRC (elt = XVECEXP (op, 0, 0))) == PLUS)
{
i++;
base = 1;
/* Now check it more carefully */
if (GET_CODE (SET_DEST (elt)) != REG
|| GET_CODE (XEXP (SET_SRC (elt), 0)) != REG
|| REGNO (XEXP (SET_SRC (elt), 0)) != REGNO (SET_DEST (elt))
|| GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT
|| INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 2) * 4
|| GET_CODE (XVECEXP (op, 0, count - 1)) != CLOBBER
|| GET_CODE (XEXP (XVECEXP (op, 0, count - 1), 0)) != REG
|| REGNO (XEXP (XVECEXP (op, 0, count - 1), 0))
!= REGNO (SET_DEST (elt)))
return 0;
count--;
}
/* Perform a quick check so we don't blow up below. */
if (count <= i
|| GET_CODE (XVECEXP (op, 0, i - 1)) != SET
|| GET_CODE (SET_DEST (XVECEXP (op, 0, i - 1))) != MEM
|| GET_CODE (SET_SRC (XVECEXP (op, 0, i - 1))) != REG)
return 0;
src_regno = REGNO (SET_SRC (XVECEXP (op, 0, i - 1)));
dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, i - 1)), 0);
for (; i < count; i++)
{
elt = XVECEXP (op, 0, i);
if (GET_CODE (elt) != SET
|| GET_CODE (SET_SRC (elt)) != REG
|| GET_MODE (SET_SRC (elt)) != SImode
|| REGNO (SET_SRC (elt)) != src_regno + i - base
|| 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 - base) * 4)
return 0;
}
return 1;
}
/* Routines for use in generating RTL */
rtx arm_gen_load_multiple (base_regno, count, from, up, write_back)
int base_regno;
int count;
rtx from;
int up;
int write_back;
{
int i = 0, j;
rtx result;
int sign = up ? 1 : -1;
result = gen_rtx (PARALLEL, VOIDmode,
rtvec_alloc (count + (write_back ? 2 : 0)));
if (write_back)
{
XVECEXP (result, 0, 0)
= gen_rtx (SET, GET_MODE (from), from,
plus_constant (from, count * 4 * sign));
i = 1;
count++;
}
for (j = 0; i < count; i++, j++)
{
XVECEXP (result, 0, i)
= gen_rtx (SET, VOIDmode, gen_rtx (REG, SImode, base_regno + j),
gen_rtx (MEM, SImode,
plus_constant (from, j * 4 * sign)));
}
if (write_back)
XVECEXP (result, 0, i) = gen_rtx (CLOBBER, SImode, from);
return result;
}
rtx arm_gen_store_multiple (base_regno, count, to, up, write_back)
int base_regno;
int count;
rtx to;
int up;
int write_back;
{
int i = 0, j;
rtx result;
int sign = up ? 1 : -1;
result = gen_rtx (PARALLEL, VOIDmode,
rtvec_alloc (count + (write_back ? 2 : 0)));
if (write_back)
{
XVECEXP (result, 0, 0)
= gen_rtx (SET, GET_MODE (to), to,
plus_constant (to, count * 4 * sign));
i = 1;
count++;
}
for (j = 0; i < count; i++, j++)
{
XVECEXP (result, 0, i)
= gen_rtx (SET, VOIDmode,
gen_rtx (MEM, SImode, plus_constant (to, j * 4 * sign)),
gen_rtx (REG, SImode, base_regno + j));
}
if (write_back)
XVECEXP (result, 0, i) = gen_rtx (CLOBBER, SImode, to);
return result;
}
/* X and Y are two things to compare using CODE. Emit the compare insn and
return the rtx for register 0 in the proper mode. FP means this is a
floating point compare: I don't think that it is needed on the arm. */
rtx
gen_compare_reg (code, x, y, fp)
enum rtx_code code;
rtx x, y;
{
enum machine_mode mode = SELECT_CC_MODE (code, x, y);
rtx cc_reg = gen_rtx (REG, mode, 24);
emit_insn (gen_rtx (SET, VOIDmode, cc_reg,
gen_rtx (COMPARE, mode, x, y)));
return cc_reg;
}
/* Check to see if a branch is forwards or backwards. Return TRUE if it
is backwards. */
int
arm_backwards_branch (from, to)
int from, to;
{
return (insn_addresses[to] < insn_addresses[from]);
}
/* Check to see if a branch is within the distance that can be done using
an arithmetic expression. */
int
short_branch (from, to)
int from, to;
{
int delta = insn_addresses[from] + 2 - insn_addresses[to];
return abs (delta) < 245; /* A small margin for safety */
}
/* Check to see that the insn isn't the target of the conditionalizing
code */
int
arm_insn_not_targeted (insn)
rtx insn;
{
return insn != arm_target_insn;
}
/* Routines to output assembly language. */ /* Routines to output assembly language. */
/* fp_immediate_constant
if the rtx is the correct value then return the string of the number.
In this way we can ensure that valid double constants are generated even
when cross compiling. */
char *
fp_immediate_constant (x)
rtx (x);
{
REAL_VALUE_TYPE r;
int i;
if (!fpa_consts_inited)
init_fpa_table ();
REAL_VALUE_FROM_CONST_DOUBLE (r, x);
for (i = 0; i < 8; i++)
if (REAL_VALUES_EQUAL (r, values_fpa[i]))
return strings_fpa[i];
abort ();
}
/* Output the operands of a LDM/STM instruction to STREAM. /* Output the operands of a LDM/STM instruction to STREAM.
MASK is the ARM register set mask of which only bits 0-15 are important. MASK is the ARM register set mask of which only bits 0-15 are important.
INSTR is the possibly suffixed base register. HAT unequals zero if a hat INSTR is the possibly suffixed base register. HAT unequals zero if a hat
...@@ -289,6 +862,134 @@ output_call (operands) ...@@ -289,6 +862,134 @@ output_call (operands)
return (""); return ("");
} /* output_call */ } /* output_call */
static int
eliminate_lr2ip (x)
rtx *x;
{
int something_changed = 0;
rtx x0 = *x;
int code = GET_CODE (x0);
register int i, j;
register char *fmt;
switch (code)
{
case REG:
if (REGNO (x0) == 14)
{
*x = gen_rtx (REG, SImode, 12);
return 1;
}
return 0;
default:
/* Scan through the sub-elements and change any references there */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
if (fmt[i] == 'e')
something_changed |= eliminate_lr2ip (&XEXP (x0, i));
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x0, i); j++)
something_changed |= eliminate_lr2ip (&XVECEXP (x0, i, j));
return something_changed;
}
}
/* Output a 'call' insn that is a reference in memory. */
char *
output_call_mem (operands)
rtx operands[];
{
operands[0] = copy_rtx (operands[0]); /* Be ultra careful */
/* Handle calls using lr by using ip (which may be clobbered in subr anyway).
*/
if (eliminate_lr2ip (&operands[0]))
arm_output_asm_insn ("mov\tip, lr", operands);
arm_output_asm_insn ("mov\tlr, pc", operands);
arm_output_asm_insn ("ldr\tpc, %0", operands);
return ("");
} /* output_call */
/* Output a move from arm registers to an fpu registers.
OPERANDS[0] is an fpu register.
OPERANDS[1] is the first registers of an arm register pair. */
char *
output_mov_long_double_fpu_from_arm (operands)
rtx operands[];
{
int arm_reg0 = REGNO (operands[1]);
rtx ops[3];
if (arm_reg0 == 12)
abort();
ops[0] = gen_rtx (REG, SImode, arm_reg0);
ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0);
ops[2] = gen_rtx (REG, SImode, 2 + arm_reg0);
arm_output_asm_insn ("stmfd\tsp!, {%0, %1, %2}", ops);
arm_output_asm_insn ("ldfe\t%0, [sp], #12", operands);
return ("");
} /* output_mov_long_double_fpu_from_arm */
/* Output a move from an fpu register to arm registers.
OPERANDS[0] is the first registers of an arm register pair.
OPERANDS[1] is an fpu register. */
char *
output_mov_long_double_arm_from_fpu (operands)
rtx operands[];
{
int arm_reg0 = REGNO (operands[0]);
rtx ops[3];
if (arm_reg0 == 12)
abort();
ops[0] = gen_rtx (REG, SImode, arm_reg0);
ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0);
ops[2] = gen_rtx (REG, SImode, 2 + arm_reg0);
arm_output_asm_insn ("stfe\t%1, [sp, #-12]!", operands);
arm_output_asm_insn ("ldmfd\tsp!, {%0, %1, %2}", ops);
return("");
} /* output_mov_long_double_arm_from_fpu */
/* Output a move from arm registers to arm registers of a long double
OPERANDS[0] is the destination.
OPERANDS[1] is the source. */
char *
output_mov_long_double_arm_from_arm (operands)
rtx operands[];
{
/* We have to be careful here because the two might overlap */
int dest_start = REGNO (operands[0]);
int src_start = REGNO (operands[1]);
rtx ops[2];
int i;
if (dest_start < src_start)
{
for (i = 0; i < 3; i++)
{
ops[0] = gen_rtx (REG, SImode, dest_start + i);
ops[1] = gen_rtx (REG, SImode, src_start + i);
arm_output_asm_insn ("mov\t%0, %1", ops);
}
}
else
{
for (i = 2; i >= 0; i--)
{
ops[0] = gen_rtx (REG, SImode, dest_start + i);
ops[1] = gen_rtx (REG, SImode, src_start + i);
arm_output_asm_insn ("mov\t%0, %1", ops);
}
}
return "";
}
/* Output a move from arm registers to an fpu registers. /* Output a move from arm registers to an fpu registers.
OPERANDS[0] is an fpu register. OPERANDS[0] is an fpu register.
OPERANDS[1] is the first registers of an arm register pair. */ OPERANDS[1] is the first registers of an arm register pair. */
...@@ -371,26 +1072,51 @@ output_move_double (operands) ...@@ -371,26 +1072,51 @@ output_move_double (operands)
CONST_DOUBLE_HIGH (operands[1])); CONST_DOUBLE_HIGH (operands[1]));
operands[1] = gen_rtx (CONST_INT, VOIDmode, operands[1] = gen_rtx (CONST_INT, VOIDmode,
CONST_DOUBLE_LOW (operands[1])); CONST_DOUBLE_LOW (operands[1]));
arm_output_asm_insn ("mov\t%0, %1", operands); output_mov_immediate (operands, FALSE, "");
arm_output_asm_insn ("mov\t%0, %1", otherops); output_mov_immediate (otherops, FALSE, "");
} }
else if (code1 == CONST_INT) else if (code1 == CONST_INT)
{ {
otherops[1] = const0_rtx; otherops[1] = const0_rtx;
arm_output_asm_insn ("mov\t%0, %1", operands); /* sign extend the intval into the high-order word */
arm_output_asm_insn ("mov\t%0, %1", otherops); /* Note: output_mov_immediate may clobber operands[1], so we
put this out first */
if (INTVAL (operands[1]) < 0)
arm_output_asm_insn ("mvn\t%0, %1", otherops);
else
arm_output_asm_insn ("mov\t%0, %1", otherops);
output_mov_immediate (operands, FALSE, "");
} }
else if (code1 == MEM) else if (code1 == MEM)
{ {
if (GET_CODE (XEXP (operands[1], 0)) == REG) switch (GET_CODE (XEXP (operands[1], 0)))
{ {
case REG:
/* Handle the simple case where address is [r, #0] more /* Handle the simple case where address is [r, #0] more
efficient. */ efficient. */
operands[1] = XEXP (operands[1], 0); operands[1] = XEXP (operands[1], 0);
arm_output_asm_insn ("ldmia\t%1, %M0", operands); arm_output_asm_insn ("ldmia\t%1, %M0", operands);
} break;
else case PRE_INC:
{ operands[1] = XEXP (XEXP (operands[1], 0), 0);
arm_output_asm_insn ("add\t%1, %1, #8", operands);
arm_output_asm_insn ("ldmia\t%1, %M0", operands);
break;
case PRE_DEC:
operands[1] = XEXP (XEXP (operands[1], 0), 0);
arm_output_asm_insn ("sub\t%1, %1, #8", operands);
arm_output_asm_insn ("ldmia\t%1, %M0", operands);
break;
case POST_INC:
operands[1] = XEXP (XEXP (operands[1], 0), 0);
arm_output_asm_insn ("ldmia\t%1!, %M0", operands);
break;
case POST_DEC:
operands[1] = XEXP (XEXP (operands[1], 0), 0);
arm_output_asm_insn ("ldmia\t%1, %M0", operands);
arm_output_asm_insn ("sub\t%1, %1, #8", operands);
break;
default:
otherops[1] = adj_offsettable_operand (operands[1], 4); otherops[1] = adj_offsettable_operand (operands[1], 4);
/* Take care of overlapping base/data reg. */ /* Take care of overlapping base/data reg. */
if (reg_mentioned_p (operands[0], operands[1])) if (reg_mentioned_p (operands[0], operands[1]))
...@@ -411,14 +1137,32 @@ output_move_double (operands) ...@@ -411,14 +1137,32 @@ output_move_double (operands)
{ {
if (REGNO (operands[1]) == 12) if (REGNO (operands[1]) == 12)
abort(); abort();
switch (GET_CODE (XEXP (operands[0], 0)))
if (GET_CODE (XEXP (operands[0], 0)) == REG) {
{ case REG:
operands[0] = XEXP (operands[0], 0); operands[0] = XEXP (operands[0], 0);
arm_output_asm_insn ("stmia\t%0, %M1", operands); arm_output_asm_insn ("stmia\t%0, %M1", operands);
} break;
else case PRE_INC:
{ operands[0] = XEXP (XEXP (operands[0], 0), 0);
arm_output_asm_insn ("add\t%0, %0, #8", operands);
arm_output_asm_insn ("stmia\t%0, %M1", operands);
break;
case PRE_DEC:
operands[0] = XEXP (XEXP (operands[0], 0), 0);
arm_output_asm_insn ("sub\t%0, %0, #8", operands);
arm_output_asm_insn ("stmia\t%0, %M1", operands);
break;
case POST_INC:
operands[0] = XEXP (XEXP (operands[0], 0), 0);
arm_output_asm_insn ("stmia\t%0!, %M1", operands);
break;
case POST_DEC:
operands[0] = XEXP (XEXP (operands[0], 0), 0);
arm_output_asm_insn ("stmia\t%0, %M1", operands);
arm_output_asm_insn ("sub\t%0, %0, #8", operands);
break;
default:
otherops[0] = adj_offsettable_operand (operands[0], 4); otherops[0] = adj_offsettable_operand (operands[0], 4);
otherops[1] = gen_rtx (REG, SImode, 1 + REGNO (operands[1])); otherops[1] = gen_rtx (REG, SImode, 1 + REGNO (operands[1]));
arm_output_asm_insn ("str\t%1, %0", operands); arm_output_asm_insn ("str\t%1, %0", operands);
...@@ -593,6 +1337,10 @@ shift_instr (op, shift_ptr) ...@@ -593,6 +1337,10 @@ shift_instr (op, shift_ptr)
mnem = "lsr"; mnem = "lsr";
max_shift = 32; max_shift = 32;
break; break;
case MULT:
*shift_ptr = gen_rtx (CONST_INT, VOIDmode,
int_log2 (INTVAL (*shift_ptr)));
return ("asl");
default: default:
abort(); abort();
} }
...@@ -719,6 +1467,21 @@ output_shifted_move (op, operands) ...@@ -719,6 +1467,21 @@ output_shifted_move (op, operands)
return (arm_output_asm_insn (mnemonic, operands)); return (arm_output_asm_insn (mnemonic, operands));
} /* output_shifted_move */ } /* output_shifted_move */
char *
output_shift_compare (operands, neg)
rtx *operands;
int neg;
{
char buf[80];
if (neg)
sprintf (buf, "cmn\t%%1, %%3, %s %%4", shift_instr (GET_CODE (operands[2]),
&operands[4]));
else
sprintf (buf, "cmp\t%%1, %%3, %s %%4", shift_instr (GET_CODE (operands[2]),
&operands[4]));
return arm_output_asm_insn (buf, operands);
} /* output_shift_compare */
/* Output a .ascii pseudo-op, keeping track of lengths. This is because /* Output a .ascii pseudo-op, keeping track of lengths. This is because
/bin/as is horribly restrictive. */ /bin/as is horribly restrictive. */
...@@ -768,31 +1531,211 @@ output_ascii_pseudo_op (stream, p, len) ...@@ -768,31 +1531,211 @@ output_ascii_pseudo_op (stream, p, len)
arm_increase_location (chars_so_far); arm_increase_location (chars_so_far);
} /* output_ascii_pseudo_op */ } /* output_ascii_pseudo_op */
/* Try to determine whether a pattern really clobbers the link register.
This information is useful when peepholing, so that lr need not be pushed
if we combine a call followed by a return */
static int
pattern_really_clobbers_lr (x)
rtx x;
{
int i;
switch (GET_CODE (x))
{
case SET:
switch (GET_CODE (SET_DEST (x)))
{
case REG:
return REGNO (SET_DEST (x)) == 14;
case SUBREG:
if (GET_CODE (XEXP (SET_DEST (x), 0)) == REG)
return REGNO (XEXP (SET_DEST (x), 0)) == 14;
abort ();
default:
return 0;
}
case PARALLEL:
for (i = 0; i < XVECLEN (x, 0); i++)
if (pattern_really_clobbers_lr (XVECEXP (x, 0, i)))
return 1;
return 0;
case CLOBBER:
switch (GET_CODE (XEXP (x, 0)))
{
case REG:
return REGNO (XEXP (x, 0)) == 14;
case SUBREG:
if (GET_CODE (XEXP (XEXP (x, 0), 0)) == REG)
return REGNO (XEXP (XEXP (x, 0), 0)) == 14;
abort ();
default:
return 0;
}
case UNSPEC:
return 1;
default:
return 0;
}
}
static int
function_really_clobbers_lr (first)
rtx first;
{
rtx insn, next;
for (insn = first; insn; insn = next_nonnote_insn (insn))
{
switch (GET_CODE (insn))
{
case BARRIER:
case NOTE:
case CODE_LABEL:
case JUMP_INSN: /* Jump insns only change the PC (and conds) */
case INLINE_HEADER:
break;
case INSN:
if (pattern_really_clobbers_lr (PATTERN (insn)))
return 1;
break;
case CALL_INSN:
/* Don't yet know how to handle those calls that are not to a
SYMBOL_REF */
if (GET_CODE (PATTERN (insn)) != PARALLEL)
abort ();
switch (GET_CODE (XVECEXP (PATTERN (insn), 0, 0)))
{
case CALL:
if (GET_CODE (XEXP (XEXP (XVECEXP (PATTERN (insn), 0, 0), 0), 0))
!= SYMBOL_REF)
return 1;
break;
case SET:
if (GET_CODE (XEXP (XEXP (SET_SRC (XVECEXP (PATTERN (insn),
0, 0)), 0), 0))
!= SYMBOL_REF)
return 1;
break;
default: /* Don't recognize it, be safe */
return 1;
}
/* A call can be made (by peepholing) not to clobber lr iff it is
followed by a return. There may, however, be a use insn iff
we are returning the result of the call.
If we run off the end of the insn chain, then that means the
call was at the end of the function. Unfortunately we don't
have a return insn for the peephole to recognize, so we
must reject this. (Can this be fixed by adding our own insn?) */
if ((next = next_nonnote_insn (insn)) == NULL)
return 1;
if (GET_CODE (next) == INSN && GET_CODE (PATTERN (next)) == USE
&& (GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET)
&& (REGNO (SET_DEST (XVECEXP (PATTERN (insn), 0, 0)))
== REGNO (XEXP (PATTERN (next), 0))))
if ((next = next_nonnote_insn (next)) == NULL)
return 1;
if (GET_CODE (next) == JUMP_INSN
&& GET_CODE (PATTERN (next)) == RETURN)
break;
return 1;
default:
abort ();
}
}
/* We have reached the end of the chain so lr was _not_ clobbered */
return 0;
}
char *
output_return_instruction (operand, really_return)
rtx operand;
int really_return;
{
char instr[100];
int reg, live_regs = 0;
if (current_function_calls_alloca && !really_return)
abort();
for (reg = 4; reg < 10; reg++)
if (regs_ever_live[reg])
live_regs++;
if (live_regs || (regs_ever_live[14] && !lr_save_eliminated))
live_regs++;
if (frame_pointer_needed)
live_regs += 4;
if (live_regs)
{
if (lr_save_eliminated || !regs_ever_live[14])
live_regs++;
if (frame_pointer_needed)
strcpy (instr, "ldm%d0ea\tfp, {");
else
strcpy (instr, "ldm%d0fd\tsp!, {");
for (reg = 4; reg < 10; reg++)
if (regs_ever_live[reg])
{
strcat (instr, reg_names[reg]);
if (--live_regs)
strcat (instr, ", ");
}
if (frame_pointer_needed)
{
strcat (instr, reg_names[11]);
strcat (instr, ", ");
strcat (instr, reg_names[13]);
strcat (instr, ", ");
strcat (instr, really_return ? reg_names[15] : reg_names[14]);
}
else
strcat (instr, really_return ? reg_names[15] : reg_names[14]);
strcat (instr, (TARGET_6 || !really_return) ? "}" : "}^");
arm_output_asm_insn (instr, &operand);
}
else if (really_return)
{
strcpy (instr, TARGET_6 ? "mov%d0\tpc, lr" : "mov%d0s\tpc, lr");
arm_output_asm_insn (instr, &operand);
}
return_used_this_function = 1;
return "";
}
/* The amount of stack adjustment that happens here, in output_return and in
output_epilogue must be exactly the same as was calculated during reload,
or things will point to the wrong place. The only time we can safely
ignore this constraint is when a function has no arguments on the stack,
no stack frame requirement and no live registers execpt for `lr'. If we
can guarantee that by making all function calls into tail calls and that
lr is not clobbered in any other way, then there is no need to push lr
onto the stack. */
void void
output_prologue (f, frame_size) output_prologue (f, frame_size)
FILE *f; FILE *f;
int frame_size; int frame_size;
{ {
int reg, live_regs_mask = 0, code_size = 0; int reg, live_regs_mask = 0, code_size = 0;
rtx operands[3]; rtx operands[3];
/* Nonzero if the `fp' (argument pointer) register is needed. */
int fp_needed = 0;
/* Nonzero if we must stuff some register arguments onto the stack as if /* Nonzero if we must stuff some register arguments onto the stack as if
they were passed there. */ they were passed there. */
int store_arg_regs = 0; int store_arg_regs = 0;
return_used_this_function = 0;
lr_save_eliminated = 0;
fprintf (f, "\t@ args = %d, pretend = %d, frame = %d\n", fprintf (f, "\t@ args = %d, pretend = %d, frame = %d\n",
current_function_args_size, current_function_pretend_args_size, frame_size); current_function_args_size, current_function_pretend_args_size,
fprintf (f, "\t@ frame_pointer_needed = %d, current_function_anonymous_args = %d\n", frame_size);
fprintf (f, "\t@ frame_needed = %d, current_function_anonymous_args = %d\n",
frame_pointer_needed, current_function_anonymous_args); frame_pointer_needed, current_function_anonymous_args);
if (current_function_pretend_args_size || current_function_args_size
|| frame_pointer_needed || current_function_anonymous_args || TARGET_APCS)
fp_needed = 1;
if (current_function_anonymous_args && current_function_pretend_args_size) if (current_function_anonymous_args && current_function_pretend_args_size)
store_arg_regs = 1; store_arg_regs = 1;
...@@ -800,18 +1743,23 @@ output_prologue (f, frame_size) ...@@ -800,18 +1743,23 @@ output_prologue (f, frame_size)
if (regs_ever_live[reg]) if (regs_ever_live[reg])
live_regs_mask |= (1 << reg); live_regs_mask |= (1 << reg);
if (fp_needed) if (frame_pointer_needed)
{ {
live_regs_mask |= 0xD800; live_regs_mask |= 0xD800;
/* The following statement is probably redundant now
because the frame pointer is recorded in regs_ever_live. */
if (frame_pointer_needed)
live_regs_mask |= (1 << FRAME_POINTER_REGNUM);
fputs ("\tmov\tip, sp\n", f); fputs ("\tmov\tip, sp\n", f);
code_size += 4; code_size += 4;
} }
else if (regs_ever_live[14]) else if (regs_ever_live[14])
live_regs_mask |= 0x4000; {
if (! current_function_args_size
&& !function_really_clobbers_lr (get_insns ()))
{
fprintf (f,"\t@ I don't think this function clobbers lr\n");
lr_save_eliminated = 1;
}
else
live_regs_mask |= 0x4000;
}
/* If CURRENT_FUNCTION_PRETEND_ARGS_SIZE, adjust the stack pointer to make /* If CURRENT_FUNCTION_PRETEND_ARGS_SIZE, adjust the stack pointer to make
room. If also STORE_ARG_REGS store the argument registers involved in room. If also STORE_ARG_REGS store the argument registers involved in
...@@ -827,6 +1775,7 @@ output_prologue (f, frame_size) ...@@ -827,6 +1775,7 @@ output_prologue (f, frame_size)
arg_size > 0; reg--, arg_size -= 4) arg_size > 0; reg--, arg_size -= 4)
mask |= (1 << reg); mask |= (1 << reg);
print_multi_reg (f, "stmfd\tsp!", mask, FALSE); print_multi_reg (f, "stmfd\tsp!", mask, FALSE);
code_size += 4;
} }
else else
{ {
...@@ -839,6 +1788,13 @@ output_prologue (f, frame_size) ...@@ -839,6 +1788,13 @@ output_prologue (f, frame_size)
if (live_regs_mask) if (live_regs_mask)
{ {
/* if a di mode load/store multiple is used, and the base register
is r3, then r4 can become an ever live register without lr
doing so, in this case we need to push lr as well, or we
will fail to get a proper return. */
live_regs_mask |= 0x4000;
lr_save_eliminated = 0;
print_multi_reg (f, "stmfd\tsp!", live_regs_mask, FALSE); print_multi_reg (f, "stmfd\tsp!", live_regs_mask, FALSE);
code_size += 4; code_size += 4;
} }
...@@ -850,23 +1806,17 @@ output_prologue (f, frame_size) ...@@ -850,23 +1806,17 @@ output_prologue (f, frame_size)
code_size += 4; code_size += 4;
} }
if (fp_needed) if (frame_pointer_needed)
{ {
/* Make `fp' point to saved value of `pc'. */ /* Make `fp' point to saved value of `pc'. */
operands[0] = arg_pointer_rtx; operands[0] = gen_rtx (REG, SImode, HARD_FRAME_POINTER_REGNUM);
operands[1] = gen_rtx (REG, SImode, 12); operands[1] = gen_rtx (REG, SImode, 12);
operands[2] = gen_rtx (CONST_INT, VOIDmode, operands[2] = gen_rtx (CONST_INT, VOIDmode,
- (4 + current_function_pretend_args_size)); - (4 + current_function_pretend_args_size));
output_add_immediate (operands); output_add_immediate (operands);
} }
if (frame_pointer_needed)
{
fprintf (f, "\tmov\trfp, sp\n");
code_size += 4;
}
if (frame_size) if (frame_size)
{ {
operands[0] = operands[1] = stack_pointer_rtx; operands[0] = operands[1] = stack_pointer_rtx;
...@@ -884,36 +1834,41 @@ output_epilogue (f, frame_size) ...@@ -884,36 +1834,41 @@ output_epilogue (f, frame_size)
int frame_size; int frame_size;
{ {
int reg, live_regs_mask = 0, code_size = 0, fp_needed = 0; int reg, live_regs_mask = 0, code_size = 0, fp_needed = 0;
/* If we need this then it will always be at lesat this much */
int floats_offset = 24;
rtx operands[3]; rtx operands[3];
if (current_function_pretend_args_size || current_function_args_size if (use_return_insn() && return_used_this_function)
|| frame_pointer_needed || current_function_anonymous_args || TARGET_APCS)
fp_needed = 1;
for (reg = 4; reg < 10; reg++)
if (regs_ever_live[reg])
live_regs_mask |= (1 << reg);
if (fp_needed)
{ {
live_regs_mask |= 0xA800; if (frame_size && !(frame_pointer_needed || TARGET_APCS))
if (frame_pointer_needed) {
live_regs_mask |= (1 << FRAME_POINTER_REGNUM); abort ();
}
return;
} }
else if (regs_ever_live[14])
live_regs_mask |= 0x4000;
for (reg = 20; reg < 24; reg++) for (reg = 4; reg <= 10; reg++)
if (regs_ever_live[reg]) if (regs_ever_live[reg])
{ {
fprintf (f, "\tldfe\t%s, [%s], #12\n", reg_names[reg], live_regs_mask |= (1 << reg);
frame_pointer_needed ? "rfp" : "sp"); floats_offset += 4;
code_size += 4;
} }
if (fp_needed)
if (frame_pointer_needed)
{ {
print_multi_reg (f, "ldmea\tfp", live_regs_mask, TRUE); for (reg = 23; reg >= 20; reg--)
if (regs_ever_live[reg])
{
fprintf (f, "\tldfe\t%s, [fp, #-%d]\n", reg_names[reg],
floats_offset);
floats_offset += 12;
code_size += 4;
}
live_regs_mask |= 0xA800;
print_multi_reg (f, "ldmea\tfp", live_regs_mask,
TARGET_6 ? FALSE : TRUE);
code_size += 4; code_size += 4;
} }
else else
...@@ -926,16 +1881,23 @@ output_epilogue (f, frame_size) ...@@ -926,16 +1881,23 @@ output_epilogue (f, frame_size)
output_add_immediate (operands); output_add_immediate (operands);
} }
for (reg = 20; reg < 24; reg++)
if (regs_ever_live[reg])
{
fprintf (f, "\tldfe\t%s, [sp], #12\n", reg_names[reg]);
code_size += 4;
}
if (current_function_pretend_args_size == 0 && regs_ever_live[14]) if (current_function_pretend_args_size == 0 && regs_ever_live[14])
{ {
print_multi_reg (f, "ldmfd\tsp!", print_multi_reg (f, "ldmfd\tsp!", live_regs_mask | 0x8000,
(live_regs_mask & ~0x4000) | 0x8000, TRUE); TARGET_6 ? FALSE : TRUE);
code_size += 4; code_size += 4;
} }
else else
{ {
if (live_regs_mask) if (live_regs_mask || regs_ever_live[14])
{ {
live_regs_mask |= 0x4000;
print_multi_reg (f, "ldmfd\tsp!", live_regs_mask, FALSE); print_multi_reg (f, "ldmfd\tsp!", live_regs_mask, FALSE);
code_size += 4; code_size += 4;
} }
...@@ -946,7 +1908,7 @@ output_epilogue (f, frame_size) ...@@ -946,7 +1908,7 @@ output_epilogue (f, frame_size)
current_function_pretend_args_size); current_function_pretend_args_size);
output_add_immediate (operands); output_add_immediate (operands);
} }
fputs ("\tmovs\tpc, lr\n", f); fputs (TARGET_6 ? "\tmov\tpc, lr\n" : "\tmovs\tpc, lr\n", f);
code_size += 4; code_size += 4;
} }
} }
...@@ -1086,6 +2048,72 @@ arm_output_llc (operands) ...@@ -1086,6 +2048,72 @@ arm_output_llc (operands)
return (""); return ("");
} /* arm_output_llc */ } /* arm_output_llc */
/* output_load_symbol ()
load a symbol that is known to be in the text segment into a register */
char *
output_load_symbol (operands)
rtx *operands;
{
char *s, *name = XSTR (operands[1], 0);
struct label_offset *he;
int hash = 0;
int offset;
if (*name != '*')
abort ();
for (s = &name[1]; *s; s++)
hash += *s;
hash = hash % LABEL_HASH_SIZE;
he = offset_table[hash];
while (he && strcmp (he->name, &name[1]))
he = he->cdr;
if (!he)
abort ();
offset = (arm_text_location + 8 - he->offset);
if (offset < 0)
abort ();
/* If the symbol is word aligned then we might be able to reduce the
number of loads */
if ((offset & 3) == 0)
{
arm_output_asm_insn ("sub\t%0, pc, #(8 + . -%a1) & 1023", operands);
if (offset > 0x3ff)
{
arm_output_asm_insn ("sub\t%0, %0, #(4 + . -%a1) & 261120",
operands);
if (offset > 0x3ffff)
{
arm_output_asm_insn ("sub\t%0, %0, #(. -%a1) & 66846720",
operands);
if (offset > 0x3ffffff)
arm_output_asm_insn ("sub\t%0, %0, #(. - 4 -%a1) & -67108864",
operands);
}
}
}
else
{
arm_output_asm_insn ("sub\t%0, pc, #(8 + . -%a1) & 255", operands);
if (offset > 0x0ff)
{
arm_output_asm_insn ("sub\t%0, %0, #(4 + . -%a1) & 65280", operands);
if (offset > 0x0ffff)
{
arm_output_asm_insn ("sub\t%0, %0, #(. -%a1) & 16711680",
operands);
if (offset > 0x0ffffff)
arm_output_asm_insn ("sub\t%0, %0, #(. - 4 -%a1) & -16777216",
operands);
}
}
}
return "";
}
/* Output code resembling an .lcomm directive. /bin/as doesn't have this /* Output code resembling an .lcomm directive. /bin/as doesn't have this
directive hence this hack, which works by reserving some `.space' in the directive hence this hack, which works by reserving some `.space' in the
...@@ -1131,6 +2159,10 @@ output_lcomm_directive (stream, name, size, rounded) ...@@ -1131,6 +2159,10 @@ output_lcomm_directive (stream, name, size, rounded)
4 -> 0 final_prescan_insn if the `target' unconditional branch is reached 4 -> 0 final_prescan_insn if the `target' unconditional branch is reached
(the target insn is arm_target_insn). (the target insn is arm_target_insn).
If the jump clobbers the conditions then we use states 2 and 4.
A similar thing can be done with conditional return insns.
XXX In case the `target' is an unconditional branch, this conditionalising XXX In case the `target' is an unconditional branch, this conditionalising
of the instructions always reduces code size, but not always execution of the instructions always reduces code size, but not always execution
time. But then, I want to reduce the code size to somewhere near what time. But then, I want to reduce the code size to somewhere near what
...@@ -1185,6 +2217,15 @@ final_prescan_insn (insn, opvec, noperands) ...@@ -1185,6 +2217,15 @@ final_prescan_insn (insn, opvec, noperands)
reversed if it appears to fail. */ reversed if it appears to fail. */
int reverse = 0; int reverse = 0;
/* JUMP_CLOBBERS will be one implies that the conditions if a branch is
taken are clobbered, even if the rtl suggests otherwise. It also
means that we have to grub around within the jump expression to find
out what the conditions are when the jump isn't taken. */
int jump_clobbers = 0;
/* If we start with a return insn, we only succeed if we find another one. */
int seeking_return = 0;
/* START_INSN will hold the insn from where we start looking. This is the /* START_INSN will hold the insn from where we start looking. This is the
first insn after the following code_label if REVERSE is true. */ first insn after the following code_label if REVERSE is true. */
rtx start_insn = insn; rtx start_insn = insn;
...@@ -1219,6 +2260,21 @@ final_prescan_insn (insn, opvec, noperands) ...@@ -1219,6 +2260,21 @@ final_prescan_insn (insn, opvec, noperands)
else else
return; return;
} }
else if (GET_CODE (body) == RETURN)
{
start_insn = next_nonnote_insn (start_insn);
if (GET_CODE (start_insn) == BARRIER)
start_insn = next_nonnote_insn (start_insn);
if (GET_CODE (start_insn) == CODE_LABEL
&& CODE_LABEL_NUMBER (start_insn) == arm_target_label
&& LABEL_NUSES (start_insn) == 1)
{
reverse = TRUE;
seeking_return = 1;
}
else
return;
}
else else
return; return;
} }
...@@ -1228,6 +2284,20 @@ final_prescan_insn (insn, opvec, noperands) ...@@ -1228,6 +2284,20 @@ final_prescan_insn (insn, opvec, noperands)
if (GET_CODE (insn) != JUMP_INSN) if (GET_CODE (insn) != JUMP_INSN)
return; return;
/* This jump might be paralled with a clobber of the condition codes
the jump should always come first */
if (GET_CODE (body) == PARALLEL && XVECLEN (body, 0) > 0)
body = XVECEXP (body, 0, 0);
#if 0
/* If this is a conditional return then we don't want to know */
if (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC
&& GET_CODE (SET_SRC (body)) == IF_THEN_ELSE
&& (GET_CODE (XEXP (SET_SRC (body), 1)) == RETURN
|| GET_CODE (XEXP (SET_SRC (body), 2)) == RETURN))
return;
#endif
if (reverse if (reverse
|| (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC || (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC
&& GET_CODE (SET_SRC (body)) == IF_THEN_ELSE)) && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE))
...@@ -1235,11 +2305,17 @@ final_prescan_insn (insn, opvec, noperands) ...@@ -1235,11 +2305,17 @@ final_prescan_insn (insn, opvec, noperands)
int insns_skipped = 0, fail = FALSE, succeed = FALSE; int insns_skipped = 0, fail = FALSE, succeed = FALSE;
/* Flag which part of the IF_THEN_ELSE is the LABEL_REF. */ /* Flag which part of the IF_THEN_ELSE is the LABEL_REF. */
int then_not_else = TRUE; int then_not_else = TRUE;
rtx this_insn = start_insn, label; rtx this_insn = start_insn, label = 0;
if (get_attr_conds (insn) == CONDS_JUMP_CLOB)
jump_clobbers = 1;
/* Register the insn jumped to. */ /* Register the insn jumped to. */
if (reverse) if (reverse)
label = XEXP (SET_SRC (body), 0); {
if (!seeking_return)
label = XEXP (SET_SRC (body), 0);
}
else if (GET_CODE (XEXP (SET_SRC (body), 1)) == LABEL_REF) else if (GET_CODE (XEXP (SET_SRC (body), 1)) == LABEL_REF)
label = XEXP (XEXP (SET_SRC (body), 1), 0); label = XEXP (XEXP (SET_SRC (body), 1), 0);
else if (GET_CODE (XEXP (SET_SRC (body), 2)) == LABEL_REF) else if (GET_CODE (XEXP (SET_SRC (body), 2)) == LABEL_REF)
...@@ -1247,6 +2323,13 @@ final_prescan_insn (insn, opvec, noperands) ...@@ -1247,6 +2323,13 @@ final_prescan_insn (insn, opvec, noperands)
label = XEXP (XEXP (SET_SRC (body), 2), 0); label = XEXP (XEXP (SET_SRC (body), 2), 0);
then_not_else = FALSE; then_not_else = FALSE;
} }
else if (GET_CODE (XEXP (SET_SRC (body), 1)) == RETURN)
seeking_return = 1;
else if (GET_CODE (XEXP (SET_SRC (body), 2)) == RETURN)
{
seeking_return = 1;
then_not_else = FALSE;
}
else else
abort (); abort ();
...@@ -1272,33 +2355,54 @@ final_prescan_insn (insn, opvec, noperands) ...@@ -1272,33 +2355,54 @@ final_prescan_insn (insn, opvec, noperands)
control falls in from somewhere else. */ control falls in from somewhere else. */
if (this_insn == label) if (this_insn == label)
{ {
arm_ccfsm_state = 1; if (jump_clobbers)
{
arm_ccfsm_state = 2;
this_insn = next_nonnote_insn (this_insn);
}
else
arm_ccfsm_state = 1;
succeed = TRUE; succeed = TRUE;
} }
else else
fail = TRUE; fail = TRUE;
break; break;
case BARRIER: /* XXX Is this case necessary? */ case BARRIER:
/* Succeed if the following insn is the target label. /* Succeed if the following insn is the target label.
Otherwise fail. */ Otherwise fail.
If return insns are used then the last insn in a function
will be a barrier. */
this_insn = next_nonnote_insn (this_insn); this_insn = next_nonnote_insn (this_insn);
if (this_insn == label) if (this_insn && this_insn == label)
{ {
arm_ccfsm_state = 1; if (jump_clobbers)
{
arm_ccfsm_state = 2;
this_insn = next_nonnote_insn (this_insn);
}
else
arm_ccfsm_state = 1;
succeed = TRUE; succeed = TRUE;
} }
else else
fail = TRUE; fail = TRUE;
break; break;
case CALL_INSN:
/* The arm 6xx uses full 32 bit addresses so the cc is not
preserved over calls */
if (TARGET_6)
fail = TRUE;
break;
case JUMP_INSN: case JUMP_INSN:
/* If this is an unconditional branch to the same label, succeed. /* If this is an unconditional branch to the same label, succeed.
If it is to another label, do nothing. If it is conditional, If it is to another label, do nothing. If it is conditional,
fail. */ fail. */
/* XXX Probably, the test for the SET and the PC are unnecessary. */ /* XXX Probably, the test for the SET and the PC are unnecessary. */
if (GET_CODE (scanbody) == SET && GET_CODE (SET_DEST (scanbody)) == PC) if (GET_CODE (scanbody) == SET
&& GET_CODE (SET_DEST (scanbody)) == PC)
{ {
if (GET_CODE (SET_SRC (scanbody)) == LABEL_REF if (GET_CODE (SET_SRC (scanbody)) == LABEL_REF
&& XEXP (SET_SRC (scanbody), 0) == label && !reverse) && XEXP (SET_SRC (scanbody), 0) == label && !reverse)
...@@ -1309,11 +2413,31 @@ final_prescan_insn (insn, opvec, noperands) ...@@ -1309,11 +2413,31 @@ final_prescan_insn (insn, opvec, noperands)
else if (GET_CODE (SET_SRC (scanbody)) == IF_THEN_ELSE) else if (GET_CODE (SET_SRC (scanbody)) == IF_THEN_ELSE)
fail = TRUE; fail = TRUE;
} }
else if (GET_CODE (scanbody) == RETURN
&& seeking_return)
{
arm_ccfsm_state = 2;
succeed = TRUE;
}
else if (GET_CODE (scanbody) == PARALLEL)
{
switch (get_attr_conds (this_insn))
{
case CONDS_NOCOND:
break;
default:
fail = TRUE;
break;
}
}
break; break;
case INSN: case INSN:
/* Instructions affecting the condition codes make it fail. */ /* Instructions using or affecting the condition codes make it
if (sets_cc0_p (scanbody)) fail. */
if ((GET_CODE (scanbody) == SET
|| GET_CODE (scanbody) == PARALLEL)
&& get_attr_conds (this_insn) != CONDS_NOCOND)
fail = TRUE; fail = TRUE;
break; break;
...@@ -1323,20 +2447,57 @@ final_prescan_insn (insn, opvec, noperands) ...@@ -1323,20 +2447,57 @@ final_prescan_insn (insn, opvec, noperands)
} }
if (succeed) if (succeed)
{ {
if (arm_ccfsm_state == 1 || reverse) if ((!seeking_return) && (arm_ccfsm_state == 1 || reverse))
arm_target_label = CODE_LABEL_NUMBER (label); arm_target_label = CODE_LABEL_NUMBER (label);
else if (arm_ccfsm_state == 2) else if (seeking_return || arm_ccfsm_state == 2)
arm_target_insn = this_insn; {
while (this_insn && GET_CODE (PATTERN (this_insn)) == USE)
{
this_insn = next_nonnote_insn (this_insn);
if (this_insn && (GET_CODE (this_insn) == BARRIER
|| GET_CODE (this_insn) == CODE_LABEL))
abort ();
}
if (!this_insn)
{
/* Oh, dear! we ran off the end.. give up */
recog (PATTERN (insn), insn, NULL_PTR);
arm_ccfsm_state = 0;
return;
}
arm_target_insn = this_insn;
}
else else
abort (); abort ();
if (jump_clobbers)
{
if (reverse)
abort ();
arm_current_cc =
get_arm_condition_code (XEXP (XEXP (XEXP (SET_SRC (body),
0), 0), 1));
if (GET_CODE (XEXP (XEXP (SET_SRC (body), 0), 0)) == AND)
arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc);
if (GET_CODE (XEXP (SET_SRC (body), 0)) == NE)
arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc);
}
else
{
/* If REVERSE is true, ARM_CURRENT_CC needs to be inverted from
what it was. */
if (!reverse)
arm_current_cc = get_arm_condition_code (XEXP (SET_SRC (body),
0));
}
/* If REVERSE is true, ARM_CURRENT_CC needs to be inverted from what
it was. */
if (!reverse)
arm_current_cc = get_arm_condition_code (XEXP (SET_SRC (body), 0));
if (reverse || then_not_else) if (reverse || then_not_else)
arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc); arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc);
} }
/* restore recog_operand (getting the attributes of other insns can
destroy this array, but final.c assumes that it remains intact
accross this call; since the insn has been recognized already we
call recog direct). */
recog (PATTERN (insn), insn, NULL_PTR);
} }
} /* final_prescan_insn */ } /* final_prescan_insn */
......
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
Copyright (C) 1991, 1993 Free Software Foundation, Inc. Copyright (C) 1991, 1993 Free Software Foundation, Inc.
Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl) Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
and Martin Simmons (@harleqn.co.uk). and Martin Simmons (@harleqn.co.uk).
More major hacks by Richard Earnshaw (rwe11@cl.cam.ac.uk)
This file is part of GNU CC. This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify GNU CC is free software; you can redistribute it and/or modify
...@@ -27,30 +28,45 @@ extern void output_prologue (); ...@@ -27,30 +28,45 @@ extern void output_prologue ();
extern void output_epilogue (); extern void output_epilogue ();
extern char *arm_output_asm_insn (); extern char *arm_output_asm_insn ();
extern char *arm_output_llc (); extern char *arm_output_llc ();
extern char *arithmetic_instr ();
extern char *output_add_immediate (); extern char *output_add_immediate ();
extern char *output_call (); extern char *output_call ();
extern char *output_call_mem ();
extern char *output_move_double (); extern char *output_move_double ();
extern char *output_mov_double_fpu_from_arm (); extern char *output_mov_double_fpu_from_arm ();
extern char *output_mov_double_arm_from_fpu (); extern char *output_mov_double_arm_from_fpu ();
extern char *output_mov_immediate (); extern char *output_mov_immediate ();
extern char *output_multi_immediate (); extern char *output_multi_immediate ();
extern char *output_shifted_move (); extern char *output_shifted_move ();
extern char *output_shift_compare ();
extern char *output_arithmetic_with_immediate_multiply (); extern char *output_arithmetic_with_immediate_multiply ();
extern char *output_arithmetic_with_shift ();
extern char *output_return_instruction ();
extern char *output_load_symbol ();
extern char *fp_immediate_constant ();
extern struct rtx_def *gen_compare_reg ();
extern struct rtx_def *arm_gen_store_multiple ();
extern struct rtx_def *arm_gen_load_multiple ();
extern char *arm_condition_codes[];
/* This is needed by the tail-calling peepholes */
extern int frame_pointer_needed;
/* Translation to find startup files. On RISCiX boxes, gcrt0.o is in #ifndef CPP_PREDEFINES
/usr/lib. */ #define CPP_PREDEFINES "-Darm -Acpu(arm) -Amachine(arm)"
#define STARTFILE_SPEC \ #endif
"%{pg:/usr/lib/gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt0.o%s}}"
#ifdef riscos #ifndef CPP_SPEC
#define CPP_PREDEFINES "-Darm -Driscos -Acpu(arm) -Amachine(arm)" #define CPP_SPEC "%{m6:-D__arm6__}"
#else
#define CPP_PREDEFINES "-Darm -Driscix -Dunix -Asystem(unix) -Acpu(arm) -Amachine(arm)"
#endif #endif
/* Run-time Target Specification. */ /* Run-time Target Specification. */
#ifndef TARGET_VERSION
#define TARGET_VERSION \ #define TARGET_VERSION \
fputs (" (ARM/RISCiX)", stderr); fputs (" (ARM/generic)", stderr);
#endif
/* Run-time compilation parameters selecting different hardware subsets. /* Run-time compilation parameters selecting different hardware subsets.
On the ARM, misuse it in a different way. */ On the ARM, misuse it in a different way. */
...@@ -70,14 +86,50 @@ extern int target_flags; ...@@ -70,14 +86,50 @@ extern int target_flags;
case instruction scheduling becomes very uninteresting. */ case instruction scheduling becomes very uninteresting. */
#define TARGET_FPE (target_flags & 4) #define TARGET_FPE (target_flags & 4)
/* Nonzero if destined for an ARM6xx. Takes out bits that assume restoration
of condition flags when returning from a branch & link (ie. a function) */
#define TARGET_6 (target_flags & 8)
/* ARM_EXTRA_TARGET_SWITCHES is used in riscix.h to define some options which
are passed to the preprocessor and the assembler post-processor. They
aren't needed in the main pass of the compiler, but if we don't define
them in target switches cc1 complains about them. For the sake of
argument lets allocate bit 31 of target flags for such options. */
#ifndef ARM_EXTRA_TARGET_SWITCHES
#define ARM_EXTRA_TARGET_SWITCHES
#endif
#define TARGET_SWITCHES \ #define TARGET_SWITCHES \
{ \ { \
{"apcs", 1}, \ {"apcs", 1}, \
{"poke-function-name", 2}, \ {"poke-function-name", 2}, \
{"fpe", 4}, \ {"fpe", 4}, \
{"6", 8}, \
{"2", -8}, \
{"3", -8}, \
ARM_EXTRA_TARGET_SWITCHES \
{"", TARGET_DEFAULT } \ {"", TARGET_DEFAULT } \
} }
/* Which processor we are running on. Currently this is only used to
get the condition code clobbering attribute right when we are running on
an arm 6 */
enum processor_type
{
PROCESSOR_ARM2,
PROCESSOR_ARM3,
PROCESSOR_ARM6
};
/* Recast the cpu class to be the cpu attribute. */
/* Recast the cpu class to be the cpu attribute. */
#define arm_cpu_attr ((enum attr_cpu)arm_cpu)
extern enum processor_type arm_cpu;
#define TARGET_DEFAULT 0 #define TARGET_DEFAULT 0
#define TARGET_MEM_FUNCTIONS 1 #define TARGET_MEM_FUNCTIONS 1
...@@ -88,33 +140,77 @@ extern int target_flags; ...@@ -88,33 +140,77 @@ extern int target_flags;
- if floating point is done by emulation, forget about instruction - if floating point is done by emulation, forget about instruction
scheduling. Note that this only saves compilation time; it doesn't scheduling. Note that this only saves compilation time; it doesn't
matter for the final code. */ matter for the final code. */
#ifdef riscos #ifndef TARGET_WHEN_DEBUGGING
#define TARGET_WHEN_DEBUGGING 3
#else
#define TARGET_WHEN_DEBUGGING 1 #define TARGET_WHEN_DEBUGGING 1
#endif #endif
#define OVERRIDE_OPTIONS \ #define OVERRIDE_OPTIONS \
{ \ { \
if (write_symbols != NO_DEBUG) \ if (write_symbols != NO_DEBUG && flag_omit_frame_pointer) \
target_flags |= TARGET_WHEN_DEBUGGING; \ warning ("-g without a frame pointer may not give sensible debugging");\
else if (TARGET_POKE_FUNCTION_NAME) \ if (TARGET_POKE_FUNCTION_NAME) \
target_flags |= 1; \ target_flags |= 1; \
if (TARGET_FPE) \ if (TARGET_FPE) \
flag_schedule_insns = flag_schedule_insns_after_reload = 0; \ flag_schedule_insns = flag_schedule_insns_after_reload = 0; \
arm_cpu = TARGET_6 ? PROCESSOR_ARM6: PROCESSOR_ARM2; \
} }
/* Omitting the frame pointer is a very good idea on the ARM, especially if /* Omitting the frame pointer is a very good idea on the ARM, especially if
not TARGET_APCS, in which case all that pushing on function entry isn't not TARGET_APCS, in which case all that pushing on function entry isn't
mandatory anymore. */ mandatory anymore.
Forcing loads to be explicit also allows cse to work better */
#define OPTIMIZATION_OPTIONS(OPTIMIZE) \ #define OPTIMIZATION_OPTIONS(OPTIMIZE) \
{ \ { \
if (OPTIMIZE) \ if (OPTIMIZE) \
flag_omit_frame_pointer = 1; \ { \
flag_force_mem = 1; \
flag_omit_frame_pointer = 1; \
} \
} }
/* Target machine storage Layout. */ /* Target machine storage Layout. */
/* Define this macro if it is advisable to hold scalars in registers
in a wider mode than that declared by the program. In such cases,
the value is constrained to be within the bounds of the declared
type, but kept valid in the wider mode. The signedness of the
extension may differ from that of the type. */
/* It is far faster to zero extend chars than to sign extend them */
#define PROMOTE_MODE(MODE,UNSIGNEDP,TYPE) \
if (GET_MODE_CLASS (MODE) == MODE_INT \
&& GET_MODE_SIZE (MODE) < 4) \
{ \
if (MODE == QImode) \
UNSIGNEDP = 1; \
(MODE) = SImode; \
}
/* Define for XFmode extended real floating point support.
This will automatically cause REAL_ARITHMETIC to be defined. */
/* For the ARM:
I think I have added all the code to make this work. Unfortunately,
early releases of the floating point emulation code on RISCiX used a
different format for extended precision numbers. On my RISCiX box there
is a bug somewhere which causes the machine to lock up when running enquire
with long doubles. There is the additional aspect that Norcroft C
treats long doubles as doubles and we ought to remain compatible.
Perhaps someone with an FPA coprocessor and not running RISCiX would like
to try this someday. */
/* #define LONG_DOUBLE_TYPE_SIZE 96 */
/* Disable XFmode patterns in md file */
#define ENABLE_XF_PATTERNS 0
/* Define if you don't want extended real, but do want to use the
software floating point emulator for REAL_ARITHMETIC and
decimal <-> binary conversion. */
/* See comment above */
#define REAL_ARITHMETIC
/* Define this if most significant bit is lowest numbered /* Define this if most significant bit is lowest numbered
in instructions that operate on numbered bit-fields. */ in instructions that operate on numbered bit-fields. */
#define BITS_BIG_ENDIAN 0 #define BITS_BIG_ENDIAN 0
...@@ -145,11 +241,20 @@ extern int target_flags; ...@@ -145,11 +241,20 @@ extern int target_flags;
#define BIGGEST_ALIGNMENT 32 #define BIGGEST_ALIGNMENT 32
/* Make strings word-aligned so strcpy from constants will be faster. */
#define CONSTANT_ALIGNMENT(EXP, ALIGN) \
(TREE_CODE (EXP) == STRING_CST \
&& (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN))
/* Every structures size must be a multiple of 32 bits. */ /* Every structures size must be a multiple of 32 bits. */
#define STRUCTURE_SIZE_BOUNDARY 32 #define STRUCTURE_SIZE_BOUNDARY 32
/* Non-zero if move instructions will actually fail to work
when given unaligned data. */
#define STRICT_ALIGNMENT 1 #define STRICT_ALIGNMENT 1
#define TARGET_FLOAT_FORMAT IEEE_FLOAT_FORMAT
/* Define number of bits in most basic integer type. /* Define number of bits in most basic integer type.
(If undefined, default is BITS_PER_WORD). */ (If undefined, default is BITS_PER_WORD). */
/* #define INT_TYPE_SIZE */ /* #define INT_TYPE_SIZE */
...@@ -177,10 +282,43 @@ extern int target_flags; ...@@ -177,10 +282,43 @@ extern int target_flags;
f4-f7 S floating point variable f4-f7 S floating point variable
cc This is NOT a real register, but is used internally
to represent things that use or set the condition
codes.
sfp This isn't either. It is used during rtl generation
since the offset between the frame pointer and the
auto's isn't known until after register allocation.
afp Nor this, we only need this because of non-local
goto. Without it fp appears to be used and the
elimination code won't get rid of sfp. It tracks
fp exactly at all times.
*: See CONDITIONAL_REGISTER_USAGE */ *: See CONDITIONAL_REGISTER_USAGE */
/* The number of hard registers is 16 ARM + 8 FPU. */ /* The stack backtrace structure is as follows:
#define FIRST_PSEUDO_REGISTER 24 fp points to here: | save code pointer | [fp]
| return link value | [fp, #-4]
| return sp value | [fp, #-8]
| return fp value | [fp, #-12]
[| saved r10 value |]
[| saved r9 value |]
[| saved r8 value |]
[| saved r7 value |]
[| saved r6 value |]
[| saved r5 value |]
[| saved r4 value |]
[| saved r3 value |]
[| saved r2 value |]
[| saved r1 value |]
[| saved r0 value |]
[| saved f7 value |] three words
[| saved f6 value |] three words
[| saved f5 value |] three words
[| saved f4 value |] three words
r0-r3 are not normally saved in a C function. */
/* The number of hard registers is 16 ARM + 8 FPU + 1 CC + 1 SFP. */
#define FIRST_PSEUDO_REGISTER 27
/* 1 for registers that have pervasive standard uses /* 1 for registers that have pervasive standard uses
and are not available for the register allocator. */ and are not available for the register allocator. */
...@@ -188,7 +326,8 @@ extern int target_flags; ...@@ -188,7 +326,8 @@ extern int target_flags;
{ \ { \
0,0,0,0,0,0,0,0, \ 0,0,0,0,0,0,0,0, \
0,0,1,1,0,1,0,1, \ 0,0,1,1,0,1,0,1, \
0,0,0,0,0,0,0,0 \ 0,0,0,0,0,0,0,0, \
1,1,1 \
} }
/* 1 for registers not available across function calls. /* 1 for registers not available across function calls.
...@@ -196,12 +335,15 @@ extern int target_flags; ...@@ -196,12 +335,15 @@ extern int target_flags;
registers that can be used without being saved. registers that can be used without being saved.
The latter must include the registers where values are returned The latter must include the registers where values are returned
and the register where structure-value addresses are passed. and the register where structure-value addresses are passed.
Aside from that, you can include as many other registers as you like. */ Aside from that, you can include as many other registers as you like.
The CC is not preserved over function calls on the ARM 6, so it is
easier to assume this for all. SFP is preserved, since FP is. */
#define CALL_USED_REGISTERS \ #define CALL_USED_REGISTERS \
{ \ { \
1,1,1,1,0,0,0,0, \ 1,1,1,1,0,0,0,0, \
0,0,1,1,1,1,1,1, \ 0,0,1,1,1,1,1,1, \
1,1,1,1,0,0,0,0 \ 1,1,1,1,0,0,0,0, \
1,1,1 \
} }
/* If doing stupid life analysis, avoid a bug causing a return value r0 to be /* If doing stupid life analysis, avoid a bug causing a return value r0 to be
...@@ -221,15 +363,19 @@ extern int target_flags; ...@@ -221,15 +363,19 @@ extern int target_flags;
On the ARM regs are UNITS_PER_WORD bits wide; FPU regs can hold any FP On the ARM regs are UNITS_PER_WORD bits wide; FPU regs can hold any FP
mode. */ mode. */
#define HARD_REGNO_NREGS(REGNO, MODE) \ #define HARD_REGNO_NREGS(REGNO, MODE) \
((REGNO) >= 16 ? 1 \ (((REGNO) >= 16 && REGNO != FRAME_POINTER_REGNUM \
&& (REGNO) != ARG_POINTER_REGNUM) ? 1 \
: ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)) : ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. /* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
This is TRUE for ARM regs since they can hold anything, and TRUE for FPU This is TRUE for ARM regs since they can hold anything, and TRUE for FPU
regs holding FP. */ regs holding FP. */
#define HARD_REGNO_MODE_OK(REGNO, MODE) \ #define HARD_REGNO_MODE_OK(REGNO, MODE) \
((REGNO) < 16 || GET_MODE_CLASS (MODE) == MODE_FLOAT) ((GET_MODE_CLASS (MODE) == MODE_CC) ? (REGNO == CC_REGNUM) : \
((REGNO) < 16 || REGNO == FRAME_POINTER_REGNUM \
|| REGNO == ARG_POINTER_REGNUM \
|| GET_MODE_CLASS (MODE) == MODE_FLOAT))
/* Value is 1 if it is a good idea to tie two pseudo registers /* Value is 1 if it is a good idea to tie two pseudo registers
when one has mode MODE1 and one has mode MODE2. when one has mode MODE1 and one has mode MODE2.
...@@ -249,15 +395,24 @@ extern int target_flags; ...@@ -249,15 +395,24 @@ extern int target_flags;
#define STACK_POINTER_REGNUM 13 #define STACK_POINTER_REGNUM 13
/* Base register for access to local variables of the function. */ /* Base register for access to local variables of the function. */
#define FRAME_POINTER_REGNUM 9 #define FRAME_POINTER_REGNUM 25
/* Define this to be where the real frame pointer is if it is not possible to
work out the offset between the frame pointer and the automatic variables
until after register allocation has taken place. FRAME_POINTER_REGNUM
should point to a special register that we will make sure is eliminated. */
#define HARD_FRAME_POINTER_REGNUM 11
/* Value should be nonzero if functions must have frame pointers. /* Value should be nonzero if functions must have frame pointers.
Zero means the frame pointer need not be set up (and parms may be accessed Zero means the frame pointer need not be set up (and parms may be accessed
via the stack pointer) in functions that seem suitable. */ via the stack pointer) in functions that seem suitable.
#define FRAME_POINTER_REQUIRED 0 If we have to have a frame pointer we might as well make use of it.
APCS says that the frame pointer does not need to be pushed in leaf
functions. */
#define FRAME_POINTER_REQUIRED (TARGET_APCS && !leaf_function_p ())
/* Base register for access to arguments of the function. */ /* Base register for access to arguments of the function. */
#define ARG_POINTER_REGNUM 11 #define ARG_POINTER_REGNUM 26
/* The native (Norcroft) Pascal compiler for the ARM passes the static chain /* The native (Norcroft) Pascal compiler for the ARM passes the static chain
as an invisible last argument (possible since varargs don't exist in as an invisible last argument (possible since varargs don't exist in
...@@ -268,14 +423,22 @@ extern int target_flags; ...@@ -268,14 +423,22 @@ extern int target_flags;
is passed to a function. */ is passed to a function. */
#define STRUCT_VALUE_REGNUM 0 #define STRUCT_VALUE_REGNUM 0
/* Internal, so that we don't need to refer to a raw number */
#define CC_REGNUM 24
/* The order in which register should be allocated. It is good to use ip /* The order in which register should be allocated. It is good to use ip
since no saving is required (though calls clobber it). It is quite good to since no saving is required (though calls clobber it) and it never contains
use lr since other calls may clobber it anyway. */ function parameters. It is quite good to use lr since other calls may
clobber it anyway. Allocate r0 through r3 in reverse order since r3 is
least likely to contain a function parameter; in addition results are
returned in r0.
*/
#define REG_ALLOC_ORDER \ #define REG_ALLOC_ORDER \
{ \ { \
0, 1, 2, 3, 12, 14, 4, 5, \ 3, 2, 1, 0, 12, 14, 4, 5, \
6, 7, 8, 10, 9, 11, 13, 15, \ 6, 7, 8, 10, 9, 11, 13, 15, \
16, 17, 18, 19, 20, 21, 22, 23 \ 16, 17, 18, 19, 20, 21, 22, 23, \
24, 25 \
} }
/* Register and constant classes. */ /* Register and constant classes. */
...@@ -306,18 +469,21 @@ enum reg_class ...@@ -306,18 +469,21 @@ enum reg_class
of length N_REG_CLASSES. */ of length N_REG_CLASSES. */
#define REG_CLASS_CONTENTS \ #define REG_CLASS_CONTENTS \
{ \ { \
0x000000, /* NO_REGS */ \ 0x0000000, /* NO_REGS */ \
0xFF0000, /* FPU_REGS */ \ 0x0FF0000, /* FPU_REGS */ \
0x00FFFF, /* GENERAL_REGS */ \ 0x200FFFF, /* GENERAL_REGS */ \
0xFFFFFF /* ALL_REGS */ \ 0x2FFFFFF /* ALL_REGS */ \
} }
/* The same information, inverted: /* The same information, inverted:
Return the class number of the smallest class containing Return the class number of the smallest class containing
reg number REGNO. This could be a conditional expression reg number REGNO. This could be a conditional expression
or could index an array. */ or could index an array. */
#define REGNO_REG_CLASS(REGNO) \ #define REGNO_REG_CLASS(REGNO) \
((REGNO) < 16 ? GENERAL_REGS : FPU_REGS) (((REGNO) < 16 || REGNO == FRAME_POINTER_REGNUM \
|| REGNO == ARG_POINTER_REGNUM) \
? GENERAL_REGS : (REGNO) == CC_REGNUM \
? NO_REGS : FPU_REGS)
/* The class value for index registers, and the one for base regs. */ /* The class value for index registers, and the one for base regs. */
#define INDEX_REG_CLASS GENERAL_REGS #define INDEX_REG_CLASS GENERAL_REGS
...@@ -334,14 +500,30 @@ enum reg_class ...@@ -334,14 +500,30 @@ enum reg_class
C is the letter, and VALUE is a constant value. C is the letter, and VALUE is a constant value.
Return 1 if VALUE is in the range specified by C. Return 1 if VALUE is in the range specified by C.
I: immediate arithmetic operand (i.e. 8 bits shifted as required). I: immediate arithmetic operand (i.e. 8 bits shifted as required).
J: valid indexing constants. */ J: valid indexing constants.
#define CONST_OK_FOR_LETTER_P(VALUE, C) \ K: as I but also (not (value)) ok.
((C) == 'I' ? const_ok_for_arm (VALUE) : \ L: as I but also (neg (value)) ok.*/
(C) == 'J' ? (abs (VALUE) < 4096) : 0) #define CONST_OK_FOR_LETTER_P(VALUE, C) \
((C) == 'I' ? const_ok_for_arm (VALUE) : \
/* Constant letter 'G' for the FPU immediate constants. */ (C) == 'J' ? ((VALUE) < 4096 && (VALUE) > -4096) : \
#define CONST_DOUBLE_OK_FOR_LETTER_P(X,C) \ (C) == 'K' ? (const_ok_for_arm (VALUE) || const_ok_for_arm (~(VALUE))) : \
((C) == 'G' ? const_double_rtx_ok_for_fpu (X) : 0) (C) == 'L' ? (const_ok_for_arm (VALUE) || const_ok_for_arm (-(VALUE))) : 0)
/* For the ARM, `Q' means that this is a memory operand that is just
an offset from a register.
`S' means any symbol that has the SYMBOL_REF_FLAG set or a CONSTANT_POOL
address. This means that the symbol is in the text segment and can be
accessed without using a load. */
#define EXTRA_CONSTRAINT(OP, C) \
((C) == 'Q' ? GET_CODE (OP) == MEM && GET_CODE (XEXP (OP, 0)) == REG \
: (C) == 'S' ? CONSTANT_ADDRESS_P (OP) : 0)
/* Constant letter 'G' for the FPU immediate constants.
'H' means the same constant negated. */
#define CONST_DOUBLE_OK_FOR_LETTER_P(X,C) \
((C) == 'G' ? const_double_rtx_ok_for_fpu (X) \
: (C) == 'H' ? neg_const_double_rtx_ok_for_fpu (X) : 0)
/* Given an rtx X being reloaded into a reg required to be /* Given an rtx X being reloaded into a reg required to be
in class CLASS, return the class of reg to actually use. in class CLASS, return the class of reg to actually use.
...@@ -349,6 +531,14 @@ enum reg_class ...@@ -349,6 +531,14 @@ enum reg_class
in some cases it is preferable to use a more restrictive class. */ in some cases it is preferable to use a more restrictive class. */
#define PREFERRED_RELOAD_CLASS(X, CLASS) (CLASS) #define PREFERRED_RELOAD_CLASS(X, CLASS) (CLASS)
/* 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. */
#define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS,MODE,X) \
(((MODE) == DFmode && (CLASS) == GENERAL_REGS \
&& true_regnum (X) == -1) ? GENERAL_REGS \
: NO_REGS)
/* Return the maximum number of consecutive registers /* Return the maximum number of consecutive registers
needed to represent mode MODE in a register of class CLASS. needed to represent mode MODE in a register of class CLASS.
ARM regs are UNITS_PER_WORD bits while FPU regs can hold any FP mode */ ARM regs are UNITS_PER_WORD bits while FPU regs can hold any FP mode */
...@@ -356,11 +546,11 @@ enum reg_class ...@@ -356,11 +546,11 @@ enum reg_class
((CLASS) == FPU_REGS ? 1 \ ((CLASS) == FPU_REGS ? 1 \
: ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)) : ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
/* Moves between FPU_REGS and GENERAL_REGS are two insns. */ /* Moves between FPU_REGS and GENERAL_REGS are two memory insns. */
#define REGISTER_MOVE_COST(CLASS1, CLASS2) \ #define REGISTER_MOVE_COST(CLASS1, CLASS2) \
((((CLASS1) == FPU_REGS && (CLASS2) != FPU_REGS) \ ((((CLASS1) == FPU_REGS && (CLASS2) != FPU_REGS) \
|| ((CLASS2) == FPU_REGS && (CLASS1) != FPU_REGS)) \ || ((CLASS2) == FPU_REGS && (CLASS1) != FPU_REGS)) \
? 4 : 2) ? 20 : 2)
/* Stack layout; function entry, exit and calling. */ /* Stack layout; function entry, exit and calling. */
...@@ -462,7 +652,7 @@ enum reg_class ...@@ -462,7 +652,7 @@ enum reg_class
For a library call, FNTYPE is 0. For a library call, FNTYPE is 0.
On the ARM, the offset starts at 0. */ On the ARM, the offset starts at 0. */
#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME) \ #define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME) \
((CUM) = (((FNTYPE) && aggregate_value_p (TREE_TYPE ((FNTYPE))) ? 4 : 0)) ((CUM) = (((FNTYPE) && aggregate_value_p (TREE_TYPE ((FNTYPE)))) ? 4 : 0))
/* Update the data in CUM to advance over an argument /* Update the data in CUM to advance over an argument
of mode MODE and data type TYPE. of mode MODE and data type TYPE.
...@@ -529,15 +719,91 @@ enum reg_class ...@@ -529,15 +719,91 @@ enum reg_class
/* Determine if the epilogue should be output as RTL. /* Determine if the epilogue should be output as RTL.
You should override this if you define FUNCTION_EXTRA_EPILOGUE. */ You should override this if you define FUNCTION_EXTRA_EPILOGUE. */
/* #define USE_RETURN_INSN use_return_insn () */ #define USE_RETURN_INSN use_return_insn ()
/* Definitions for register eliminations.
This is an array of structures. Each structure initializes one pair
of eliminable registers. The "from" register number is given first,
followed by "to". Eliminations of the same "from" register are listed
in order of preference.
We have two registers that can be eliminated on the ARM. First, the
arg pointer register can often be eliminated in favor of the stack
pointer register. Secondly, the pseudo frame pointer register can always
be eliminated; it is replaced with either the stack or the real frame
pointer. */
#define ELIMINABLE_REGS \
{{ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}, \
{FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}}
/* Given FROM and TO register numbers, say whether this elimination is allowed.
Frame pointer elimination is automatically handled.
All eliminations are permissible. Note that ARG_POINTER_REGNUM and
HARD_FRAME_POINTER_REGNUM are infact the same thing. If we need a frame
pointer, we must eliminate FRAME_POINTER_REGNUM into
HARD_FRAME_POINTER_REGNUM and not into STACK_POINTER_REGNUM. */
#define CAN_ELIMINATE(FROM, TO) \
(((TO) == STACK_POINTER_REGNUM && frame_pointer_needed) ? 0 : 1)
/* Define the offset between two registers, one to be eliminated, and the other
its replacement, at the start of a routine. */
#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
{ \
if ((FROM) == ARG_POINTER_REGNUM && (TO) == HARD_FRAME_POINTER_REGNUM)\
(OFFSET) = 0; \
else if ((FROM) == FRAME_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM)\
(OFFSET) = (get_frame_size () + 3 & ~3); \
else \
{ \
int regno; \
int offset = 12; \
\
for (regno = 4; regno <= 10; regno++) \
if (regs_ever_live[regno]) \
offset += 4; \
for (regno = 20; regno <=23; regno++) \
if (regs_ever_live[regno]) \
offset += 12; \
if ((FROM) == FRAME_POINTER_REGNUM) \
(OFFSET) = -offset; \
else \
{ \
if (! regs_ever_live[HARD_FRAME_POINTER_REGNUM]) \
offset -= 16; \
if (regs_ever_live[14]) \
offset += 4; \
(OFFSET) = (get_frame_size () + 3 & ~3) + offset; \
} \
} \
}
#if 0
/* Store in the variable DEPTH the initial difference between the frame /* Store in the variable DEPTH the initial difference between the frame
pointer reg contents and the stack pointer reg contents, as of the start of pointer reg contents and the stack pointer reg contents, as of the start of
the function body. This depends on the layout of the fixed parts of the the function body. This depends on the layout of the fixed parts of the
stack frame and on how registers are saved. */ stack frame and on how registers are saved. */
#define INITIAL_FRAME_POINTER_OFFSET(DEPTH) \
{ \
int regno; \
int offset = 12; \
\
for (regno = 0; regno < FRAME_POINTER_REGNUM; regno++) \
if (regs_ever_live[regno]) \
offset += 4; \
for (regno = 20; regno < 24; regno++) \
if (regs_ever_live[regno]) \
offset += 12; \
(DEPTH) = offset + (get_frame_size () + 3 & ~3); \
}
#define INITIAL_FRAME_POINTER_OFFSET(DEPTH) \ #define INITIAL_FRAME_POINTER_OFFSET(DEPTH) \
(DEPTH) = (get_frame_size () + 3) & ~3; (DEPTH) = (get_frame_size () + 3) & ~3;
#endif
/* Output assembler code for a block containing the constant parts /* Output assembler code for a block containing the constant parts
of a trampoline, leaving space for the variable parts. of a trampoline, leaving space for the variable parts.
...@@ -600,15 +866,19 @@ enum reg_class ...@@ -600,15 +866,19 @@ enum reg_class
has been allocated, which happens in local-alloc.c. has been allocated, which happens in local-alloc.c.
On the ARM, don't allow the pc to be used. */ On the ARM, don't allow the pc to be used. */
#define REGNO_OK_FOR_BASE_P(REGNO) \ #define REGNO_OK_FOR_BASE_P(REGNO) \
((REGNO) < 15 || (unsigned) reg_renumber[(REGNO)] < 15) ((REGNO) < 15 || (REGNO) == FRAME_POINTER_REGNUM \
#define REGNO_OK_FOR_INDEX_P(REGNO) \ || (REGNO) == ARG_POINTER_REGNUM \
|| (unsigned) reg_renumber[(REGNO)] < 15 \
|| (unsigned) reg_renumber[(REGNO)] == FRAME_POINTER_REGNUM \
|| (unsigned) reg_renumber[(REGNO)] == ARG_POINTER_REGNUM)
#define REGNO_OK_FOR_INDEX_P(REGNO) \
REGNO_OK_FOR_BASE_P(REGNO) REGNO_OK_FOR_BASE_P(REGNO)
/* Maximum number of registers that can appear in a valid memory address. /* Maximum number of registers that can appear in a valid memory address.
The addressing mode [ra,rb, <shift> rc] uses the greatest number of Shifts in addresses can't be by a register. */
registers. */
#define MAX_REGS_PER_ADDRESS 3 #define MAX_REGS_PER_ADDRESS 2
/* Recognize any constant value that is a valid address. */ /* Recognize any constant value that is a valid address. */
/* XXX We can address any constant, eventually... */ /* XXX We can address any constant, eventually... */
...@@ -620,8 +890,9 @@ enum reg_class ...@@ -620,8 +890,9 @@ enum reg_class
|| GET_CODE(X) == CONST ) || GET_CODE(X) == CONST )
#endif #endif
#define CONSTANT_ADDRESS_P(X) \ #define CONSTANT_ADDRESS_P(X) \
(GET_CODE (X) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (X)) (GET_CODE (X) == SYMBOL_REF \
&& (CONSTANT_POOL_ADDRESS_P (X) || SYMBOL_REF_FLAG (X)))
/* Nonzero if the constant value X is a legitimate general operand. /* Nonzero if the constant value X is a legitimate general operand.
It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE.
...@@ -632,10 +903,20 @@ enum reg_class ...@@ -632,10 +903,20 @@ enum reg_class
#define LEGITIMATE_CONSTANT_P(X) \ #define LEGITIMATE_CONSTANT_P(X) \
(GET_CODE (X) == CONST_INT \ (GET_CODE (X) == CONST_INT \
|| (GET_CODE (X) == CONST_DOUBLE \ || (GET_CODE (X) == CONST_DOUBLE \
&& const_double_rtx_ok_for_fpu (X))) && (const_double_rtx_ok_for_fpu (X) \
#if 0 || neg_const_double_rtx_ok_for_fpu (X))) \
|| GET_CODE(X) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P(X)) || CONSTANT_ADDRESS_P (X))
#endif
/* Symbols in the text segment can be accessed without indirecting via the
constant pool; it may take an extra binary operation, but this is still
faster than indirecting via memory. */
#define ENCODE_SECTION_INFO(decl) \
{ \
if (TREE_CONSTANT (decl) \
&& (!flag_writable_strings || TREE_CODE (decl) != STRING_CST)) \
SYMBOL_REF_FLAG (XEXP (TREE_CST_RTL (decl), 0)) = 1; \
}
/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx /* The macros REG_OK_FOR..._P assume that the arg is a REG rtx
and check its validity for a certain class. and check its validity for a certain class.
...@@ -644,23 +925,36 @@ enum reg_class ...@@ -644,23 +925,36 @@ enum reg_class
them unless they have been allocated suitable hard regs. them unless they have been allocated suitable hard regs.
The symbol REG_OK_STRICT causes the latter definition to be used. */ The symbol REG_OK_STRICT causes the latter definition to be used. */
#ifndef REG_OK_STRICT #ifndef REG_OK_STRICT
/* Nonzero if X is a hard reg that can be used as a base reg /* Nonzero if X is a hard reg that can be used as a base reg
or if it is a pseudo reg. */ or if it is a pseudo reg. */
#define REG_OK_FOR_BASE_P(X) \ #define REG_OK_FOR_BASE_P(X) \
(REGNO (X) < 16 || REGNO (X) >= 24) (REGNO (X) < 16 || REGNO (X) >= FIRST_PSEUDO_REGISTER \
|| REGNO (X) == FRAME_POINTER_REGNUM || REGNO (X) == ARG_POINTER_REGNUM)
/* Nonzero if X is a hard reg that can be used as an index /* Nonzero if X is a hard reg that can be used as an index
or if it is a pseudo reg. */ or if it is a pseudo reg. */
#define REG_OK_FOR_INDEX_P(X) \ #define REG_OK_FOR_INDEX_P(X) \
REG_OK_FOR_BASE_P(X) REG_OK_FOR_BASE_P(X)
#define REG_OK_FOR_PRE_POST_P(X) \
(REGNO (X) < 16 || REGNO (X) >= FIRST_PSEUDO_REGISTER) #define REG_OK_FOR_PRE_POST_P(X) \
(REGNO (X) < 16 || REGNO (X) >= FIRST_PSEUDO_REGISTER \
|| REGNO (X) == FRAME_POINTER_REGNUM || REGNO (X) == ARG_POINTER_REGNUM)
#else #else
/* Nonzero if X is a hard reg that can be used as a base reg. */ /* Nonzero if X is a hard reg that can be used as a base reg. */
#define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P (REGNO (X)) #define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P (REGNO (X))
/* Nonzero if X is a hard reg that can be used as an index. */ /* Nonzero if X is a hard reg that can be used as an index. */
#define REG_OK_FOR_INDEX_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X)) #define REG_OK_FOR_INDEX_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X))
#define REG_OK_FOR_PRE_POST_P(X) \
(REGNO (X) < 16 || (unsigned) reg_renumber[REGNO (X)] < 16) #define REG_OK_FOR_PRE_POST_P(X) \
(REGNO (X) < 16 || (unsigned) reg_renumber[REGNO (X)] < 16 \
|| REGNO (X) == FRAME_POINTER_REGNUM || REGNO (X) == ARG_POINTER_REGNUM \
|| (unsigned) reg_renumber[REGNO (X)] == FRAME_POINTER_REGNUM \
|| (unsigned) reg_renumber[REGNO (X)] == ARG_POINTER_REGNUM)
#endif #endif
/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression /* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression
...@@ -678,18 +972,24 @@ enum reg_class ...@@ -678,18 +972,24 @@ enum reg_class
/* A C statement (sans semicolon) to jump to LABEL for legitimate index RTXs /* A C statement (sans semicolon) to jump to LABEL for legitimate index RTXs
used by the macro GO_IF_LEGITIMATE_ADDRESS. Floating point indices can used by the macro GO_IF_LEGITIMATE_ADDRESS. Floating point indices can
only be small constants. */ only be small constants. */
#define GO_IF_LEGITIMATE_INDEX(MODE, BASE_REGNO, INDEX, LABEL) \ #define GO_IF_LEGITIMATE_INDEX(MODE, BASE_REGNO, INDEX, LABEL) \
do \ do \
{ \ { \
int range; \ int range; \
int code = GET_CODE (INDEX); \
\ \
if (GET_MODE_CLASS (MODE) == MODE_FLOAT) \ if (GET_MODE_CLASS (MODE) == MODE_FLOAT) \
range = 1024; \ { \
if (code == CONST_INT && INTVAL (INDEX) < 1024 \
&& INTVAL (INDEX) > -1024 \
&& (INTVAL (INDEX) & 3) == 0) \
goto LABEL; \
} \
else \ else \
{ \ { \
if (INDEX_REGISTER_RTX_P (INDEX)) \ if (INDEX_REGISTER_RTX_P (INDEX) && GET_MODE_SIZE (MODE) <= 4) \
goto LABEL; \ goto LABEL; \
if (GET_MODE_SIZE (MODE) <= 4 && GET_CODE (INDEX) == MULT) \ if (GET_MODE_SIZE (MODE) <= 4 && code == MULT) \
{ \ { \
rtx xiop0 = XEXP (INDEX, 0); \ rtx xiop0 = XEXP (INDEX, 0); \
rtx xiop1 = XEXP (INDEX, 1); \ rtx xiop1 = XEXP (INDEX, 1); \
...@@ -700,20 +1000,29 @@ do \ ...@@ -700,20 +1000,29 @@ do \
&& power_of_two_operand (xiop0, SImode)) \ && power_of_two_operand (xiop0, SImode)) \
goto LABEL; \ goto LABEL; \
} \ } \
range = 4096; \ if (GET_MODE_SIZE (MODE) <= 4 \
&& (code == LSHIFTRT || code == ASHIFTRT || code == LSHIFT \
|| code == ASHIFT || code == ROTATERT)) \
{ \
rtx op = XEXP (INDEX, 1); \
if (INDEX_REGISTER_RTX_P (XEXP (INDEX, 0)) \
&& GET_CODE (op) == CONST_INT && INTVAL (op) > 0 \
&& INTVAL (op) <= 31) \
goto LABEL; \
} \
range = (MODE) == HImode ? 4095 : 4096; \
if (code == CONST_INT && INTVAL (INDEX) < range \
&& INTVAL (INDEX) > -range) \
goto LABEL; \
} \ } \
\
if (GET_CODE (INDEX) == CONST_INT && INTVAL (INDEX) < range \
&& INTVAL (INDEX) > -range) \
goto LABEL; \
} while (0) } while (0)
/* Jump to LABEL if X is a valid address RTX. This must also take /* Jump to LABEL if X is a valid address RTX. This must also take
REG_OK_STRICT into account when deciding about valid registers, but it uses REG_OK_STRICT into account when deciding about valid registers, but it uses
the above macros so we are in luck. Allow REG, REG+REG, REG+INDEX, the above macros so we are in luck. Allow REG, REG+REG, REG+INDEX,
INDEX+REG, REG-INDEX, and non floating SYMBOL_REF to the constant pool. INDEX+REG, REG-INDEX, and non floating SYMBOL_REF to the constant pool.
Allow REG-only and AUTINC-REG if handling TImode. Other symbol refs must Allow REG-only and AUTINC-REG if handling TImode or HImode. Other symbol
be forced though a static cell to ensure addressability. */ refs must be forced though a static cell to ensure addressability. */
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, LABEL) \ #define GO_IF_LEGITIMATE_ADDRESS(MODE, X, LABEL) \
{ \ { \
if (BASE_REGISTER_RTX_P (X)) \ if (BASE_REGISTER_RTX_P (X)) \
...@@ -834,10 +1143,9 @@ do \ ...@@ -834,10 +1143,9 @@ do \
/* This is the kind of divide that is easiest to do in the general case. */ /* This is the kind of divide that is easiest to do in the general case. */
#define EASY_DIV_EXPR TRUNC_DIV_EXPR #define EASY_DIV_EXPR TRUNC_DIV_EXPR
/* 'char' is signed by default on RISCiX, unsigned on RISCOS. */ /* signed 'char' is most compatible, but RISC OS wants it unsigned.
#ifdef riscos unsigned is probably best, but may break some code. */
#define DEFAULT_SIGNED_CHAR 0 #ifndef DEFAULT_SIGNED_CHAR
#else
#define DEFAULT_SIGNED_CHAR 1 #define DEFAULT_SIGNED_CHAR 1
#endif #endif
...@@ -848,6 +1156,17 @@ do \ ...@@ -848,6 +1156,17 @@ do \
in one reasonably fast instruction. */ in one reasonably fast instruction. */
#define MOVE_MAX 4 #define MOVE_MAX 4
/* Define if operations between registers always perform the operation
on the full register even if a narrower mode is specified. */
#define WORD_REGISTER_OPERATIONS
/* Define if loading in MODE, an integral mode narrower than BITS_PER_WORD
will either zero-extend or sign-extend. The value of this macro should
be the code that says which one of the two operations is implicitly
done, NIL if none. */
#define LOAD_EXTEND_OP(MODE) \
((MODE) == QImode ? ZERO_EXTEND : NIL)
/* Define this if zero-extension is slow (more than one real instruction). /* Define this if zero-extension is slow (more than one real instruction).
On the ARM, it is more than one instruction only if not fetching from On the ARM, it is more than one instruction only if not fetching from
memory. */ memory. */
...@@ -861,10 +1180,11 @@ do \ ...@@ -861,10 +1180,11 @@ do \
that the native compiler puts too large (> 32) immediate shift counts that the native compiler puts too large (> 32) immediate shift counts
into a register and shifts by the register, letting the ARM decide what into a register and shifts by the register, letting the ARM decide what
to do instead of doing that itself. */ to do instead of doing that itself. */
#define SHIFT_COUNT_TRUNCATED 1 /* This is all wrong. Defining SHIFT_COUNT_TRUNCATED tells combine that
code like (X << (Y % 32)) for register X, Y is equivalent to (X << Y).
/* We have the vprintf function. */ On the arm, Y in a register is used modulo 256 for the shift. Only for
#define HAVE_VPRINTF 1 rotates is modulo 32 used. */
/* #define SHIFT_COUNT_TRUNCATED 1 */
/* XX This is not true, is it? */ /* XX This is not true, is it? */
/* All integers have the same format so truncation is easy. */ /* All integers have the same format so truncation is easy. */
...@@ -886,77 +1206,194 @@ do \ ...@@ -886,77 +1206,194 @@ do \
/* The relative costs of various types of constants. Note that cse.c defines /* The relative costs of various types of constants. Note that cse.c defines
REG = 1, SUBREG = 2, any node = (2 + sum of subnodes). */ REG = 1, SUBREG = 2, any node = (2 + sum of subnodes). */
#define CONST_COSTS(RTX, CODE, OUTER_CODE) \ #define CONST_COSTS(RTX, CODE, OUTER_CODE) \
case CONST_INT: \ case CONST_INT: \
if (const_ok_for_arm (INTVAL (RTX))) \ if (const_ok_for_arm (INTVAL (RTX))) \
return (2); \ return (OUTER_CODE) == SET ? 2 : -1; \
else \ else if (OUTER_CODE == AND \
return (5); \ && const_ok_for_arm (~INTVAL (RTX))) \
\ return -1; \
case CONST: \ else if ((OUTER_CODE == COMPARE \
case LABEL_REF: \ || OUTER_CODE == PLUS || OUTER_CODE == MINUS) \
case SYMBOL_REF: \ && const_ok_for_arm (-INTVAL (RTX))) \
return (6); \ return -1; \
\ else \
case CONST_DOUBLE: \ return 5; \
if (const_double_rtx_ok_for_fpu (RTX)) \ case CONST: \
return(2); \ case LABEL_REF: \
else \ case SYMBOL_REF: \
return(7); return 6; \
case CONST_DOUBLE: \
if (const_double_rtx_ok_for_fpu (RTX)) \
return (OUTER_CODE) == SET ? 2 : -1; \
else if (((OUTER_CODE) == COMPARE || (OUTER_CODE) == PLUS) \
&& neg_const_double_rtx_ok_for_fpu (RTX)) \
return -1; \
return(7);
#define RTX_COSTS(X,CODE,OUTER_CODE) \
case MEM: \
{ \
int num_words = (GET_MODE_SIZE (GET_MODE (X)) > UNITS_PER_WORD) ? 2 : 1;\
return (COSTS_N_INSNS (10*num_words)); \
} \
case MULT: \
if (GET_CODE (XEXP (X, 1)) == CONST_INT \
&& exact_log2 (INTVAL (XEXP (X, 1))) >= 0) \
return rtx_cost (XEXP (X, 0), GET_CODE (X))+1; \
return COSTS_N_INSNS (9); \
case LSHIFT: \
case ASHIFT: \
case LSHIFTRT: \
case ASHIFTRT: \
if (GET_CODE (XEXP (X, 1)) == CONST_INT) \
return rtx_cost (XEXP (X, 0), GET_CODE (X))+1; \
break; \
case MINUS: \
{ \
enum rtx_code code = GET_CODE (XEXP (X, 1)); \
if (code == MULT) \
{ \
if (GET_CODE (XEXP (XEXP (X, 1), 1)) == CONST_INT \
&& exact_log2 (INTVAL (XEXP (XEXP (X, 0), 1))) >= 0) \
return COSTS_N_INSNS (1); \
break; \
} \
else if (code == ASHIFT || code == LSHIFT || code == ASHIFTRT \
|| code == LSHIFTRT) \
return COSTS_N_INSNS (1); \
} /* fall through */ \
case PLUS: \
case IOR: \
case XOR: \
case AND: \
{ \
enum rtx_code code = GET_CODE (XEXP (X, 0)); \
if (code == MULT) \
{ \
if (GET_CODE (XEXP (XEXP (X, 0), 1)) == CONST_INT \
&& exact_log2 (INTVAL (XEXP (XEXP (X, 0), 1))) >= 0) \
return COSTS_N_INSNS (1); \
if (GET_CODE (X) == PLUS) \
return COSTS_N_INSNS (12); \
break; \
} \
else if (code == ASHIFT || code == LSHIFT || code == ASHIFTRT \
|| code == LSHIFTRT) \
return COSTS_N_INSNS (1); \
break; \
} \
case NOT: \
return rtx_cost (XEXP (X, 0), GET_CODE (XEXP (X, 0))); \
case IF_THEN_ELSE: \
{ \
if (GET_CODE (XEXP(X,1)) == PC || GET_CODE (XEXP(X,2)) == PC) \
return COSTS_N_INSNS (4); \
return COSTS_N_INSNS (1); \
} \
case SIGN_EXTEND: \
return COSTS_N_INSNS (2); \
case ZERO_EXTEND: \
if (GET_MODE (XEXP (X, 0)) == QImode) \
{ \
if (GET_CODE (XEXP (X, 0)) == MEM) \
return COSTS_N_INSNS (10); \
return COSTS_N_INSNS (1); \
} \
break; \
case COMPARE: \
if (GET_CODE (XEXP (X, 1)) == REG) \
return 4; \
case SMIN: \
case SMAX: \
case UMIN: \
case UMAX: \
return COSTS_N_INSNS (3); \
case ABS: \
if (GET_MODE (X) == SImode) \
return COSTS_N_INSNS (2); \
return COSTS_N_INSNS (1);
/* Moves to and from memory are quite expensive */
#define MEMORY_MOVE_COST(MODE) 10
/* All address computations that can be done are free */
#define ADDRESS_COST(x) 2
/* Try to generate sequences that don't involve branches, we can then use
conditional instructions */
#define BRANCH_COST 4
/* Condition code information. */ /* Condition code information. */
/* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE,
/* Store in cc_status the expressions return the mode to be used for the comparison.
that the condition codes will describe CCFPEmode should be used with floating inequalites,
after execution of an instruction whose pattern is EXP. CCFPmode should be used with floating equalities.
Do not alter them if the instruction would not alter the cc's. */ CC_NOOVmode should be used with SImode integer equalites
CCmode should be used otherwise. */
/* On the ARM nothing sets the condition code implicitly---apart from DImode
operations excluding moves---but we have to watch for registers in the #define EXTRA_CC_MODES CC_NOOVmode, CCFPmode, CCFPEmode
condition code value being clobbered. This clobbering includes (alas)
function calls. XXX They could just be considered to clobber regs 0-3 and #define EXTRA_CC_NAMES "CC_NOOV", "CCFP", "CCFPE"
10-15 with extra work. */
#define NOTICE_UPDATE_CC(EXP, INSN) \ #define SELECT_CC_MODE(OP,X,Y) \
{ \ (GET_MODE_CLASS (GET_MODE (X)) == MODE_FLOAT \
if (GET_MODE (EXP) == DImode \ ? ((OP == EQ || OP == NE) ? CCFPmode : CCFPEmode) \
&& GET_CODE (EXP) == SET \ : ((GET_MODE (X) == SImode) \
&& GET_CODE (SET_SRC (EXP)) != REG \ && ((OP) == EQ || (OP) == NE) \
&& GET_CODE (SET_SRC (EXP)) != MEM \ && (GET_CODE (X) == PLUS || GET_CODE (X) == MINUS \
&& GET_CODE (SET_SRC (EXP)) != CONST_INT) \ || GET_CODE (X) == AND || GET_CODE (X) == IOR \
CC_STATUS_INIT; \ || GET_CODE (X) == XOR || GET_CODE (X) == MULT \
else if (GET_CODE (EXP) == SET) \ || GET_CODE (X) == NOT || GET_CODE (X) == NEG \
{ \ || GET_CODE (X) == LSHIFT || GET_CODE (X) == LSHIFTRT \
rtx dest = SET_DEST (EXP); \ || GET_CODE (X) == ASHIFT || GET_CODE (X) == ASHIFTRT \
if (dest == cc0_rtx) \ || GET_CODE (X) == ROTATERT || GET_CODE (X) == ZERO_EXTRACT) \
{ \ ? CC_NOOVmode \
cc_status.flags = 0; \ : GET_MODE (X) == QImode ? CC_NOOVmode : CCmode))
cc_status.value1 = SET_DEST (EXP); \
cc_status.value2 = SET_SRC (EXP); \ #define STORE_FLAG_VALUE 1
} \
if (BASE_REGISTER_RTX_P (dest)) \ /* Define the information needed to generate branch insns. This is
{ \ stored from the compare operation. Note that we can't use "rtx" here
if (cc_status.value1 \ since it hasn't been defined! */
&& reg_overlap_mentioned_p (dest, cc_status.value1)) \
cc_status.value1 = 0; \ extern struct rtx_def *arm_compare_op0, *arm_compare_op1;
if (cc_status.value2 \ extern int arm_compare_fp;
&& reg_overlap_mentioned_p (dest, cc_status.value2)) \
cc_status.value2 = 0; \ /* Define the codes that are matched by predicates in arm.c */
} \ #define PREDICATE_CODES \
} \ {"s_register_operand", {SUBREG, REG}}, \
else if (GET_CODE (INSN) != JUMP_INSN && GET_CODE (EXP) == PARALLEL) \ {"arm_add_operand", {SUBREG, REG, CONST_INT}}, \
{ \ {"fpu_add_operand", {SUBREG, REG, CONST_DOUBLE}}, \
CC_STATUS_INIT; \ {"arm_rhs_operand", {SUBREG, REG, CONST_INT}}, \
} \ {"fpu_rhs_operand", {SUBREG, REG, CONST_DOUBLE}}, \
} {"arm_not_operand", {SUBREG, REG, CONST_INT}}, \
{"shiftable_operator", {PLUS, MINUS, AND, IOR, XOR}}, \
{"minmax_operator", {SMIN, SMAX, UMIN, UMAX}}, \
{"shift_operator", {ASHIFT, LSHIFT, ASHIFTRT, LSHIFTRT, MULT}}, \
{"di_operand", {SUBREG, REG, CONST_INT, CONST_DOUBLE, MEM}}, \
{"load_multiple_operation", {PARALLEL}}, \
{"store_multiple_operation", {PARALLEL}}, \
{"equality_operator", {EQ, NE}}, \
{"arm_rhsm_operand", {SUBREG, REG, CONST_INT, MEM}}, \
{"const_shift_operand", {CONST_INT}}, \
{"index_operand", {SUBREG, REG, CONST_INT}}, \
{"cc_register", {REG}},
/* Assembler output control */ /* Assembler output control */
#ifndef ARM_OS_NAME
#define ARM_OS_NAME "(generic)"
#endif
/* The text to go at the start of the assembler file */ /* The text to go at the start of the assembler file */
#define ASM_FILE_START(STREAM) \ #define ASM_FILE_START(STREAM) \
{ \ { \
extern char *version_string; \ extern char *version_string; \
\ \
fprintf (STREAM,"@ Generated by gcc %s for ARM/RISCiX\n", version_string); \ fprintf (STREAM,"@ Generated by gcc %s for ARM/%s\n", version_string, \
ARM_OS_NAME); \
fprintf (STREAM,"rfp\t.req\tr9\n"); \ fprintf (STREAM,"rfp\t.req\tr9\n"); \
fprintf (STREAM,"fp\t.req\tr11\n"); \ fprintf (STREAM,"fp\t.req\tr11\n"); \
fprintf (STREAM,"ip\t.req\tr12\n"); \ fprintf (STREAM,"ip\t.req\tr12\n"); \
...@@ -980,18 +1417,35 @@ do \ ...@@ -980,18 +1417,35 @@ do \
{ \ { \
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \
"r8","rfp", "sl", "fp", "ip", "sp", "lr", "pc", \ "r8","rfp", "sl", "fp", "ip", "sp", "lr", "pc", \
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7" \ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \
"cc", "sfp", "afp" \
} }
/* Arm Assembler barfs on dollars */
#define DOLLARS_IN_IDENTIFIERS 0
#define NO_DOLLAR_IN_LABEL
/* DBX register number for a given compiler register number */ /* DBX register number for a given compiler register number */
#define DBX_REGISTER_NUMBER(REGNO) (REGNO) #define DBX_REGISTER_NUMBER(REGNO) (REGNO)
/* Generate DBX debugging information. */ /* Generate DBX debugging information. riscix.h will undefine this because
the native assembler does not support stabs. */
#define DBX_DEBUGGING_INFO 1 #define DBX_DEBUGGING_INFO 1
/* Acorn dbx moans about continuation chars, so don't use any. */ /* Acorn dbx moans about continuation chars, so don't use any. */
#define DBX_CONTIN_LENGTH 0 #define DBX_CONTIN_LENGTH 0
/* Output a source filename for the debugger. RISCiX dbx insists that the
``desc'' field is set to compiler version number >= 315 (sic). */
#define DBX_OUTPUT_MAIN_SOURCE_FILENAME(STREAM,NAME) \
do { \
fprintf (STREAM, ".stabs \"%s\",%d,0,315,%s\n", (NAME), N_SO, \
&ltext_label_name[1]); \
text_section (); \
ASM_OUTPUT_INTERNAL_LABEL (STREAM, "Ltext", 0); \
} while (0)
/* Output a label definition. */ /* Output a label definition. */
#define ASM_OUTPUT_LABEL(STREAM,NAME) \ #define ASM_OUTPUT_LABEL(STREAM,NAME) \
arm_asm_output_label ((STREAM), (NAME)) arm_asm_output_label ((STREAM), (NAME))
...@@ -1021,11 +1475,14 @@ do \ ...@@ -1021,11 +1475,14 @@ do \
char *s = (char *) alloca (11 + strlen (PREFIX)); \ char *s = (char *) alloca (11 + strlen (PREFIX)); \
extern int arm_target_label, arm_ccfsm_state; \ extern int arm_target_label, arm_ccfsm_state; \
\ \
if (arm_ccfsm_state == 3 && arm_target_label == (NUM)) \ if (arm_ccfsm_state == 3 && arm_target_label == (NUM) \
arm_ccfsm_state = 0; \ && !strcmp (PREFIX, "L")) \
strcpy (s, "*"); \ { \
sprintf (&s[strlen (s)], "%s%d", (PREFIX), (NUM)); \ arm_ccfsm_state = 0; \
arm_asm_output_label (STREAM, s); \ } \
strcpy (s, "*"); \
sprintf (&s[strlen (s)], "%s%d", (PREFIX), (NUM)); \
arm_asm_output_label (STREAM, s); \
} while (0) } while (0)
/* Nothing special is done about jump tables */ /* Nothing special is done about jump tables */
...@@ -1057,13 +1514,30 @@ do \ ...@@ -1057,13 +1514,30 @@ do \
, fprintf (STREAM, "\t.word\tL%d\n", VALUE)) , fprintf (STREAM, "\t.word\tL%d\n", VALUE))
/* Output various types of constants. */ /* Output various types of constants. */
#define ASM_OUTPUT_DOUBLE(STREAM, VALUE) \ #define ASM_OUTPUT_LONG_DOUBLE(STREAM,VALUE) \
(arm_increase_location (sizeof (double)) \ do { long l[3]; \
, fprintf (STREAM, "\t.double\t%20.20f\n", VALUE)) arm_increase_location (12); \
REAL_VALUE_TO_TARGET_LONG_DOUBLE (VALUE, l); \
#define ASM_OUTPUT_FLOAT(STREAM, VALUE) \ if (sizeof (int) == sizeof (long)) \
(arm_increase_location (sizeof (float)) \ fprintf (STREAM, "\t.long 0x%x,0x%x,0x%x\n", l[2], l[1], l[0]); \
, fprintf (STREAM, "\t.float\t%20.20f\n", VALUE)) else \
fprintf (STREAM, "\t.long 0x%lx,0x%lx,0x%lx\n", l[2], l[1], l[0]); \
} while (0)
#define ASM_OUTPUT_DOUBLE(STREAM, VALUE) \
do { char dstr[30]; \
arm_increase_location (8); \
REAL_VALUE_TO_DECIMAL (VALUE, "%.20g", dstr); \
fprintf (STREAM, "\t.double %s\n", dstr); \
} while (0)
#define ASM_OUTPUT_FLOAT(STREAM, VALUE) \
do { char dstr[30]; \
arm_increase_location (4); \
REAL_VALUE_TO_DECIMAL (VALUE, "%.20g", dstr); \
fprintf (STREAM, "\t.float %s\n", dstr); \
} while (0);
#define ASM_OUTPUT_INT(STREAM, EXP) \ #define ASM_OUTPUT_INT(STREAM, EXP) \
(fprintf (STREAM, "\t.word\t"), \ (fprintf (STREAM, "\t.word\t"), \
...@@ -1154,7 +1628,9 @@ do \ ...@@ -1154,7 +1628,9 @@ do \
small-distance conditional branches and have ASM_OUTPUT_OPCODE make the small-distance conditional branches and have ASM_OUTPUT_OPCODE make the
instructions conditional. Suffixes like s (affect flags) and b (bytewise instructions conditional. Suffixes like s (affect flags) and b (bytewise
load/store) need to stay suffixes, so the possible condition code comes load/store) need to stay suffixes, so the possible condition code comes
before these suffixes. */ before these suffixes. %d<n> or %D<n> may appear in the opcode if
it can take a condition; a null rtx will cause no condition to be added,
this is what we expect to happen if arm_ccfsm_state is non-zero. */
#define ASM_OUTPUT_OPCODE(STREAM, PTR) \ #define ASM_OUTPUT_OPCODE(STREAM, PTR) \
{ \ { \
extern int arm_ccfsm_state, arm_current_cc; \ extern int arm_ccfsm_state, arm_current_cc; \
...@@ -1162,17 +1638,13 @@ do \ ...@@ -1162,17 +1638,13 @@ do \
int i; \ int i; \
\ \
fflush (STREAM); /* XXX for debugging only. */ \ fflush (STREAM); /* XXX for debugging only. */ \
if (arm_ccfsm_state == 1 || arm_ccfsm_state == 2) \ if (arm_ccfsm_state == 3 || arm_ccfsm_state == 4) \
{ \
fprintf (STREAM, "@ \t"); \
arm_ccfsm_state += 2; \
} \
else if (arm_ccfsm_state == 3 || arm_ccfsm_state == 4) \
{ \ { \
for (i = 0; *(PTR) != ' ' && *(PTR) != '\t' && i < 3; i++, (PTR)++) \ for (i = 0; *(PTR) != ' ' && *(PTR) != '\t' && *(PTR) != '%' && i < 3;\
i++, (PTR)++) \
putc (*(PTR), STREAM); \ putc (*(PTR), STREAM); \
fprintf (STREAM, "%s", arm_condition_codes[arm_current_cc]); \ fprintf (STREAM, "%s", arm_condition_codes[arm_current_cc]); \
for (; *(PTR) != ' ' && *(PTR) != '\t'; (PTR)++) \ for (; *(PTR) != ' ' && *(PTR) != '\t' && *(PTR) != '%'; (PTR)++) \
putc (*(PTR), STREAM); \ putc (*(PTR), STREAM); \
} \ } \
} }
...@@ -1186,44 +1658,56 @@ do \ ...@@ -1186,44 +1658,56 @@ do \
/* Output an operand of an instruction. If X is a REG and CODE is `M', output /* Output an operand of an instruction. If X is a REG and CODE is `M', output
a ldm/stm style multi-reg. */ a ldm/stm style multi-reg. */
#define PRINT_OPERAND(STREAM, X, CODE) \ #define PRINT_OPERAND(STREAM, X, CODE) \
{ \ { \
if ((CODE) == 'R') \ if ((CODE) == 'd') \
fputs (reg_names[REGNO (X) + 1], (STREAM)); \ { \
else if (GET_CODE (X) == REG) \ if (X) \
{ \ fputs (arm_condition_codes[get_arm_condition_code (X)], \
if ((CODE) != 'M') \ (STREAM)); \
fputs (reg_names[REGNO (X)], (STREAM)); \ } \
else \ else if ((CODE) == 'D') \
fprintf ((STREAM), "{%s-%s}", \ { \
reg_names[REGNO (X)], \ if (X) \
reg_names[REGNO (X) - 1 \ fputs (arm_condition_codes[get_arm_condition_code (X) ^ 1], \
+ ((GET_MODE_SIZE (GET_MODE (X)) \ (STREAM)); \
+ GET_MODE_SIZE (SImode) - 1) \ } \
/ GET_MODE_SIZE (SImode))]); \ else if ((CODE) == 'R') \
} \ fputs (reg_names[REGNO (X) + 1], (STREAM)); \
else if (GET_CODE (X) == MEM) \ else if (GET_CODE (X) == REG) \
{ \ { \
extern int output_memory_reference_mode; \ if ((CODE) != 'M') \
output_memory_reference_mode = GET_MODE (X); \ fputs (reg_names[REGNO (X)], (STREAM)); \
output_address (XEXP (X, 0)); \ else \
} \ fprintf ((STREAM), "{%s-%s}", \
else if (GET_CODE(X) == CONST_DOUBLE) \ reg_names[REGNO (X)], \
{ \ reg_names[REGNO (X) - 1 \
union real_extract u; \ + ((GET_MODE_SIZE (GET_MODE (X)) \
u.i[0] = CONST_DOUBLE_LOW (X); \ + GET_MODE_SIZE (SImode) - 1) \
u.i[1] = CONST_DOUBLE_HIGH (X); \ / GET_MODE_SIZE (SImode))]); \
fprintf(STREAM,"#%20.20f",u.d); \ } \
} \ else if (GET_CODE (X) == MEM) \
else if (GET_CODE (X) == NEG) \ { \
{ \ extern int output_memory_reference_mode; \
fputc ('-', (STREAM)); \ output_memory_reference_mode = GET_MODE (X); \
output_operand ((X), 0); \ output_address (XEXP (X, 0)); \
} \ } \
else \ else if (GET_CODE(X) == CONST_DOUBLE) \
{ \ { \
fputc('#', STREAM); \ union real_extract u; \
output_addr_const(STREAM, X); \ u.i[0] = CONST_DOUBLE_LOW (X); \
} \ u.i[1] = CONST_DOUBLE_HIGH (X); \
fprintf(STREAM,"#%s", fp_immediate_constant(X)); \
} \
else if (GET_CODE (X) == NEG) \
{ \
fputc ('-', (STREAM)); \
output_operand ((X), 0); \
} \
else \
{ \
fputc('#', STREAM); \
output_addr_const(STREAM, X); \
} \
} }
/* Output the address of an operand. */ /* Output the address of an operand. */
...@@ -1275,10 +1759,25 @@ do \ ...@@ -1275,10 +1759,25 @@ do \
} \ } \
else \ else \
abort(); \ abort(); \
fprintf (STREAM, "[%s, %s%s, asl#%d]", base_reg_name, \ fprintf (STREAM, "[%s, %s%s, asl #%d]", base_reg_name, \
is_minus ? "-" : "", reg_names[REGNO (index)], \ is_minus ? "-" : "", reg_names[REGNO (index)], \
shift); \ shift); \
break; \ break; \
case ASHIFTRT: \
case LSHIFTRT: \
case ASHIFT: \
case LSHIFT: \
case ROTATERT: \
{ \
char *shift_type = shift_instr (GET_CODE (index), \
&XEXP (index, 1)); \
shift = INTVAL (XEXP (index, 1)); \
index = XEXP (index, 0); \
fprintf (STREAM, "[%s, %s%s, %s #%d]", base_reg_name, \
is_minus ? "-" : "", reg_names[REGNO (index)], \
shift_type, shift); \
break; \
} \
\ \
default: \ default: \
abort(); \ abort(); \
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
Copyright (C) 1991, 1993 Free Software Foundation, Inc. Copyright (C) 1991, 1993 Free Software Foundation, Inc.
Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl) Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
and Martin Simmons (@harleqn.co.uk). and Martin Simmons (@harleqn.co.uk).
More major hacks by Richard Earnshaw (rwe11@cl.cam.ac.uk)
This file is part of GNU CC. This file is part of GNU CC.
...@@ -29,6 +30,28 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ ...@@ -29,6 +30,28 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#define HOST_BITS_PER_INT 32 #define HOST_BITS_PER_INT 32
#define HOST_BITS_PER_LONG 32 #define HOST_BITS_PER_LONG 32
/* A code distinguishing the floating point format of the host
machine. There are three defined values: IEEE_FLOAT_FORMAT,
VAX_FLOAT_FORMAT, and UNKNOWN_FLOAT_FORMAT. */
#define HOST_FLOAT_FORMAT IEEE_FLOAT_FORMAT
/* If not compiled with GNU C, use C alloca. */
#ifndef __GNUC__
#define USE_C_ALLOCA
#endif
/* Define this if the library function putenv is available on your machine */
#define HAVE_PUTENV 1
/* Define this if the library function vprintf is available on your machine */
#define HAVE_VPRINTF 1
/* Define this to be 1 if you know the host compiler supports prototypes, even
if it doesn't define __STDC__, or define it to be 0 if you do not want any
prototypes when compiling GNU CC. */
#define USE_PROTOTYPES 1
/* target machine dependencies. /* target machine dependencies.
tm.h is a symbolic link to the actual target specific file. */ tm.h is a symbolic link to the actual target specific file. */
#include "tm.h" #include "tm.h"
...@@ -38,3 +61,5 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ ...@@ -38,3 +61,5 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#define FATAL_EXIT_CODE 33 #define FATAL_EXIT_CODE 33
/* EOF xm-arm.h */ /* EOF xm-arm.h */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment