home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fresh Fish 7
/
FreshFishVol7.bin
/
bbs
/
gnu
/
gcc-2.3.3-src.lha
/
GNU
/
src
/
amiga
/
gcc-2.3.3
/
config
/
arm.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-02-06
|
36KB
|
1,344 lines
/* Output routines for GCC for ARM/RISCiX.
Copyright (C) 1991 Free Software Foundation, Inc.
Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
and Martin Simmons (@harleqn.co.uk).
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <stdio.h>
#include "assert.h"
#include "config.h"
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "insn-flags.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
/* The maximum number of insns skipped which will be conditionalised if
possible. */
#define MAX_INSNS_SKIPPED 5
/* Some function declarations. */
extern FILE *asm_out_file;
extern char *output_multi_immediate ();
extern char *arm_output_asm_insn ();
extern void arm_increase_location ();
/* 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
PRINT_OPERAND_ADDRESS. */
int output_memory_reference_mode;
/* Nonzero if the prologue must setup `fp'. */
int current_function_anonymous_args;
/* Location counter of .text segment. */
int arm_text_location = 0;
/* A hash table is used to store text segment labels and their associated
offset from the start of the text segment. */
struct label_offset
{
char *name;
int offset;
struct label_offset *cdr;
};
#define LABEL_HASH_SIZE 257
static struct label_offset *offset_table[LABEL_HASH_SIZE];
/* For an explanation of these variables, see final_prescan_insn below. */
int arm_ccfsm_state;
int arm_current_cc;
rtx arm_target_insn;
int arm_target_label;
char *arm_condition_codes[];
/* Return the number of mov instructions needed to get the constant VALUE into
a register. */
int
arm_const_nmoves (value)
register int value;
{
register int i;
if (value == 0)
return (1);
for (i = 0; value; i++, value &= ~0xff)
while ((value & 3) == 0)
value = (value >> 2) | ((value & 3) << 30);
return (i);
} /* arm_const_nmoves */
/* Return TRUE if int I is a valid immediate ARM constant. */
int
const_ok_for_arm (i)
int i;
{
unsigned int mask = ~0xFF;
do
{
if ((i & mask) == 0)
return(TRUE);
mask = (mask << 2) | (mask >> (32 - 2));
} while (mask != ~0xFF);
return (FALSE);
} /* const_ok_for_arm */
/* Return TRUE if rtx X is a valid immediate FPU constant. */
int
const_double_rtx_ok_for_fpu (x)
rtx x;
{
double d;
union real_extract u;
u.i[0] = CONST_DOUBLE_LOW(x);
u.i[1] = CONST_DOUBLE_HIGH(x);
d = u.d;
return (d == 0.0 || d == 1.0 || d == 2.0 || d == 3.0
|| d == 4.0 || d == 5.0 || d == 0.5 || d == 10.0);
} /* const_double_rtx_ok_for_fpu */
/* Predicates for `match_operand' and `match_operator'. */
/* Return TRUE for valid operands for the rhs of an ARM instruction. */
int
arm_rhs_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op))));
} /* arm_rhs_operand */
/* Return TRUE for valid operands for the rhs of an FPU instruction. */
int
fpu_rhs_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (register_operand (op, mode))
return(TRUE);
else if (GET_CODE (op) == CONST_DOUBLE)
return (const_double_rtx_ok_for_fpu (op));
else return (FALSE);
} /* fpu_rhs_operand */
/* Return nonzero if OP is a constant power of two. */
int
power_of_two_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == CONST_INT)
{
int value = INTVAL(op);
return (value != 0 && (value & (value-1)) == 0);
}
return (FALSE);
} /* power_of_two_operand */
/* Return TRUE for a valid operand of a DImode operation.
Either: REG, CONST_DOUBLE or MEM(offsettable).
Note that this disallows MEM(REG+REG). */
int
di_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (register_operand (op, mode))
return (TRUE);
switch (GET_CODE (op))
{
case CONST_DOUBLE:
case CONST_INT:
return (TRUE);
case MEM:
return (memory_address_p (DImode, XEXP (op, 0))
&& offsettable_address_p (FALSE, DImode, XEXP (op, 0)));
default:
return (FALSE);
}
} /* di_operand */
/* Return TRUE for valid index operands. */
int
index_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (register_operand(op, mode)
|| (immediate_operand (op, mode) && abs (INTVAL (op)) < 4096));
} /* index_operand */
/* Return TRUE for arithmetic operators which can be combined with a multiply
(shift). */
int
shiftable_operator (x, mode)
rtx x;
enum machine_mode mode;
{
if (GET_MODE (x) != mode)
return FALSE;
else
{
enum rtx_code code = GET_CODE (x);
return (code == PLUS || code == MINUS
|| code == IOR || code == XOR || code == AND);
}
} /* shiftable_operator */
/* Return TRUE for shift operators. */
int
shift_operator (x, mode)
rtx x;
enum machine_mode mode;
{
if (GET_MODE (x) != mode)
return FALSE;
else
{
enum rtx_code code = GET_CODE (x);
return (code == ASHIFT || code == LSHIFT
|| code == ASHIFTRT || code == LSHIFTRT);
}
} /* shift_operator */
/* Routines to output assembly language. */
/* 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.
INSTR is the possibly suffixed base register. HAT unequals zero if a hat
must follow the register list. */
void
print_multi_reg (stream, instr, mask, hat)
FILE *stream;
char *instr;
int mask, hat;
{
int i;
int not_first = FALSE;
fprintf (stream, "\t%s, {", instr);
for (i = 0; i < 16; i++)
if (mask & (1 << i))
{
if (not_first)
fprintf (stream, ", ");
fprintf (stream, "%s", reg_names[i]);
not_first = TRUE;
}
fprintf (stream, "}%s\n", hat ? "^" : "");
} /* print_multi_reg */
/* Output a 'call' insn. */
char *
output_call (operands)
rtx operands[];
{
operands[0] = XEXP (operands[0], 0);
/* Handle calls to lr using ip (which may be clobbered in subr anyway). */
if (REGNO (operands[0]) == 14)
{
operands[0] = gen_rtx (REG, SImode, 12);
arm_output_asm_insn ("mov\t%0, lr", operands);
}
arm_output_asm_insn ("mov\tlr, pc", operands);
arm_output_asm_insn ("mov\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_double_fpu_from_arm (operands)
rtx operands[];
{
int arm_reg0 = REGNO (operands[1]);
rtx ops[2];
if (arm_reg0 == 12)
abort();
ops[0] = gen_rtx (REG, SImode, arm_reg0);
ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0);
arm_output_asm_insn ("stmfd\tsp!, {%0, %1}", ops);
arm_output_asm_insn ("ldfd\t%0, [sp], #8", operands);
return ("");
} /* output_mov_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_double_arm_from_fpu (operands)
rtx operands[];
{
int arm_reg0 = REGNO (operands[0]);
rtx ops[2];
if (arm_reg0 == 12)
abort();
ops[0] = gen_rtx (REG, SImode, arm_reg0);
ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0);
arm_output_asm_insn ("stfd\t%1, [sp, #-8]!", operands);
arm_output_asm_insn ("ldmfd\tsp!